使用 JavaScript 實作動態切換 Tab 面板的按鈕控制

前言

在許多網頁應用中,常見的 UI 元件之一是 Tab 切換功能,讓使用者能在同一區塊內切換不同內容面板。這段程式碼示範如何用純 JavaScript 實作按鈕控制多個 Tab 面板的顯示與隱藏,適合有基礎 DOM 操作經驗,想了解如何手動管理元素狀態的前端工程師或自學者。

按鈕與面板元素的選取

首先,程式碼透過 document.querySelectorAll 分別取得所有具有 .action-btn.tab-panel 類別的元素。這兩組元素分別代表切換按鈕與對應的內容面板。若任一組元素不存在,則直接結束執行,避免後續錯誤。

const buttons = document.querySelectorAll('.action-btn');
const panels  = document.querySelectorAll('.tab-panel');
if (!buttons.length || !panels.length) return;

activateButton 函式設計

activateButton 是核心函式,負責切換按鈕的「活躍」狀態與對應面板的顯示。它透過按鈕的 data-target 屬性取得目標面板的類別名稱,並依序執行:

  1. 移除所有按鈕的 is-active 樣式,確保只有一個按鈕處於活躍狀態。
  2. 為當前按鈕加上 is-active 樣式。
  3. 隱藏所有面板(移除 is-active 樣式)。
  4. 顯示對應目標面板(可支援多個面板同時顯示),加上 is-active 樣式。

這種設計讓按鈕與面板的關聯透過 CSS 類別靈活控制,方便樣式調整與擴充。

function activateButton(btn) {
  const target = btn.dataset.target;
  if (!target) return;

  buttons.forEach(b => b.classList.remove('is-active'));
  btn.classList.add('is-active');

  panels.forEach(p => p.classList.remove('is-active'));

  const targetPanels = document.querySelectorAll('.' + target);
  targetPanels.forEach(p => p.classList.add('is-active'));
}

綁定事件與初始化

接著,為每個按鈕綁定點擊事件,點擊時呼叫 activateButton,並阻止預設行為(例如連結跳轉)。

最後,為了讓頁面載入時即有預設顯示的面板,程式碼自動觸發第一個按鈕的點擊事件,確保初始化狀態與使用者互動完全一致,避免手動設定狀態可能導致的不同步問題。

buttons.forEach(btn => {
  btn.addEventListener('click', e => {
    e.preventDefault();
    activateButton(btn);
  });
});

buttons[0].click();

實務應用與延伸

這種純前端的 Tab 切換實作適合用於靜態頁面或不依賴框架的專案。若需要支援動態面板內容或更複雜的狀態管理,可考慮結合前端框架或狀態管理工具。

此外,為提升無障礙性,可在按鈕加入 ARIA 屬性,並確保鍵盤操作友好。

常見問題與注意事項

  • 確保每個按鈕的 data-target 對應的面板類別名稱正確,否則無法正確顯示。
  • 若有多個面板共用同一類別,會同時顯示,這是設計上的彈性,但需注意樣式與結構。
  • 自動觸發點擊事件的做法雖方便,但若未考慮瀏覽器支援或其他腳本,可能會有兼容性問題。

完整程式碼

document.addEventListener('DOMContentLoaded', function () {
  const buttons = document.querySelectorAll('.action-btn');
  const panels  = document.querySelectorAll('.tab-panel');

  if (!buttons.length || !panels.length) return;

  function activateButton(btn) {
    const target = btn.dataset.target;
    if (!target) return;

    buttons.forEach(function (b) {
      b.classList.remove('is-active');
    });

    btn.classList.add('is-active');

    panels.forEach(function (p) {
      p.classList.remove('is-active');
    });

    const targetPanels = document.querySelectorAll('.' + target);
    targetPanels.forEach(function (p) {
      p.classList.add('is-active');
    });
  }

  buttons.forEach(function (btn) {
    btn.addEventListener('click', function (e) {
      e.preventDefault();
      activateButton(btn);
    });
  });

  buttons[0].click();
});