不論是登入畫面、條款確認還是圖片放大看,Modal 燈箱元件在網站中隨處可見。看似簡單的彈窗,如果沒有處理好「背景鎖定」與「遮罩點擊關閉」,就會造成體驗感下降,今天我們用純原生 DOM 操作寫出專業等級的 Modal 燈箱。
1. document.body.style.overflow = 'hidden' 阻擋滾動
為什麼選用這個屬性?
當 Modal 彈窗打開時,如果使用者滑動滾輪,後方的網頁內容依然會跟著上下滾動,這在使用者體驗(UX)上是非常差的。直接將 body 的 overflow 屬性設定為 "hidden",就能瞬間鎖定網頁的滾動條。
這個屬性在幹嘛?
overflow: hidden 會告訴瀏覽器:「只要超出視窗範圍的內容,通通隱藏且不允許滾動」。當關閉彈窗時,再將其重置為 ""(恢復預設),頁面就能重新滑動。
有沒有其他替代方案?
event.preventDefault()
touchmove 或 wheel 事件並取消預設行為。body 的 CSS overflow 屬性是最乾淨高效的做法。2. 遮罩層點擊關閉(event.target 與事件冒泡判斷)
為什麼選用這個判斷?
好的 Modal 設計通常允許使用者點擊「彈窗外的半透明黑色遮罩」來快速關閉彈窗。我們需要判斷使用者點擊的到底是不是「遮罩本體」,而不是點到內部的「對話框文字區域」。
這個判斷在幹嘛?
if (event.target === modalOverlay):檢查觸發點擊事件的 DOM 元素(event.target)是否恰好就是遮罩層本身。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>第十課 - 輕量化 Modal 燈箱效果</title>
<style>
body { font-family: sans-serif; padding: 40px; background-color: #f4f6f8; line-height: 1.6; }
/* 觸發按鈕樣式 */
.open-btn {
padding: 12px 24px;
font-size: 16px;
background-color: #2ecc71;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
}
/* 全螢幕半透明黑色遮罩 (預設隱藏) */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5); /* 50% 半透明黑 */
display: flex;
justify-content: center;
align-items: center;
/* 視覺隱藏與動畫準備 */
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
z-index: 1000;
}
/* 彈窗主體對話框 */
.modal-box {
background: white;
width: 400px;
padding: 30px;
border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
transform: translateY(-20px); /* 預設往上偏一點,準備做下滑動畫 */
transition: transform 0.3s ease;
position: relative;
}
/* 當 Modal 處於顯示狀態時的 CSS 類別 */
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal-overlay.active .modal-box {
transform: translateY(0); /* 對話框歸位 */
}
/* 關閉按鈕 (x) */
.close-btn {
position: absolute;
top: 15px;
right: 20px;
font-size: 22px;
cursor: pointer;
color: #999;
}
.close-btn:hover { color: #333; }
</style>
</head>
<body>
<h2>第十課:Modal 彈窗燈箱展示</h2>
<p>點擊下方按鈕打開彈窗,注意觀察:彈窗打開時背景頁面無法滾動。</p>
<button id="openModalBtn" class="open-btn">開啟會員條款</button>
<!-- 故意拉長頁面高度以測試滾動阻擋 -->
<div style="height: 1500px; margin-top: 20px; background: #eef2f5; padding: 20px;">
(頁面延伸區塊 - 測試背景鎖定)
</div>
<!-- Modal 燈箱 HTML 結構 -->
<div id="modalOverlay" class="modal-overlay">
<div class="modal-box">
<span id="closeModalBtn" class="close-btn">×</span>
<h3>會員服務條款</h3>
<p>歡迎使用我們的服務!點擊遮罩區域或右上角關閉按鈕即可退出此對話框。</p>
<p>當這個視窗開啟時,背景的網頁捲軸會被暫時鎖定,避免使用者混淆。</p>
</div>
</div>
<script>
// ----------------------------------------------------
// JavaScript 邏輯 (Modal 開關與 body 鎖定)
// ----------------------------------------------------
// 第一步:取得所需的 DOM 元素
const openModalBtn = document.getElementById("openModalBtn");
const closeModalBtn = document.getElementById("closeModalBtn");
const modalOverlay = document.getElementById("modalOverlay");
// 開啟彈窗的函式
function openModal() {
// 1. 加上 active 類別,顯示遮罩與播放淡入動畫
modalOverlay.classList.add("active");
// 2. 鎖定 body 滾動條,防止背景頁面繼續滑動
document.body.style.overflow = "hidden";
}
// 關閉彈窗的函式
function closeModal() {
// 1. 移除 active 類別,隱藏遮罩
modalOverlay.classList.remove("active");
// 2. 恢復 body 滾動條
document.body.style.overflow = "";
}
// 第二步:為按鈕綁定開啟與關閉事件
openModalBtn.addEventListener("click", openModal);
closeModalBtn.addEventListener("click", closeModal);
// 第三步:點擊黑色遮罩背景也能關閉彈窗 (提升 UX)
modalOverlay.addEventListener("click", function(event) {
// 判斷點擊的目標是否為遮罩層本身 (而不是裡面的 .modal-box)
if (event.target === modalOverlay) {
closeModal();
}
});
</script>
</body>
</html>
今天我們搞定了現代網頁極具代表性的 UI 組件——Modal 燈箱。結合了動態 Class 切換、視窗防滾動鎖定與事件點擊目標判斷,這套邏輯可以直接套用到未來的購物車確認框、產品快速預覽等功能中。
常常在社交平台或新聞網站看到「往下滑動時自動載入更多新卡片」的動態生成效果嗎,下集,
【第十一課】動態生成:無窮滾動/卡片動態渲染(createElement 與 appendChild),我們將學習如何用 JS 動態產出大量 DOM 卡片並掛載到頁面上。