JavaScript【タブ機能】のプログラムの書き方

2021年2月27日JavaScript

JavaScriptでタブ機能のプログラムの書き方を紹介します。

タブ機能のプログラムの書き方

HTML

<div class="about">
  <div class="about__tab-container">
    <button
      class="btn about__tab about__tab--1 about__tab--active"
      data-tab="1"
    >
      <span>01</span>tab1
    </button>
    <button class="btn about__tab about__tab--2" data-tab="2">
      <span>02</span>tab2
    </button>
    <button class="btn about__tab about__tab--3" data-tab="3">
      <span>03</span>tab3
    </button>
  </div>
  <div class="about__content about__content--1 about__content--active">
    <h5 class="about__header">テキストtab1のタイトル</h5>
    <p>テキストtab1</p>
  </div>
  <div class="about__content about__content--2">
    <h5 class="about__header">テキストtab2のタイトル</h5>
    <p>テキストtab2</p>
  </div>
  <div class="about__content about__content--3">
    <h5 class="about__header">テキストtab3のタイトル</h5>
    <p>テキストtab3</p>
  </div>
</div>

CSS


.btn {
  display: inline-block;
  // background-color: hsl(20, 60%, 60%);
  font-size: 1rem;
  font-family: inherit;
  font-weight: 500;
  border: none;
  padding: 0.25rem 1.5rem;
  border-radius: 10rem;
  cursor: pointer;
  transition: all 0.3s;
}

.about {
  max-width: 100rem;
  margin: 12rem auto 0 auto;

  background-color: #fff;
}

.about__tab-container {
  display: flex;
  justify-content: center;
}

.about__tab {
  margin-right: 2.5rem;
  transform: translateY(-50%);
}

.about__tab span {
  margin-right: 1rem;
  font-weight: 600;
  display: inline-block;
}

.about__tab--1 {
  background-color: red;
}

.about__tab--1:hover {
  background-color: orangered;
}
.about__tab--2 {
  background-color: green;
}

.about__tab--2:hover {
  background-color: greenyellow;
}

.about__tab--3 {
  background-color: blue;
  margin: 0;
}

.about__tab--3:hover {
  background-color: skyblue;
}

.about__tab--active {
  transform: translateY(-68%);
}

.about__content {
  display: none;
  font-size: 1.7rem;
  padding: 2.5rem 7rem 6.5rem 7rem;
}

.about__content--active {
  display: grid;
  grid-template-rows: 7rem 1fr;
  column-gap: 3rem;
  row-gap: 0.5rem;
}

.about__header {
  font-size: 2.25rem;
  font-weight: 500;
  align-self: center;
}


JavaScript

まずは、下のように定義します。



//要素を定義
  const tabs = document.querySelectorAll('.about__tab');
  const tabsContainer = document.querySelector('.about__tab-container');
  const tabsContent = document.querySelectorAll('.about__content');


//イベントの設定
  tabsContainer.addEventListener('click', function (e) {
  const clicked = e.target.closest('.about__tab');


//Guard clause(Guard Clause ガード節,ガード条件)
  if (!clicked) return; 


// active classesを取り除く
  //activeついているものをはずす
  tabs.forEach(t => t.classList.remove('about__tab--active'));
  //コンテンツ部分も同じくactiveをはずす
  tabsContent.forEach(c => c.classList.remove('about__content--active'));


// activeをつける
  clicked.classList.add('about__tab--active');

  //data属性を使う。
  document.querySelector(`.about__content--${clicked.dataset.tab}`)
    .classList.add('about__content--active');
});

要素の取得にはclosest()を使う

aタグだけならe.targetでいいのですが、spanタグなど他の要素がある場合、そこをクリックしても反応しません。
そのため、ボタンをクリックしたら、たとえspanタグの上でも反応するようにしたいところです。 そこでclosest()を使います。
about__tabクラスのついたbutton自体がクリックされたらbuttonを選択し、buttonの中のspanタグでもaタグでもクリックされたらその直近の親のbuttonが選択できるようになります。

なお、forEachを次のように使って取得することもできますが、効率を考えると上のclosest()メソッドを使う方がいいです。



tabs.forEach(t => t.addEventListener('click', (e) => {});

Guard clause(Guard Clause ガード節,ガード条件)

closest()は該当がないとnullを返します。
そのため、何もしないとnullでエラーになるので、Guard Clauseを設定します。
clickedがfalseなら、即座に終了するという条件です。
nullはfalsyな値のため、trueとなり、その後のコードが実行されません。

なお、伝統的なやり方だと、次のような方法になります。



  if(clicked) {
  //activeついているものをはずす
    tabs.forEach(t => t.classList.remove('about__tab--active'));
    activeをつける
  clicked.classList.add('about__tab--active');
  }

ネストが深くなるので上の方がモダンなやり方のほうがいいでしょう。

active classesをすべてから取り除いて、activeにしたいものにつける

タブの部分も、コンテンツの部分も基本的に同じで、まずはforEachでそれぞれactiveがついているものを取り除きます。
それから、クリックされた要素にacitveを追加します。

コンテンツをタブと連動されるにはdata属性を利用する

コンテンツをタブと連動されるにはdata属性とテンプレートリテラルを利用します。