タブ1のコンテンツ

Explanation

  • タブ数が画面幅を超える際は、横スクロールができるようになっています。
  • 最初にアクティブにするタブの設定はURLに.js-tabGroupがついた要素のidをキーとし、タブのボタンに指定したaria-controlsの値を値に設定します。

    例)https://www.kblog53.com/demos/tab-menu-switchable-by-query-parameters?tabA=tabpanel1

Sample Code

index.html

<div id="tabA" class="tab-menu js-tabGroup">
  <div class="tab-menu__controls" role="tablist">
    <button
      type="button"
      id="tab1"
      class="tab-menu__control-item"
      role="tab"
      aria-controls="tabpanel1"
      aria-selected="true"
    >
      タブ1
    </button>
    <button
      type="button"
      id="tab2"
      class="tab-menu__control-item"
      role="tab"
      aria-controls="tabpanel2"
      aria-selected="false"
    >
      タブ2
    </button>
    <button
      type="button"
      id="tab3"
      class="tab-menu__control-item"
      role="tab"
      aria-controls="tabpanel3"
      aria-selected="false"
    >
      タブ3
    </button>
  </div>

  <div class="tab-menu__panels">
    <div
      id="tabpanel1"
      class="tab-menu__panel-item"
      role="tabpanel"
      aria-labelledby="tab1"
    >
      <p>タブ1のコンテンツ</p>
    </div>
    <div
      id="tabpanel2"
      class="tab-menu__panel-item"
      role="tabpanel"
      aria-labelledby="tab2"
      hidden
    >
      <p>タブ2のコンテンツ</p>
    </div>
    <div
      id="tabpanel3"
      class="tab-menu__panel-item"
      role="tabpanel"
      aria-labelledby="tab3"
      hidden
    >
      <p>タブ3のコンテンツ</p>
    </div>
  </div>
</div>

style.css

.tab-menu__controls {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.tab-menu__control-item {
  flex-shrink: 0;
  padding: 5px 15px;
  background-color: unset;
  border: none;
  border-bottom: 3px solid #f5f5f5;
  transition: all 0.1s ease-in-out;
}

.tab-menu__control-item[aria-selected=true] {
  font-weight: 600;
  border-bottom-color: #78b6f5;
}

.tab-menu__panels {
  margin-top: 10px;
}

.tab-menu__panel-item {
  padding: 20px 10px;
  background-color: #f5f5f5;
}

@media (hover: hover) {
  .tab-menu__control-item {
    cursor: pointer;
  }
}

index.js

"use strict";
document.addEventListener("DOMContentLoaded", () => {
  initTabMenu();
});
/**
 * タブメニューを初期化する
 */
const initTabMenu = () => {
  const TAB_GROUP_SELECTOR = ".js-tabGroup";
  const TAB_CONTROL_SELECTOR = '[role="tab"]';
  const TAB_PANEL_SELECTOR = '[role="tabpanel"]';
  const tabGroups = document.querySelectorAll(TAB_GROUP_SELECTOR);
  tabGroups.forEach((tabGroup) => {
    const tabs = tabGroup.querySelectorAll(TAB_CONTROL_SELECTOR);
    tabs.forEach((tab) => {
      tab.addEventListener("click", changeTabs);
    });
    const tabId = tabGroup.getAttribute("id");
    if (tabId) {
      // クエリパラメータでタブを指定している場合、そのタブを表示する
      const urlParams = new URLSearchParams(window.location.search);
      const tabParam = urlParams.get(tabId);
      const targetTab = tabGroup.querySelector(`[aria-controls="${tabParam}"]`);
      if (targetTab) {
        targetTab.click();
      }
    }
  });
  function changeTabs(e) {
    const _target = e.currentTarget;
    if (!_target) return;
    const _tabGroup = _target.closest(TAB_GROUP_SELECTOR);
    if (!_tabGroup) return;
    const _tabs = _tabGroup.querySelectorAll(TAB_CONTROL_SELECTOR);
    const _panels = _tabGroup.querySelectorAll(TAB_PANEL_SELECTOR);
    // すべてのタブのaria-selectedをfalseに設定
    _tabs.forEach((_tab) => {
      _tab.setAttribute("aria-selected", "false");
    });
    // クリックされたタブのaria-selectedをtrueに設定
    _target.setAttribute("aria-selected", "true");
    // すべてのパネルを非表示にする
    _panels.forEach((_panel) => {
      _panel.setAttribute("hidden", "true");
    });
    // クリックされたタブに対応するパネルを表示する
    const _targetPanelId = _target.getAttribute("aria-controls");
    const _targetPanel = _tabGroup.querySelector(`#${_targetPanelId}`);
    if (_targetPanel) {
      _targetPanel.removeAttribute("hidden");
    }
  }
};