🕌

アクセシビリティに配慮したタブパネルをFLOCSSとVanillaJSで書きたい

2022/07/04に公開約3,700字

挙動(ソース)

※resetCSSを前提とした必要最低限のコードしか書いていないのでリストに「・」が出てますがご容赦ください...(コードの見通し悪くしたくない)

コード(見やすい用)

HTML

      <!-- タブナビゲーション -->
      <ul class="c-tab-navigation">
        <li class="p-tab-navigation__list">
          <a href=".p-tabpanel__item--1" class="p-tab-navigation__anchor js-tabAnchor" aria-label="このタイトルに関連するタブを開く" role="tab" aria-expanded="false" aria-controls="panel-1" id="tab-1">
            タブ1
          </a>
        </li>
        <!-- hrefに記述されたクラス名のパネルを表示-->
        <li class="p-tab-navigation__list">
          <a href=".p-tabpanel__item--2" class="p-tab-navigation__anchor js-tabAnchor" aria-label="このタイトルに関連するタブを開く" role="tab" aria-expanded="false" aria-controls="panel-2" id="tab-2">
            タブ2
          </a>
        </li>
        <!-- hrefに記述されたクラス名のパネルを表示-->
        <li class="p-tab-navigation__list">
          <a href=".p-tabpanel__item--3" class="p-tab-navigation__anchor js-tabAnchor" aria-label="このタイトルに関連するタブを開く" role="tab" aria-expanded="false" aria-controls="panel-3" id="tab-3">
            タブ3
          </a>
        </li>
        <!-- hrefに記述されたクラス名のパネルを表示-->
      </ul>

      <!-- タブパネル -->
      <div class="c-tabpanel">
        <div class="p-tabpanel__item p-tabpanel__item--1 js-tabpanel" role="tabpanel" aria-controls="panel-1" id="tab-1" aria-hidden="true">
          <!-- ここにパネルの中身を記述 -->
          <p>パネル1</p>
        </div>

        <div class="p-tabpanel__item p-tabpanel__item--2 js-tabpanel" role="tabpanel" aria-controls="panel-2" id="tab-2" aria-hidden="true">
          <!-- ここにパネルの中身を記述 -->
          <p>パネル2</p>
        </div>

        <div class="p-tabpanel__item p-tabpanel__item--3 js-tabpanel" role="tabpanel" aria-controls="panel-3" id="tab-3" aria-hidden="true">
          <!-- ここにパネルの中身を記述 -->
          <p>パネル3</p>
        </div>

      </div>

SCSS

.c-tab-navigation {
  display: flex;
  .p-tab-navigation__list {
    .p-tab-navigation__anchor {
      color: #2b2b2b; // 非アクティブなナビカラー
      background-color: #f4f5f7; // 非アクティブなナビ背景色
      transition: 0.2s;
      &:hover,
      &.is-active {
        color: #f4f5f7;
        background-color: #2b2b2b;
      }
    }
  }
}

.c-tabpanel {
  position: relative;
  top: 0;
  left: 0;
  .p-tabpanel__item {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    visibility: hidden;
    transition: 0.2s;
    &.is-active {
      opacity: 1;
      visibility: visible;
      transition: 0.4s;
    }
  }
}

JavaScript

const tabAnchors = document.querySelectorAll(".js-tabAnchor");
const tabLength = tabAnchors.length;
const tabPanel = document.querySelectorAll(".js-tabpanel");
const panelLength = tabPanel.length;

tabAnchors.forEach(function (elem, index) {
  elem.addEventListener("click", function (event) {
    event.preventDefault();
    if (elem.classList.contains("is-active")) {
      return;
    }
    for (let i = 0; i < tabLength; i++) {
      tabAnchors[i].classList.remove("is-active");
      // 一旦全てのトリガーの選択状態をfalseに
      tabAnchors[i].setAttribute("aria-expanded", "false");
      // 一旦全てのパネルを非表示に
      tabPanel[i].classList.remove("is-active");
      // 一旦全てのパネルの非表示状態をfalseに
      tabPanel[i].setAttribute("aria-hidden", "true");
    }
    // クリックされたタブナビゲーションを選択状態に
    elem.classList.add("is-active");
    elem.setAttribute("aria-expanded", "true");
    const activeTabAttr = elem.getAttribute("href");
    const activeTab = document.querySelector(activeTabAttr);
    // 対応するタブパネルを表示状態に
    activeTab.classList.add("is-active");
    activeTab.setAttribute("aria-hidden", "false");
  });
});

//最初のタブのクリックイベントを発火状態に設定
tabAnchors[0].click();

Discussion

ログインするとコメントできます