【JavaScript】アコーディオンメニューの作り方【クリックで開閉】

JavaScript
記事内に商品プロモーションを含む場合があります。

生のJavaScript(Vanilla JS)でアコーディオンメニューが作りたい。

この記事では、jQueryを一切使わず、生のJavaScript(Vanilla JS)でアコーディオンメニューを作る方法について解説します。

なお、今回ご紹介するのは、下のように各メニューをクリックするとスライドしながら開閉するアコーディオンになります。

See the Pen accordion_js by たけち (@kwxteqls-the-scripter) on CodePen.

スポンサーリンク

JavaScriptでアコーディオンメニューを作る方法

では早速、JavaScriptを使ってアコーディオンメニューを作っていきましょう。

HTMLとCSSで見た目を作る

まずは、HTMLとCSSでアコーディオンメニューの見た目を作ります。

<div class="accordions">
  <div class="accordion">
    <div class="accordion__menu">
      <button class="accordion__button">Menu1</button>
      <div class="accordion__body">
        <p class="accordion__text">
          テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
        </p>
      </div>
    </div>
    <div class="accordion__menu">
      <button class="accordion__button">Menu2</button>
      <div class="accordion__body">
        <p class="accordion__text">
          テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
          テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
          テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト
        </p>
      </div>
    </div>
    <div class="accordion__menu">
      <button class="accordion__button">Menu3</button>
      <div class="accordion__body">
        <p class="accordion__text">
          テキストテキストテキストテキストテキスト
        </p>
      </div>
    </div>
  </div>
</div>
.accordions {
  max-width: 600px;
  width: 100%;
  margin-right: auto;
  margin-left: auto;
}
.accordion {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.accordion__menu {
  border: 1px solid #223a70;
}
.accordion__button {
  appearance: none;
  text-align: inherit;
  width: 100%;
  background-color: #223a70;
  color: #fff;
  padding: 20px;
}
.accordion__body {
  height: 0;
  overflow: hidden;
  transition: all 0.5s;
}
.accordion__text {
  padding: 20px;
}

ポイントは、テキスト部分が「accordion__body」と「accordion__text」の二重構造になっている点で、親要素である「accordion__body」のheightを0にすることで非表示にしています。

なぜdisplayやopacityで非表示にしないかと言うと、スライドで表示・非表示を切り替えたいからです。

jQueryには「slideToggle」という便利な関数がありますが、JavaScriptにはありません。そのため、heightを0にして非表示にし、JavaScriptで高さを付与してスライドを表現するのです。

なお、heightを0にするだけだと中身のテキストが溢れるので、親要素にoverflowをhiddenを指定しておきましょう。

あと、アニメーション用にtransitionも一緒に指定しておきます。

.accordion__body {
  height: 0;
  overflow: hidden;
  transition: all 0.5s;
}

JavaScriptでアニメーションを付ける

次に、JavaScriptでアコーディオンメニューに動きをつけましょう。

ボタンのクリックイベントを作成

まずは、ボタンのクリックイベントから作ります。

ボタンは複数あるので、buttonsという変数に全てのボタン要素(accordion__button)を格納し、forEachで回します。

ボタンクリック時のイベントオブジェクトを取得するため、引数に「e」を渡しておきます。

const buttons = document.querySelectorAll('.accordion__button');

buttons.forEach((button) => {
  button.addEventListener('click', (e) => {
    // 処理内容
  });
});

「開く」動きを作る

イベントリスナー内で、クリックされたボタンに紐づくテキストの親要素(accordion__body)と子要素(accordion__text)を取得します。

親要素はボタンの兄弟関係にある1つ後の要素なので「nextElementSibling」で、子要素は「children」で取得可能です。

const buttons = document.querySelectorAll('.accordion__button');

buttons.forEach((button) => {
  button.addEventListener('click', (e) => {
  //テキストの親要素を取得
    const body = e.currentTarget.nextElementSibling;
  //テキストの子要素を取得
    const text = body.children[0]; 
  });
});

子要素の高さを「offsetHeight」で取得し、あらかじめ高さ(height)を0にしておいた親要素に代入します。

これで、アコーディオンメニューの「開く」の動きが完成します。

buttons.forEach((button) => {

  button.addEventListener('click', (e) => {
    const body = e.currentTarget.nextElementSibling;
    const text = body.children[0];

    //子要素の高さを取得して親要素に代入
  const textHeight = text.offsetHeight;
  body.style.height = textHeight + 'px';
  });
});

「閉じる」動きを作る

まずは、アコーディオンメニューの開閉を判定するために、次のように記述します。

これで、ボタンをクリックする度に親要素(accordion__menu)にopenクラスが付け外しされ、メニューの開閉状況を判定することができます。

buttons.forEach((button) => {

  button.addEventListener('click', (e) => {
    const body = e.currentTarget.nextElementSibling;
    const text = body.children[0];
  const textHeight = text.offsetHeight;
  body.style.height = textHeight + 'px';

    // ボタンの親要素にopenクラスを付け外しする
    const menu = e.currentTarget.parentNode;
    menu.classList.toggle('open');
  });
});

最後に、if文を使って条件分岐を行い、「openクラスが付いていたらheightを付与する」「openクラスが付いていなければheightを0にする」としましょう。

const buttons = document.querySelectorAll('.accordion__button');

buttons.forEach((button) => {
  button.addEventListener('click', (e) => {
    const body = e.currentTarget.nextElementSibling;
    const text = body.children[0];
    const textHeight = text.offsetHeight;
    const menu = e.currentTarget.parentNode;
    menu.classList.toggle('open');

    //条件分岐で開閉を切り替える
    if(menu.classList.contains('open')) {
      body.style.height = textHeight + 'px';
    } else {
      body.style.height = 0;
    }
  });
});

下のように動けば完成です。

See the Pen accordion_js by たけち (@kwxteqls-the-scripter) on CodePen.