電商網站的商品頁、後台管理系統,甚至個人作品集,隨處可見「頁籤(Tab)切換」功能。
Q:要如何寫出擴充性高、不寫死 ID 的頁籤組件?
A:利用 HTML5 的 data-* 自訂屬性。
data-* 屬性與 dataset:在 HTML 寫入 data-tab="目標ID",JavaScript 可以直接用 element.dataset.tab 讀取資料,達成 HTML 與 JS 的無縫連結。.active 類別,最後再單獨幫被點擊的那一組加上 .active,徹底避免多重顯示的錯誤。1. HTML5 data-* 自訂屬性與 element.dataset
為什麼選用這個語法?
在 HTML 標籤上記錄自訂資料非常方便(例如 <button data-tab="tab-1">)。JavaScript 可以透過 element.dataset.tab 直接取得 "tab-1" 這個字串。這樣就能將按鈕與欲顯示的內容區塊進行一對一關聯,讓 JavaScript 邏輯極度簡潔且具備高度擴充性。
這個語法在幹嘛?
data-* 允許開發者在 HTML 元素上儲存額外的資訊,dataset 則是 DOM 物件存取這些自訂屬性的專用介面。
有沒有其他替代方案?
id 或 class 屬性字串切割
id="tab-btn-1",再用 JS 寫 split('-') 拆解數字。data-* 是 HTML5 規範中專門用來處理資料關聯的正統做法。2. 排他思想 (Exclusive Control / Reset Pattern)
為什麼選用這個邏輯?
切換頁籤的核心邏輯是:「先把所有頁籤的點亮狀態與所有內容區塊隱藏,再把當前被點擊的那一組點亮與顯示」。
這個邏輯在幹嘛?
透過 forEach 迴圈預先清空(remove('active')),確保畫面上永遠只有一個頁籤處於啟用狀態,避免出現多個頁籤同時顯示的 Bug。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>第十二課 - Tab 頁籤內容切換器</title>
<style>
body { font-family: sans-serif; padding: 40px; background-color: #f8f9fa; }
.tab-container { max-width: 500px; margin: 0 auto; background: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); overflow: hidden; }
/* 頁籤按鈕選單區域 */
.tab-menu { display: flex; background-color: #e9ecef; border-bottom: 2px solid #dee2e6; }
/* 單個按鈕樣式 */
.tab-btn {
flex: 1;
padding: 15px;
border: none;
background: none;
font-size: 16px;
font-weight: bold;
color: #6c757d;
cursor: pointer;
transition: all 0.2s ease;
}
.tab-btn:hover { color: #007bff; }
/* 按鈕啟用 (Active) 樣式 */
.tab-btn.active {
color: #007bff;
background-color: white;
border-bottom: 3px solid #007bff;
}
/* 頁籤內容容器 */
.tab-content-wrapper { padding: 25px; }
/* 單個內容區塊 (預設隱藏) */
.tab-panel { display: none; }
/* 內容區塊啟用 (Active) 樣式 */
.tab-panel.active { display: block; animation: fadeIn 0.3s ease; }
/* 淡入動畫 */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(5px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="tab-container">
<!-- 頁籤按鈕選單 (使用 data-tab 綁定目標 Panel 的 ID) -->
<div class="tab-menu">
<button class="tab-btn active" data-tab="tab-detail">商品詳情</button>
<button class="tab-btn" data-tab="tab-specs">規格說明</button>
<button class="tab-btn" data-tab="tab-reviews">顧客評價</button>
</div>
<!-- 頁籤對應內容區塊 -->
<div class="tab-content-wrapper">
<div id="tab-detail" class="tab-panel active">
<h3>商品詳情</h3>
<p>這款高質感藍牙耳機採用頂級降噪技術,帶來沉浸式的聆聽體驗。續航力高達 30 小時!</p>
</div>
<div id="tab-specs" class="tab-panel">
<h3>規格說明</h3>
<ul>
<li>藍牙版本:5.3</li>
<li>電池容量:500 mAh</li>
<li>防水等級:IPX5 防潑水</li>
<li>重量:210 克</li>
</ul>
</div>
<div id="tab-reviews" class="tab-panel">
<h3>顧客評價 (4.9/5)</h3>
<p><strong>小明:</strong> 音質非常好,降噪效果驚人!CP 值極高。</p>
<p><strong>小華:</strong> 配戴非常舒服,戴一整天耳朵也不會痛。</p>
</div>
</div>
</div>
<script>
// ----------------------------------------------------
// JavaScript 邏輯 (data-tab 與動態切換)
// ----------------------------------------------------
// 第一步:取得所有的按鈕與所有的內容面板 (Panel)
const tabBtns = document.querySelectorAll(".tab-btn");
const tabPanels = document.querySelectorAll(".tab-panel");
// 第二步:使用 forEach 為每一個按鈕綁定點擊事件
tabBtns.forEach(function(btn) {
btn.addEventListener("click", function() {
// 1. 取得被點擊按鈕上的 data-tab 屬性值 (對應目標 Panel 的 ID)
const targetTabId = btn.dataset.tab;
// 2. 排他思想:移除所有按鈕的 active 類別,並隱藏所有的 Panel
tabBtns.forEach(b => b.classList.remove("active"));
tabPanels.forEach(p => p.classList.remove("active"));
// 3. 點亮當前被點擊的按鈕
btn.classList.add("active");
// 4. 找到對應 ID 的 Panel 並加上 active 顯示出來
const targetPanel = document.getElementById(targetTabId);
if (targetPanel) {
targetPanel.classList.add("active");
}
});
});
</script>
</body>
</html>
今天我們學會了如何使用 dataset 來處理 DOM 之間的資料關聯。這種「標籤記資料、JS 讀資料、CSS 控視覺」的三位一體思維,是現代前端開發中最推薦的元件建構方式。
我們常常在藝廊網站、電商商品頁看到「點擊小圖,上方大圖與文字說明會跟著改變」的圖片藝廊
—>【第十三課】媒體呈現:動態圖片展示藝廊(Image Gallery & Event Delegation),我們將學習如何運用「事件委派」打造高效能的動態圖片切換組件。