<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DOM操作 &#8211; 小豬日常</title>
	<atom:link href="https://piglife.tw/tag/dom/feed/" rel="self" type="application/rss+xml" />
	<link>https://piglife.tw</link>
	<description>Hello World，一個紀錄生活與學習的地方</description>
	<lastBuildDate>Fri, 12 Dec 2025 14:44:42 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://piglife.tw/wp-content/uploads/2017/10/cropped-logo-1-32x32.png</url>
	<title>DOM操作 &#8211; 小豬日常</title>
	<link>https://piglife.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>使用 JavaScript 實作動態切換 Tab 面板的按鈕控制</title>
		<link>https://piglife.tw/technical-notes/javascript-tab-panel-switch/</link>
					<comments>https://piglife.tw/technical-notes/javascript-tab-panel-switch/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Wed, 10 Dec 2025 10:22:38 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[DOM操作]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Tab切換]]></category>
		<guid isPermaLink="false">https://piglife.tw/technical-notes/javascript-tab-panel-switch/</guid>

					<description><![CDATA[介紹如何用純 JavaScript 實作按鈕控制的 Tab 面板切換功能，包含狀態管理與事件綁定，適...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在許多網頁應用中，常見的 UI 元件之一是 Tab 切換功能，讓使用者能在同一區塊內切換不同內容面板。這段程式碼示範如何用純 JavaScript 實作按鈕控制多個 Tab 面板的顯示與隱藏，適合有基礎 DOM 操作經驗，想了解如何手動管理元素狀態的前端工程師或自學者。</p>
<h2 class="wp-block-heading">按鈕與面板元素的選取</h2>
<p>首先，程式碼透過 <code>document.querySelectorAll</code> 分別取得所有具有 <code>.action-btn</code> 與 <code>.tab-panel</code> 類別的元素。這兩組元素分別代表切換按鈕與對應的內容面板。若任一組元素不存在，則直接結束執行，避免後續錯誤。</p>
<pre><code class="lang-javascript language-javascript javascript">const buttons = document.querySelectorAll(&#039;.action-btn&#039;);
const panels  = document.querySelectorAll(&#039;.tab-panel&#039;);
if (!buttons.length || !panels.length) return;</code></pre>
<h2 class="wp-block-heading">activateButton 函式設計</h2>
<p><code>activateButton</code> 是核心函式，負責切換按鈕的「活躍」狀態與對應面板的顯示。它透過按鈕的 <code>data-target</code> 屬性取得目標面板的類別名稱，並依序執行：</p>
<ol>
<li>移除所有按鈕的 <code>is-active</code> 樣式，確保只有一個按鈕處於活躍狀態。</li>
<li>為當前按鈕加上 <code>is-active</code> 樣式。</li>
<li>隱藏所有面板（移除 <code>is-active</code> 樣式）。</li>
<li>顯示對應目標面板（可支援多個面板同時顯示），加上 <code>is-active</code> 樣式。</li>
</ol>
<p>這種設計讓按鈕與面板的關聯透過 CSS 類別靈活控制，方便樣式調整與擴充。</p>
<pre><code class="lang-javascript language-javascript javascript">function activateButton(btn) {
  const target = btn.dataset.target;
  if (!target) return;

  buttons.forEach(b =&gt; b.classList.remove(&#039;is-active&#039;));
  btn.classList.add(&#039;is-active&#039;);

  panels.forEach(p =&gt; p.classList.remove(&#039;is-active&#039;));

  const targetPanels = document.querySelectorAll(&#039;.&#039; + target);
  targetPanels.forEach(p =&gt; p.classList.add(&#039;is-active&#039;));
}</code></pre>
<h2 class="wp-block-heading">綁定事件與初始化</h2>
<p>接著，為每個按鈕綁定點擊事件，點擊時呼叫 <code>activateButton</code>，並阻止預設行為（例如連結跳轉）。</p>
<p>最後，為了讓頁面載入時即有預設顯示的面板，程式碼自動觸發第一個按鈕的點擊事件，確保初始化狀態與使用者互動完全一致，避免手動設定狀態可能導致的不同步問題。</p>
<pre><code class="lang-javascript language-javascript javascript">buttons.forEach(btn =&gt; {
  btn.addEventListener(&#039;click&#039;, e =&gt; {
    e.preventDefault();
    activateButton(btn);
  });
});

buttons[0].click();</code></pre>
<h2 class="wp-block-heading">實務應用與延伸</h2>
<p>這種純前端的 Tab 切換實作適合用於靜態頁面或不依賴框架的專案。若需要支援動態面板內容或更複雜的狀態管理，可考慮結合前端框架或狀態管理工具。</p>
<p>此外，為提升無障礙性，可在按鈕加入 ARIA 屬性，並確保鍵盤操作友好。</p>
<h2 class="wp-block-heading">常見問題與注意事項</h2>
<ul>
<li>確保每個按鈕的 <code>data-target</code> 對應的面板類別名稱正確，否則無法正確顯示。</li>
<li>若有多個面板共用同一類別，會同時顯示，這是設計上的彈性，但需注意樣式與結構。</li>
<li>自動觸發點擊事件的做法雖方便，但若未考慮瀏覽器支援或其他腳本，可能會有兼容性問題。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-javascript language-javascript javascript">document.addEventListener(&#039;DOMContentLoaded&#039;, function () {
  const buttons = document.querySelectorAll(&#039;.action-btn&#039;);
  const panels  = document.querySelectorAll(&#039;.tab-panel&#039;);

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

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

    buttons.forEach(function (b) {
      b.classList.remove(&#039;is-active&#039;);
    });

    btn.classList.add(&#039;is-active&#039;);

    panels.forEach(function (p) {
      p.classList.remove(&#039;is-active&#039;);
    });

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

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

  buttons[0].click();
});</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/javascript-tab-panel-switch/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>使用 JavaScript 控制表格列依年份篩選顯示</title>
		<link>https://piglife.tw/technical-notes/javascript-table-row-filter/</link>
					<comments>https://piglife.tw/technical-notes/javascript-table-row-filter/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Wed, 10 Dec 2025 10:22:08 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[DOM操作]]></category>
		<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://piglife.tw/technical-notes/javascript-table-row-filter/</guid>

					<description><![CDATA[這篇文章說明如何使用純 JavaScript 根據下拉選單選擇，動態篩選並顯示表格中帶有年份標記的列...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在許多資料表格中，我們常需要根據某個條件（例如年份）來篩選顯示特定的列。這段程式碼示範如何利用純 JavaScript 操作 DOM，根據下拉選單的選擇動態控制表格中帶有特定屬性（data-block）的列顯示與隱藏。適合需要簡潔且無依賴外部函式庫的前端工程師或自學者參考。</p>
<h2 class="wp-block-heading">取得目標元素與資料列</h2>
<p>首先，我們透過 <code>document.getElementById</code> 取得下拉選單元素，並使用 <code>document.querySelectorAll</code> 選取所有帶有 <code>data-block</code> 屬性的表格列（tr）。這些列代表不同年份的資料區塊。</p>
<pre><code class="lang-javascript language-javascript javascript">const select = document.getElementById(&#039;select-download&#039;);
const blocks = Array.from(document.querySelectorAll(&#039;table tr[data-block]&#039;));</code></pre>
<h2 class="wp-block-heading">儲存原始顯示狀態</h2>
<p>為了避免直接覆寫 <code>display</code> 屬性導致無法還原，我們先讀取每個列的原始顯示狀態，並存入自訂的 <code>data-orig-display</code> 屬性中。這樣在切換篩選條件時，可以回復到原本的顯示方式。</p>
<pre><code class="lang-javascript language-javascript javascript">blocks.forEach(el =&gt; {
  if (!el.dataset.origDisplay) {
    const d = window.getComputedStyle(el).display;
    el.dataset.origDisplay = (d &amp;&amp; d !== &#039;none&#039;) ? d : &#039;block&#039;;
  }
});</code></pre>
<h2 class="wp-block-heading">篩選函式設計</h2>
<p><code>applyFilter</code> 函式會根據下拉選單的值，決定每個資料列是否顯示。若選擇 &#8220;all&#8221;，則全部顯示；否則只顯示 <code>data-block</code> 屬性值符合的列，其他則隱藏。</p>
<pre><code class="lang-javascript language-javascript javascript">function applyFilter() {
  const val = select.value; // 例如 &#039;all&#039; 或 &#039;2025&#039;
  blocks.forEach(el =&gt; {
    const year = el.getAttribute(&#039;data-block&#039;);
    if (val === &#039;all&#039; || year === val) {
      el.style.display = el.dataset.origDisplay;
    } else {
      el.style.display = &#039;none&#039;;
    }
  });
}</code></pre>
<h2 class="wp-block-heading">綁定事件與初始化</h2>
<p>程式碼在 DOMContentLoaded 時執行，確保元素已存在。初次執行一次篩選，並監聽下拉選單的 <code>change</code> 事件，動態更新顯示狀態。</p>
<pre><code class="lang-javascript language-javascript javascript">applyFilter();
select.addEventListener(&#039;change&#039;, applyFilter);</code></pre>
<h2 class="wp-block-heading">實務應用與延伸</h2>
<p>這種用法適合靜態或小型資料表格的前端篩選，無需後端或複雜框架支援。若資料量大，可考慮分頁或虛擬滾動優化效能。未來也能擴充多條件篩選，或搭配 CSS 動畫增強使用者體驗。</p>
<h2 class="wp-block-heading">常見問題與注意事項</h2>
<ul>
<li>確保 <code>data-block</code> 屬性值與下拉選單選項一致。</li>
<li>若原始顯示狀態非 <code>block</code>，此程式碼會自動保存並還原，避免顯示異常。</li>
<li>若表格結構改變，需確認選取器仍正確。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-javascript language-javascript javascript">document.addEventListener(&#039;DOMContentLoaded&#039;, function () {
  const select = document.getElementById(&#039;select-download&#039;);
  if (!select) return;

  // 只控制 Greenshift 的區塊，且有 data-block（年份）
  const blocks = Array.from(
    document.querySelectorAll(&#039;table tr[data-block]&#039;)
  );

  // 記住原本的 display（以防原本不是 block）
  blocks.forEach(el =&gt; {
    if (!el.dataset.origDisplay) {
      const d = window.getComputedStyle(el).display;
      el.dataset.origDisplay = (d &amp;&amp; d !== &#039;none&#039;) ? d : &#039;block&#039;;
    }
  });

  function applyFilter() {
    const val = select.value; // &#039;all&#039; 或 &#039;2025&#039;...
    blocks.forEach(el =&gt; {
      const year = el.getAttribute(&#039;data-block&#039;);
      if (val === &#039;all&#039; || year === val) {
        el.style.display = el.dataset.origDisplay;
      } else {
        el.style.display = &#039;none&#039;;
      }
    });
  }

  // 初次執行一次（依目前選中的值）
  applyFilter();

  // 監聽選單變更
  select.addEventListener(&#039;change&#039;, applyFilter);
});</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/javascript-table-row-filter/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>使用 JavaScript 自動隱藏無目錄的 toc-block 區塊</title>
		<link>https://piglife.tw/technical-notes/javascript-hide-empty-toc-block/</link>
					<comments>https://piglife.tw/technical-notes/javascript-hide-empty-toc-block/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Wed, 10 Dec 2025 10:21:42 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[DOMContentLoaded]]></category>
		<category><![CDATA[DOM操作]]></category>
		<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://piglife.tw/technical-notes/javascript-hide-empty-toc-block/</guid>

					<description><![CDATA[本文介紹如何使用 JavaScript 在頁面載入後自動隱藏沒有目錄內容的 toc-block，提升...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在網頁中，我們常會使用目錄區塊（toc-block）來幫助使用者快速瀏覽內容，但有時候目錄內容（gs-toc）可能因為資料不完整或條件不符而不存在，這時候空白的目錄區塊反而會影響頁面美觀與使用體驗。本文示範如何用簡單的 JavaScript 程式碼，在頁面載入完成後自動檢查並隱藏沒有目錄內容的 toc-block，適合前端工程師或自學者優化動態內容顯示。</p>
<h2 class="wp-block-heading">為什麼要隱藏空的 toc-block</h2>
<p>空的目錄區塊會佔據頁面空間，造成視覺上的空洞，甚至誤導使用者以為頁面資料不完整。透過程式自動判斷並隱藏，可以提升頁面整潔度與使用者體驗，尤其在內容經常變動或由後端動態產生時更為重要。</p>
<h2 class="wp-block-heading">主要程式碼說明</h2>
<p>以下是核心程式碼片段：</p>
<pre><code class="lang-javascript language-javascript javascript">document.addEventListener(&quot;DOMContentLoaded&quot;, function() {
  const tocBlocks = document.querySelectorAll(&quot;.toc-block&quot;);

  tocBlocks.forEach(block =&gt; {
    const hasToc = block.querySelector(&quot;.gs-toc&quot;);
    if (!hasToc) {
      block.style.display = &quot;none&quot;;
    }
  });
});</code></pre>
<ul>
<li>使用 <code>DOMContentLoaded</code> 事件確保 DOM 元素已經載入完畢，避免查詢不到元素。</li>
<li>利用 <code>document.querySelectorAll</code> 選取所有 <code>.toc-block</code> 元素，方便批次處理。</li>
<li>針對每個 toc-block，使用 <code>querySelector</code> 檢查是否含有 <code>.gs-toc</code> 子元素。</li>
<li>若找不到 <code>.gs-toc</code>，代表此目錄區塊沒有內容，將其 <code>display</code> 設為 <code>none</code>，達到隱藏效果。</li>
</ul>
<h2 class="wp-block-heading">實務應用與優化方向</h2>
<ul>
<li>此方法適用於靜態或動態產生的目錄區塊，確保頁面不會顯示空白區塊。</li>
<li>若目錄內容是透過 AJAX 載入，需將檢查邏輯放在資料載入完成後執行。</li>
<li>可搭配 CSS 動畫效果，讓隱藏過程更為平滑。</li>
<li>若頁面中 toc-block 數量龐大，考慮使用更有效率的 DOM 操作或節流機制。</li>
</ul>
<h2 class="wp-block-heading">常見問題與注意事項</h2>
<ul>
<li>確認 <code>.gs-toc</code> 是目錄內容的正確 class 名稱，避免誤判。</li>
<li>若頁面中有多層目錄結構，需調整選擇器以符合需求。</li>
<li>使用 <code>display: none</code> 會完全移除元素佔用空間，若想保留佔位可改用其他 CSS 屬性。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-javascript language-javascript javascript">document.addEventListener(&quot;DOMContentLoaded&quot;, function() {
  // 取得所有 toc-block
  const tocBlocks = document.querySelectorAll(&quot;.toc-block&quot;);

  tocBlocks.forEach(block =&gt; {
    // 檢查這個 toc-block 底下是否有 gs-toc
    const hasToc = block.querySelector(&quot;.gs-toc&quot;);

    // 如果沒有找到，則隱藏整個區塊
    if (!hasToc) {
      block.style.display = &quot;none&quot;;
    }
  });
});</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/javascript-hide-empty-toc-block/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>為 WordPress 分類清單實作手風琴展開收合功能</title>
		<link>https://piglife.tw/technical-notes/wordpress-category-accordion/</link>
					<comments>https://piglife.tw/technical-notes/wordpress-category-accordion/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Wed, 10 Dec 2025 10:21:20 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[DOM操作]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://piglife.tw/technical-notes/wordpress-category-accordion/</guid>

					<description><![CDATA[這篇文章解析如何為 WordPress 分類清單加入手風琴展開收合功能，包含無障礙 ARIA 屬性設...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>在 WordPress 網站中，分類清單常用於側邊欄或導覽列，當分類層級較多時，使用者體驗會因為清單過長而降低。這段程式碼的目標是為多層分類清單加入手風琴（Accordion）功能，使得使用者可以展開或收合子分類，提升導覽的清晰度與操作便利性。本文適合有基礎 JavaScript 與 DOM 操作經驗的前端工程師或自學者，想了解如何結合無障礙設計（ARIA）與動態 DOM 操作來優化分類清單。</p>
<h2 class="wp-block-heading">目標與使用情境</h2>
<p>這段程式碼主要解決以下問題：</p>
<ul>
<li>多層分類清單展開收合控制</li>
<li>一次只允許展開一個分類（手風琴嚴格模式）</li>
<li>兼顧無障礙操作，使用 ARIA 屬性提示狀態</li>
<li>保持原有連結功能不受影響</li>
</ul>
<p>適用於 WordPress 產生的分類清單（如 <code>.wp-block-categories</code> 或 <code>.wp-block-categories-list</code>），並且希望加強使用者操作體驗與無障礙支援。</p>
<h2 class="wp-block-heading">主要流程解析</h2>
<h3 class="wp-block-heading">1. 選取目標清單容器</h3>
<pre><code class="lang-javascript language-javascript javascript">const roots = Array.from(document.querySelectorAll(&#039;.wp-block-categories, .wp-block-categories-list&#039;));
if (!roots.length) return;</code></pre>
<p>這裡先找出所有符合條件的分類清單容器，若無則直接結束，避免不必要的後續操作。</p>
<h3 class="wp-block-heading">2. 設定手風琴模式</h3>
<pre><code class="lang-javascript language-javascript javascript">const singleOpen = true;</code></pre>
<p>設定是否一次只允許展開一個分類項目，<code>true</code> 表示嚴格手風琴模式。</p>
<h3 class="wp-block-heading">3. 標記並處理有子分類的項目</h3>
<pre><code class="lang-javascript language-javascript javascript">const parents = Array.from(root.querySelectorAll(&#039;li.cat-item &gt; ul.children&#039;))
  .map(ul =&gt; ul.parentElement)
  .filter(li =&gt; li &amp;&amp; li.matches(&#039;.cat-item&#039;));</code></pre>
<p>找出所有有子清單的分類項目（<code>li.cat-item</code> 中包含 <code>ul.children</code>），這些項目需要被加上展開收合功能。</p>
<h3 class="wp-block-heading">4. 加入切換按鈕與 ARIA 屬性</h3>
<pre><code class="lang-javascript language-javascript javascript">const toggleBtn = document.createElement(&#039;button&#039;);
toggleBtn.className = &#039;cat-toggle&#039;;
toggleBtn.type = &#039;button&#039;;
toggleBtn.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);
toggleBtn.setAttribute(&#039;aria-controls&#039;, subId);
toggleBtn.setAttribute(&#039;title&#039;, &#039;展開/收合&#039;);</code></pre>
<p>為每個有子分類的 <code>li</code> 加入一個按鈕，用於切換展開狀態，同時透過 <code>aria-expanded</code> 與 <code>aria-controls</code> 屬性提升無障礙體驗。</p>
<h3 class="wp-block-heading">5. 切換展開收合狀態的邏輯</h3>
<pre><code class="lang-javascript language-javascript javascript">function toggle(open) {
  const willOpen = (typeof open === &#039;boolean&#039;) ? open : !li.classList.contains(&#039;is-open&#039;);
  if (willOpen) {
    if (singleOpen) {
      const siblings = Array.from(li.parentElement.children)
        .filter(el =&gt; el !== li &amp;&amp; el.classList &amp;&amp; el.classList.contains(&#039;is-open&#039;));
      siblings.forEach(sib =&gt; {
        sib.classList.remove(&#039;is-open&#039;);
        const btn = sib.querySelector(&#039;:scope &gt; .cat-toggle&#039;);
        if (btn) btn.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);
      });
    }
    li.classList.add(&#039;is-open&#039;);
    toggleBtn.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);
  } else {
    li.classList.remove(&#039;is-open&#039;);
    toggleBtn.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);
  }
}</code></pre>
<p>此函式負責切換展開狀態，若啟用嚴格手風琴模式，會同時關閉同層其他已展開的分類。狀態透過 <code>is-open</code> 類別與按鈕的 <code>aria-expanded</code> 屬性同步更新。</p>
<h3 class="wp-block-heading">6. 綁定事件與點擊行為控制</h3>
<ul>
<li>按鈕點擊時切換展開狀態</li>
<li>點擊非連結與非按鈕的 <code>li</code> 項目空白處也能切換展開，避免影響原本連結導頁</li>
</ul>
<pre><code class="lang-javascript language-javascript javascript">toggleBtn.addEventListener(&#039;click&#039;, function(e) {
  e.preventDefault();
  e.stopPropagation();
  toggle();
});

li.addEventListener(&#039;click&#039;, function(e) {
  const isLink = e.target.closest(&#039;a&#039;);
  const isBtn  = e.target.closest(&#039;.cat-toggle&#039;);
  if (!isLink &amp;&amp; !isBtn) {
    e.preventDefault();
    toggle();
  }
});</code></pre>
<h3 class="wp-block-heading">7. 預設展開目前分類項目</h3>
<p>若分類項目本身或其子分類包含 <code>current-cat</code> 或 <code>current-cat-parent</code> 類別，則預設展開，方便使用者辨識目前所在分類。</p>
<pre><code class="lang-javascript language-javascript javascript">if (li.classList.contains(&#039;current-cat&#039;) ||
    li.classList.contains(&#039;current-cat-parent&#039;) ||
    sub.querySelector(&#039;.current-cat, .current-cat-parent&#039;)) {
  toggle(true);
}</code></pre>
<h2 class="wp-block-heading">實務應用與優化方向</h2>
<ul>
<li>可依需求調整 <code>singleOpen</code> 為 <code>false</code>，允許多個分類同時展開</li>
<li>按鈕樣式與動畫可加入 CSS 過渡效果，提升使用者體驗</li>
<li>可擴充鍵盤操作支援，增強無障礙友善度</li>
<li>若分類清單動態更新，需額外處理事件重新綁定</li>
</ul>
<h2 class="wp-block-heading">常見問題與注意事項</h2>
<ul>
<li>插入按鈕位置需謹慎，避免破壞原本的連結結構</li>
<li>使用 <code>aria-controls</code> 需確保對應的子清單有唯一 ID</li>
<li>點擊事件阻止預設行為，避免影響連結跳轉，需確認邏輯正確</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-javascript language-javascript javascript">document.addEventListener(&#039;DOMContentLoaded&#039;, function() {
  // 你貼的清單容器 class
  const roots = Array.from(document.querySelectorAll(&#039;.wp-block-categories, .wp-block-categories-list&#039;));
  if (!roots.length) return;

  // 設定：是否一次只開一個（true = 手風琴嚴格模式）
  const singleOpen = true;

  roots.forEach(function(root) {
    // 標記啟用
    root.classList.add(&#039;cat-accordion&#039;);

    // 找到有 children 的 cat-item
    const parents = Array.from(root.querySelectorAll(&#039;li.cat-item &gt; ul.children&#039;))
      .map(ul =&gt; ul.parentElement)
      .filter(li =&gt; li &amp;&amp; li.matches(&#039;.cat-item&#039;));

    // 為每個需要的 li 補上 class 與切換按鈕/ARIA
    parents.forEach(function(li, idx) {
      li.classList.add(&#039;has-children&#039;);

      const sub = li.querySelector(&#039;:scope &gt; ul.children&#039;);
      // 建唯一 id，便於 aria-controls 連結
      const subId = sub.id || (&#039;cat-sub-&#039; + Math.random().toString(36).slice(2));
      sub.id = subId;

      // 建立切換按鈕（放在 
&lt;a&gt; 後面，不影響原有點擊導覽）
      const toggleBtn = document.createElement(&#039;button&#039;);
      toggleBtn.className = &#039;cat-toggle&#039;;
      toggleBtn.type = &#039;button&#039;;
      toggleBtn.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);
      toggleBtn.setAttribute(&#039;aria-controls&#039;, subId);
      toggleBtn.setAttribute(&#039;title&#039;, &#039;展開/收合&#039;);

      // 插到 a 後（若沒有 a，則插到 li 內最前）
      const anchor = li.querySelector(&#039;:scope &gt; a&#039;);
      if (anchor &amp;&amp; anchor.nextSibling) {
        anchor.parentNode.insertBefore(toggleBtn, anchor.nextSibling);
      } else if (anchor) {
        anchor.parentNode.appendChild(toggleBtn);
      } else {
        li.insertBefore(toggleBtn, li.firstChild);
      }

      // 切換邏輯
      function toggle(open) {
        const willOpen = (typeof open === &#039;boolean&#039;) ? open : !li.classList.contains(&#039;is-open&#039;);
        if (willOpen) {
          if (singleOpen) {
            // 關閉同層其他
            const siblings = Array.from(li.parentElement.children)
              .filter(el =&gt; el !== li &amp;&amp; el.classList &amp;&amp; el.classList.contains(&#039;is-open&#039;));
            siblings.forEach(sib =&gt; {
              sib.classList.remove(&#039;is-open&#039;);
              const btn = sib.querySelector(&#039;:scope &gt; .cat-toggle&#039;);
              if (btn) btn.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);
            });
          }
          li.classList.add(&#039;is-open&#039;);
          toggleBtn.setAttribute(&#039;aria-expanded&#039;, &#039;true&#039;);
        } else {
          li.classList.remove(&#039;is-open&#039;);
          toggleBtn.setAttribute(&#039;aria-expanded&#039;, &#039;false&#039;);
        }
      }

      // 綁定按鈕點擊
      toggleBtn.addEventListener(&#039;click&#039;, function(e) {
        e.preventDefault();
        e.stopPropagation();
        toggle();
      });

      // 如果想讓點 li 標題也能展開（但避免影響連結導頁）：
      // 只在點擊「li 空白處」才切換；點到 a 時仍正常導頁
      li.addEventListener(&#039;click&#039;, function(e) {
        const isLink = e.target.closest(&#039;a&#039;);
        const isBtn  = e.target.closest(&#039;.cat-toggle&#039;);
        if (!isLink &amp;&amp; !isBtn) {
          e.preventDefault();
          toggle();
        }
      });

      // 進階：如果此項或其子層有 &quot;current-cat&quot; / &quot;current-cat-parent&quot; 類別，預設展開
      if (li.classList.contains(&#039;current-cat&#039;) ||
          li.classList.contains(&#039;current-cat-parent&#039;) ||
          sub.querySelector(&#039;.current-cat, .current-cat-parent&#039;)) {
        toggle(true);
      }
    });
  });
});</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/wordpress-category-accordion/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>原生 JavaScript 實作 Tab 功能選單筆記</title>
		<link>https://piglife.tw/technical-notes/native-javascript-tab-menu/</link>
					<comments>https://piglife.tw/technical-notes/native-javascript-tab-menu/#respond</comments>
		
		<dc:creator><![CDATA[小豬]]></dc:creator>
		<pubDate>Thu, 02 Nov 2017 17:06:42 +0000</pubDate>
				<category><![CDATA[技術筆記]]></category>
		<category><![CDATA[DOM操作]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[tab功能]]></category>
		<category><![CDATA[事件處理]]></category>
		<category><![CDATA[前端設計]]></category>
		<category><![CDATA[原生js]]></category>
		<guid isPermaLink="false">https://piglife.tw/?p=237</guid>

					<description><![CDATA[介紹如何使用原生 JavaScript 實作 Tab 功能選單，說明 HTML 結構與 JavaSc...]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">前言</h2>
<p>Tab 功能是前端常見的 UI 元件，許多框架都提供現成的實作，但若想深入理解其背後的運作原理，使用原生 JavaScript 自行實作是一個很好的練習。本文適合有基礎程式能力的工程師或自學者，透過範例拆解，了解如何控制元素顯示與切換樣式。</p>
<h2 class="wp-block-heading">HTML 結構說明</h2>
<p>Tab 功能主要由兩部分組成：控制按鈕與內容區塊。</p>
<ul>
<li>按鈕部分使用 <code> &lt;a&gt;</code> 標籤，<code>href=&quot;javascript:void(0)&quot;</code> 用來阻止預設跳轉行為。</li>
<li>按鈕會帶有 <code>tablink</code> 及特定的 <code>testbtn</code> class，方便 JavaScript 操作。</li>
<li><code>onclick</code> 事件會呼叫 <code>openClass(event, &#039;classX&#039;)</code>，帶入事件物件與對應內容區塊的 id。</li>
<li>內容區塊以 <code>id</code> 和 <code>class=&quot;class&quot;</code> 標示，對應按鈕的參數，方便顯示與隱藏。</li>
</ul>
<pre><code class="lang-html language-html html">&lt;div class=&quot;bar-block&quot;&gt;
  &lt;div class=&quot;container&quot;&gt;

&lt;h5&gt;選單&lt;/h5&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink testbtn&quot; onclick=&quot;openClass(event, &#039;class1&#039;)&quot;&gt;教學1&lt;/a&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink&quot; onclick=&quot;openClass(event, &#039;class2&#039;)&quot;&gt;教學2&lt;/a&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink&quot; onclick=&quot;openClass(event, &#039;class3&#039;)&quot;&gt;教學3&lt;/a&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink&quot; onclick=&quot;openClass(event, &#039;class4&#039;)&quot;&gt;教學4&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;class1&quot; class=&quot;container class&quot;&gt;
  教學1內容
&lt;/div&gt;
&lt;div id=&quot;class2&quot; class=&quot;container class&quot;&gt;
  教學2內容
&lt;/div&gt;
&lt;div id=&quot;class3&quot; class=&quot;container class&quot;&gt;
  教學3內容
&lt;/div&gt;
&lt;div id=&quot;class4&quot; class=&quot;container class&quot;&gt;
  教學4內容
&lt;/div&gt;</code></pre>
<h2 class="wp-block-heading">JavaScript 功能解析</h2>
<p><code>openClass</code> 函式負責切換 Tab 的顯示狀態與按鈕樣式：</p>
<ol>
<li>使用 <code>document.getElementsByClassName(&quot;class&quot;)</code> 取得所有內容區塊，並將它們隱藏（<code>display = &quot;none&quot;</code>）。</li>
<li>取得所有按鈕元素 <code>tablinks</code>，並移除所有按鈕的 <code>red</code> 樣式，確保只有一個按鈕被標示為選中。</li>
<li>根據傳入的 <code>className</code> 參數，將對應內容區塊顯示（<code>display = &quot;block&quot;</code>）。</li>
<li>對當前點擊的按鈕元素新增 <code>red</code> class，標示為選中狀態。</li>
</ol>
<pre><code class="lang-js language-js js">function openClass(evt, className) {
  var i, x, tablinks;
  x = document.getElementsByClassName(&quot;class&quot;);
  for (i = 0; i &lt; x.length; i++) {
    x[i].style.display = &quot;none&quot;;
  }
  tablinks = document.getElementsByClassName(&quot;tablink&quot;);
  for (i = 0; i &lt; tablinks.length; i++) {
    tablinks[i].classList.remove(&quot;red&quot;);
  }
  document.getElementById(className).style.display = &quot;block&quot;;
  evt.currentTarget.classList.add(&quot;red&quot;);
}

// 頁面載入後自動觸發第一個帶有 testbtn 的按鈕點擊
var mybtn = document.getElementsByClassName(&quot;testbtn&quot;)[0];
mybtn.click();</code></pre>
<h2 class="wp-block-heading">實際應用與延伸</h2>
<ul>
<li>可依需求調整 <code>red</code> class 的 CSS 樣式，達到不同的選中效果。</li>
<li>若 Tab 數量動態產生，需確保事件綁定正確。</li>
<li>可改用事件代理方式優化效能。</li>
<li>進階可結合動畫效果，提升使用者體驗。</li>
</ul>
<h2 class="wp-block-heading">常見坑點</h2>
<ul>
<li>忘記阻止 <code> &lt;a&gt;</code> 的預設行為會導致頁面跳轉。</li>
<li>未正確移除其他按鈕的選中樣式，導致多個 Tab 同時被標示。</li>
<li>忘記預設觸發第一個 Tab，頁面初始狀態會空白。</li>
</ul>
<h2 class="wp-block-heading">完整程式碼</h2>
<pre><code class="lang-text language-text text">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;zh-TW&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;title&gt;原生 JavaScript Tab 功能&lt;/title&gt;
&lt;style&gt;
  .red { color: red; font-weight: bold; }
  .class { display: none; }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div class=&quot;bar-block&quot;&gt;
  &lt;div class=&quot;container&quot;&gt;

&lt;h5&gt;選單&lt;/h5&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink testbtn&quot; onclick=&quot;openClass(event, &#039;class1&#039;)&quot;&gt;教學1&lt;/a&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink&quot; onclick=&quot;openClass(event, &#039;class2&#039;)&quot;&gt;教學2&lt;/a&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink&quot; onclick=&quot;openClass(event, &#039;class3&#039;)&quot;&gt;教學3&lt;/a&gt;
    &lt;a href=&quot;javascript:void(0)&quot; class=&quot;bar-item button tablink&quot; onclick=&quot;openClass(event, &#039;class4&#039;)&quot;&gt;教學4&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div id=&quot;class1&quot; class=&quot;container class&quot;&gt;
  教學1內容
&lt;/div&gt;
&lt;div id=&quot;class2&quot; class=&quot;container class&quot;&gt;
  教學2內容
&lt;/div&gt;
&lt;div id=&quot;class3&quot; class=&quot;container class&quot;&gt;
  教學3內容
&lt;/div&gt;
&lt;div id=&quot;class4&quot; class=&quot;container class&quot;&gt;
  教學4內容
&lt;/div&gt;

&lt;script&gt;
function openClass(evt, className) {
  var i, x, tablinks;
  x = document.getElementsByClassName(&quot;class&quot;);
  for (i = 0; i &lt; x.length; i++) {
    x[i].style.display = &quot;none&quot;;
  }
  tablinks = document.getElementsByClassName(&quot;tablink&quot;);
  for (i = 0; i &lt; tablinks.length; i++) {
    tablinks[i].classList.remove(&quot;red&quot;);
  }
  document.getElementById(className).style.display = &quot;block&quot;;
  evt.currentTarget.classList.add(&quot;red&quot;);
}

var mybtn = document.getElementsByClassName(&quot;testbtn&quot;)[0];
mybtn.click();
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></content:encoded>
					
					<wfw:commentRss>https://piglife.tw/technical-notes/native-javascript-tab-menu/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
