iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
JavaScript

解決學了JavaScript不知道能做什麼:用DOM操作打造實用網頁效果 系列 第 5

第五課:【元件設計】手風琴式 FAQ 折疊選單

  • 分享至 

  • xImage
  •  

前言

網站的「常見問題(FAQ)」頁面,若把所有答案一次攤開會讓頁面顯得很混亂。「手風琴折疊元件」讓使用者點擊標題時才展開對應答案,是維持介面整潔的好幫手。

核心觀念與語法拆解

  1. querySelectorAll + forEach:一次選取多個相同的 DOM 元素,並用迴圈批次為它們綁定事件監聽器。
  2. nextElementSibling:精準取得點擊目標「緊鄰的下一個 DOM 節點」,就不用去為每個答案區塊設定獨立 ID 。

觀念與函式深度解析

1. document.querySelectorAll('.accordion-header')forEach()

  • 為什麼選用這個函式

    手風琴選單通常有多個題目(例如 3 個 FAQ 標題)。querySelectorAll 可以一次選取畫面上所有帶有指定 Class 的元素,並回傳一個 NodeList 清單;再搭配 forEach() 迴圈,就能批次為每一個標題綁定點擊事件。

  • 這個函式在幹嘛

    querySelectorAll 在整頁找尋匹配 CSS 選擇器的元素集;forEach 則是逐一走訪集合中的每個項目。

  • 有沒有其他替代方案

    • 方案 A:事件委派
      • 簡介:將監聽器綁定在外層父元素 <ul> 上,透過 event.target 判斷點擊目標。
      • 比較:當項目數量極多時(如成百上千條),事件委派效能更好;但在基礎元件(3~5 個標題)時,用 querySelectorAll + forEach 邏輯最直覺好懂。

2. element.nextElementSibling 屬性

  • 為什麼選用這個屬性

    點擊標題時,我們需要去控制「緊接在標題後面的內容區塊」。nextElementSibling 可以直接準確抓到下一個同階層的 HTML 元素,完全不用為每個內容區塊獨立命名 ID!

  • 這個屬性在幹嘛

    在 DOM 樹狀結構中尋找同一層級的「下一個相鄰元素」。

  • 有沒有其他替代方案

    • 方案 A:querySelector('.accordion-content')
      • 簡介:在全域尋找內容。
      • 比較:這會固定抓到全網頁「第一個」內容區塊,無法對應到使用者當前點擊的那一個特定標題。nextElementSibling 才是針對相對位置操作。

完整實作程式碼

<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <title>第五課 - 手風琴式 FAQ 折疊選單</title>
  <style>
    body { font-family: sans-serif; padding: 40px; background-color: #f4f6f8; }
    .accordion { width: 400px; margin: 0 auto; }

    /* FAQ 標題樣式 */
    .accordion-header {
      background-color: #007bff;
      color: white;
      padding: 15px;
      cursor: pointer;
      border-radius: 4px;
      margin-top: 10px;
      user-select: none;
    }

    /* FAQ 內容區塊樣式 (預設隱藏) */
    .accordion-content {
      background-color: white;
      padding: 0 15px;
      max-height: 0;           /* 初始高度為 0 */
      overflow: hidden;        /* 超出高度時隱藏 */
      transition: max-height 0.3s ease, padding 0.3s ease; /* 平滑展開動畫 */
      border-radius: 0 0 4px 4px;
    }

    /* 展開狀態下的 CSS 類別 */
    .accordion-content.active {
      max-height: 200px;       /* 設定適當的最大高度來讓它滑出 */
      padding: 15px;
    }</style>
</head>
<body>

  <div class="accordion">
    <h2>常見問題 FAQ</h2>

    <div class="accordion-header">Q1: 這個課程適合完全零基礎嗎?</div>
    <div class="accordion-content">是的!本系列專為學過語法卻不會實作的初學者設計,從基礎 DOM 開始帶你動手做。</div>

    <div class="accordion-header">Q2: 寫網頁需要安裝很複雜的軟體嗎?</div>
    <div class="accordion-content">完全不需要,只需要安裝 VS Code 編輯器與 Google Chrome 瀏覽器即可。</div>

    <div class="accordion-header">Q3: 完成 30 天後我能做出什麼?</div>
    <div class="accordion-content">你將掌握常用網頁 UI 元件、API 資料串接,並建立屬於自己的 GitHub 作品集網頁!</div>
  </div>

  <script>
    // ----------------------------------------------------
    // JavaScript 邏輯 (DOM 操作)
    // ----------------------------------------------------

    // 第一步:取得畫面上所有的 .accordion-header 標題按鈕
    const headers = document.querySelectorAll(".accordion-header");

    // 第二步:使用 forEach 迴圈,為每一個標題綁定點擊事件
    headers.forEach(function(header){

      header.addEventListener("click", function(){

        // 第三步:找到目前點擊標題的下一個同階層元素 (即對應的 .accordion-content)
        const content = header.nextElementSibling;

        // 第四步:使用 classList.toggle 切換 'active' 類別
        // 有 active 類別時展開,沒有時收合
        content.classList.toggle("active");

      });

    });</script>
</body>
</html

總結與實用情境

透過 DOM 的相對位置比對(nextElementSibling)與 CSS max-height 動畫配合,我們就能打造既美觀又具備平滑展開效果的組件。這種相對節點存取的技巧,未來在側邊多層級選單、購物車數量加減等情境中都會頻繁使用。

下集預告

使用者在輸入社群貼文或評論時,網站常會顯示「剩餘字數提示」或「超過字數變紅」。這即時視覺反饋是怎麼做出來的

【第六課】視覺反饋:字數計數器與上限提示(keyup 與樣式控制)。


上一篇
內容篩選:文字框即時搜尋與清單過濾(input 事件與 includes)
系列文
解決學了JavaScript不知道能做什麼:用DOM操作打造實用網頁效果 5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言