// タブ切り替え (function () { const init = () => { const groups = document.querySelectorAll('.tabs[data-tabs]'); groups.forEach((root) => { const tablist = root.querySelector('[role="tablist"]'); const tabs = Array.from(root.querySelectorAll('[role="tab"]')); // 上部のみ const panels = Array.from(root.querySelectorAll('[role="tabpanel"]')); const jumpers = Array.from(root.querySelectorAll('[data-tab-target]')); // 下部ボタン // マッピング検証 tabs.forEach((t) => { const pid = t.getAttribute('aria-controls'); const panel = pid && root.querySelector(`#${pid}`); if (!panel) console.warn('[tabs] パネルが見つかりません:', t, '→', pid); }); // 初期表示の整合 const firstSelected = tabs.find(t => t.getAttribute('aria-selected') === 'true') || tabs[0]; activateTab(tabs, panels, firstSelected, { focusTab: false }); // クリック(上部タブ) tabs.forEach((tab) => { tab.addEventListener('click', () => activateTab(tabs, panels, tab, { focusTab: false })); }); // キーボード(上部タブ) tablist?.addEventListener('keydown', (e) => { const i = tabs.indexOf(document.activeElement); if (i === -1) return; switch (e.key) { case 'ArrowRight': case 'ArrowDown': e.preventDefault(); tabs[(i + 1) % tabs.length].focus(); break; case 'ArrowLeft': case 'ArrowUp': e.preventDefault(); tabs[(i - 1 + tabs.length) % tabs.length].focus(); break; case 'Home': e.preventDefault(); tabs[0].focus(); break; case 'End': e.preventDefault(); tabs[tabs.length - 1].focus(); break; case 'Enter': case ' ': e.preventDefault(); activateTab(tabs, panels, document.activeElement, { focusTab: true }); break; } }); // 下部ボタン(data-tab-target)→ 上部タブを起動 jumpers.forEach((btn) => { btn.addEventListener('click', () => { const targetId = btn.getAttribute('data-tab-target'); if (!targetId) return; const targetTab = root.querySelector(`#${CSS && CSS.escape ? CSS.escape(targetId) : targetId}`); if (targetTab && tabs.includes(targetTab)) { // パネルへフォーカス(読み上げ用) activateTab(tabs, panels, targetTab, { focusTab: false, focusPanel: true }); } }); }); // ハッシュ対応(#tab-2 / #panel-2) const deepLink = () => { const hash = window.location.hash.slice(1); if (!hash) return; const target = tabs.find(t => t.id === hash || t.getAttribute('aria-controls') === hash); if (target) activateTab(tabs, panels, target, { focusTab: true }); }; window.addEventListener('hashchange', deepLink); deepLink(); }); }; function activateTab(tabs, panels, tab, { focusTab = false, focusPanel = false } = {}) { if (!tab) return; const pid = tab.getAttribute('aria-controls'); const panel = pid && document.getElementById(pid); // 上部タブの on/off を一元管理 tabs.forEach(t => { const selected = t === tab; t.setAttribute('aria-selected', selected ? 'true' : 'false'); t.tabIndex = selected ? 0 : -1; }); // パネル表示 panels.forEach(p => { p.hidden = (p !== panel); }); // フォーカス制御 if (focusTab) tab.focus(); if (focusPanel && panel) panel.focus(); } // DOM 準備後に実行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init, { once: true }); } else { init(); } })();