Trang chá»§
Modal UI
Save
Settings
Sign Up
Log In
Save
Settings
HTML
Copy
<div id="modal-center"> <button class="btn">Open modal center fade zoom</button> </div>
CSS
Copy
* { margin: 0; padding: 0; box-sizing: border-box; } /* ===== Modal Base ===== */ .modal { position: fixed; inset: 0; background-color: rgba(0, 0, 0, 0.2); text-align: center; user-select: none; } .modal::before { content: ""; display: inline-block; vertical-align: middle; height: 100%; width: 0; margin-left: -0.25em; } .dialog { position: relative; text-align: left; overflow: hidden; width: 600px; max-width: 600px; background: #fff; padding: 20px; border-radius: 10px; display: inline-flex; flex-direction: column; vertical-align: middle; max-height: calc(100vh - 200px); user-select: text; } .close { position: absolute; right: 20px; cursor: pointer; } .close:hover svg path { fill: #df0606; } .close svg { width: 16px; height: 16px; } .close svg path { transition: all 0.3s linear; fill: #a3a3a3; } .title { font-size: 16px; font-weight: bold; color: #3b3b3b; margin-bottom: 16px; width: calc(100% - 24px); } .content { height: 100%; overflow-y: auto; } /* ===== Modal Animation ===== */ .dialog.center { transition: transform 0.4s ease, opacity 0.4s linear; transform: scale(0); opacity: 0; } .dialog.center.open { transform: scale(1); opacity: 1; } /* ===== Button ===== */ .btn { margin-top: 20px; padding-inline: 20px; height: 40px; line-height: 40px; color: #fff; background: #307dca; border-radius: 10px; border: none; transition: all 0.3s linear; } .btn:hover { opacity: 0.6; } /* ===== Responsive ===== */ @media (max-width: 640px) { .dialog { width: 100%; max-width: calc(100vw - 32px); } }
JS
Copy
// ==== Common ==== function isFunction(func) { return typeof func === "function"; } // ==== HTML Template ==== const htmlModal = ({ title, contentHtml, className }) => ` <div class="dialog${className ? ` ${className}` : ""}"> <div class="close"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"> <path d="M393.4 41.4c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L269.3 256 438.6 425.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L224 301.3 54.6 470.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L178.7 256 9.4 86.6C-3.1 74.1-3.1 53.9 9.4 41.4s32.8-12.5 45.3 0L224 210.7 393.4 41.4z" /> </svg> </div> ${title ? `<h2 class="title">${title}</h2>` : ""} <div class="content">${contentHtml ?? ""}</div> </div> `; // ==== Base Modal ==== class Modal { #btn; #dialog; #mask = document.createElement("div"); #title; #html; #class; onClose; constructor({ title, contentHtml, btnEl, className }) { this.#title = title; this.#html = contentHtml; this.#btn = btnEl; this.#class = className; } open() { const mask = this.#mask; mask.className = "modal"; mask.innerHTML = htmlModal({ title: this.#title, contentHtml: this.#html, className: this.#class, }); const dialog = mask.querySelector(".dialog"); this.#dialog = dialog; dialog.addEventListener("click", (e) => e.stopPropagation()); const closeBtn = mask.querySelector(".close"); [mask, closeBtn].forEach((el) => { el.addEventListener("click", () => { this.destroy(); if (isFunction(this.onClose)) this.onClose(); }); }); document.body.append(mask); return this; } destroy() { document.body.removeChild(this.#mask); return this; } get dialog() { return this.#dialog; } get mask() { return this.#mask; } get btn() { return this.#btn; } } // ==== Modal Fade Zoom ==== class ModalFadeZoom extends Modal { open() { super.open(); const dialog = this.dialog; const btn = this.btn; setTimeout(() => { const rectBtn = btn.getBoundingClientRect(); const rectDialog = dialog.getBoundingClientRect(); const top = rectBtn.top + btn.clientHeight / 2 - (rectDialog.top - dialog.clientHeight / 2); const left = rectBtn.left + btn.clientWidth / 2 - (window.innerWidth - dialog.clientWidth) / 2; dialog.style.transformOrigin = `${left}px ${top}px`; }, 10); setTimeout(() => dialog.classList.add("open"), 20); return this; } destroy() { const dialog = this.dialog; const mask = this.mask; dialog.classList.remove("open"); setTimeout(() => document.body.removeChild(mask), 400); return this; } } // ==== Run ==== const btn = document.querySelector("#modal-center .btn"); btn?.addEventListener("click", () => { const modal = new ModalFadeZoom({ title: "Modal fade zoom", btnEl: btn, className: "center", contentHtml: ` <p> This modal demonstrates a center fade zoom animation. It opens smoothly and focuses on the button position. </p> `, }); modal.open(); });