Written by TSUYOSHI

JavaScript ES6 でタブ機能を実装する

JavaScript PROGRAMMING

本記事では、JavaScriptのES6でタブ機能を実装する方法とソースコードの解説をします。
タブのトリガーとなるメニューをクリックした際に、コンテンツが切り替わる実装をしていて、jQueryは利用していません。

この記事を書いている僕は、フロントエンドのプログラマー(フリーランス)として、これまで3年以上のWeb制作経験があり、HTMLやCSSのマークアップからJavaScriptのフレームワークであるVue,React,Nuxtなどフロントエンドの開発を得意としています。

この記事を読むことで、ES6のモダンな書き方でタブ実装を学ぶことができます。

GitHubサンプルコード

サンプルコードとして、GitHubにコードを上げています。
https://github.com/it-web-life/javascript_es6_tab
※「git clone」するか、緑色の「Code」ボタンをクリックして「Download ZIP」をクリックしてダウンロードして中身を解凍してください。

完成したソースコードについて、動作を確認できるURLは以下になります。
https://it-web-life.github.io/javascript_es6_tab/

タブ機能の実装

タブの項目をクリックすると、紐付けられたコンテンツ部分が表示される仕組みで実装するので、以下のようにclassを付与します。

・タブのトリガーとなる各要素:「js-tab-trigger」
・タブのコンテンツとなる各要素:「js-tab-content」

また「js-tab-trigger」「js-tab-content」の両方に2つのdata属性を付与します。

・「js-tab-trigger」と「js-tab-content」で紐付いている要素に「data-tab-name」で同じ名前をつけます。
・切替可能なタブが複数ある場合を考慮して、同じタブのグループには「data-tab-group」を追加します。

そして、選択中のタブ要素には、「.is-current」をつけます。
選択されていないタブのコンテンツにはstyle=”display:none;”をつけて非表示にします。

▼index.html

<div class="tab">
  <!-- Tab 項目 -->
  <div class="tab__button">
    <ul class="list">
      <!-- ①選択中のタブ項目に.is-currentを付与する -->
      <li
        class="js-tab-trigger is-current"
        data-tab-group="tabGroup1"
        data-tab-name="cat01"
      >
        タブの項目1
      </li>
      <li
        class="js-tab-trigger"
        data-tab-group="tabGroup1"
        data-tab-name="cat02"
      >
        タブの項目2
      </li>
      <li
        class="js-tab-trigger item"
        data-tab-group="tabGroup1"
        data-tab-name="cat03"
      >
        タブの項目3
      </li>
      <li
        class="js-tab-trigger item"
        data-tab-group="tabGroup1"
        data-tab-name="cat04"
      >
        タブの項目4
      </li>
    </ul>
  </div>

  <!-- Tab コンテンツ -->
  <div class="tab__content">
    <!-- ②選択中のコンテンツに.is-currentを付与する -->
    <div
      class="js-tab-content is-current"
      data-tab-group="tabGroup1"
      data-tab-name="cat01"
    >
      【タブのコンテンツ1】
    </div>
    <!-- ③選択されていない非表示のコンテンツにstyle="display: none;"をつける -->
    <div
      class="js-tab-content"
      style="display: none;"
      data-tab-group="tabGroup1"
      data-tab-name="cat02"
    >
      【タブのコンテンツ2】
    </div>
    <!-- ③選択されていない非表示のコンテンツにstyle="display: none;"をつける -->
    <div
      class="js-tab-content"
      style="display: none;"
      data-tab-group="tabGroup1"
      data-tab-name="cat03"
    >
      【タブのコンテンツ3】
    </div>
    <!-- ③選択されていない非表示のコンテンツにstyle="display: none;"をつける -->
    <div
      class="js-tab-content"
      style="display: none;"
      data-tab-group="tabGroup1"
      data-tab-name="cat04"
    >
      【タブのコンテンツ4】
    </div>
  </div>
</div>

以下のようにtab.jsにタブの機能をES6で書いていきます。
最初に取得したHTML Elementや紐づくdata属性を覚えておくために、トリガー用とコンテンツ用に別々に格納場所を用意します。
▼tab.js

const tabTriggers = [];
const tabContents = [];

次にタブの要素を取得して、「tabTriggers」「tabContents」へ格納する処理を書きます。
「generateTab()」というメソッドで書きます。
▼tab.js

export const generateTab = (wrapper) => {
  const triggers = [...wrapper.getElementsByClassName('js-tab-trigger')];
  const contents = [...wrapper.getElementsByClassName('js-tab-content')];

  // Tabトリガー設定
  for (const trigger of triggers) {
    // data属性のlistとinfoを取得する
    const triggerGroup= trigger.getAttribute('data-tab-group');
    const triggerName = trigger.getAttribute('data-tab-name');

    // data属性のlistとinfoは設定必須なのでチェック
    if (!triggerGroup || !triggerName) {
      continue;
    }

    // Tabのトリガーを設定
    tabTriggers.push({
      element: trigger,
      group: triggerGroup,
      name: triggerName,
    });

    // トリガーのクリックイベントを設定
    trigger.addEventListener('click', () => changeTab(event, triggerGroup, triggerName));
  }

  // Tabコンテンツ設定
  for (const content of contents) {
    // data属性のlistとinfoを取得する
    const contentGroup = content.getAttribute('data-tab-group');
    const contentName = content.getAttribute('data-tab-name');

    // data属性のlistとinfoは設定必須なのでチェック
    if (!contentGroup || !contentName) {
      continue;
    }

    // Tabのコンテンツを設定
    tabContents.push({
      element: content,
      group: contentGroup,
      name: contentName,
    });
  }
}

処理としては、generateTab()がコールされた際に、渡されたwrapper要素(通常はdocumentを渡す想定)内で、タブのトリガー「.js-tab-trigger」とコンテンツ「.js-tab-content」要素を取得し、そこから付随するdata属性「data-tab-group」「data-tab-name」を取得します。
これらを管理するため、「tabTriggers」「tabContents」へ格納します。

そして、クリック時のイベントハンドラーでコールしている「changeTab()」を作成します。
▼tab.js

const changeTab = (event, group, name) => {
  console.log('changeTab');
  event.preventDefault();

  // タブの選択中トリガーを切り替える
  for (const trigger of tabTriggers) {
    // 同一group以外は無視する
    if (trigger.group !== group) {
      continue;
    }

    // 選択中のタブを切替
    if (trigger.name === name) {
      trigger.element.classList.add('is-current');
    } else {
      trigger.element.classList.remove('is-current');
    }
  }

  // タブの選択中コンテンツを切り替える
  for (const content of tabContents) {
    // 同一group以外は無視する
    if (content.group !== group) {
      continue;
    }

    // 選択中のタブを切替
    if (content.name === name) {
      // 表示
      content.element.style.display = '';
      content.element.classList.add('is-current');
    } else {
      // 非表示
      content.element.style.display = 'none';
      content.element.classList.remove('is-current');
    }
  }
};

トリガーがクリックされた際にコールされるため、引数で渡されたgroup, nameをもとに、管理しているタブのトリガー・コンテンツの要素を格納している「tabTriggers」「tabContents」をすべてループして、表示の切替を行います。

JavaScriptの呼び出し元となる、index.jsでtab.jsのgenerateTabメソッドを呼び出しています。
▼index.js

import { generateTab } from './modules/tab';

window.addEventListener('DOMContentLoaded', (event) => {
  generateTab(document);
});

まとめ

完成したソースコード
https://github.com/it-web-life/javascript_es6_tab
サンプルの動作を確認できるサイト
https://it-web-life.github.io/javascript_es6_tab/

今回のコードをローカル環境で、動かす際にはファイルをダウンロード後に「$ yarn install」してから、以下のコマンドでscript操作可能です。

  • $ yarn dev : webpack-dev-serverが立ち上がって、localhost:8081で動作確認ができます。
  • $ yarn build : デプロイ用ファイルを出力します。distディレクトリに、bandle.js、style.css が作成されます。
  • $ yarn build:dev : 通常使いませんが、デバッグ用のファイルを出力します。distディレクトリ内に開発用に使うコードを出力します。

以前、jQueryで実装したタブ機能を、内容を少し変えてES6で今回は実装してみました。

ご参考になれば幸いです。

※当サイトでは一部のリンクについてアフィリエイトプログラムを利用して商品を紹介しています