アコーディオンメニュー(ドロップダウンメニュー)をJavaScriptで実装してみる(パターン1)(備忘録)

今回は、アコーディオンメニュー(ドロップダウンメニュー)を実装してみる。

アコーディオンメニュー(ドロップダウンメニュー)というのは、見出しをクリックした時に、その見出しに対する中身を表示させたい場合に使うことが多い。

例えば、ページの量が縦に長くなってしまう場合、読み手にとってはあまりに長いページだと判断され、読む気が無くなったりすることがある。

そんな時に、縦に長くなってしまう要素を最初から隠した状態でコンパクトに見せることで、読み手がページを訪れた時にそこまで長いページではないなと判断して読み進めてもらえる可能性が上がることがあるのだ。

しかし、多用するのはあまり推奨されていないようだ。なぜなら、読み手にとって展開するアクションが発生し、これが面倒だと感じられる時があるからである。

使うときは、読み手を意識して考えていくのがいいだろう。

今回の実装パターンは、最初のページ読み込み時は全て閉じた状態にしている。

また、各タイトルをクリックしたら中身が展開され、展開されているタイトルをクリックしたら閉じる仕様になっている。

それでは解説に入っていく。

htmlの記述について

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>アコーディオンメニュー</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <section>
      <div class="ly_inner" id="menu1">
        <h2>menu1</h2>
        <!-- アコーディオン -->
        <div class="bl_accordion">
          <h3 class="bl_accordionTitle">タイトル1</h3>
          <div class="bl_accordionContent">
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
            <p>タイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入りますタイトル1の内容が入ります</p>
          </div>
          <h3 class="bl_accordionTitle">タイトル2</h3>
          <div class="bl_accordionContent">
            <p>タイトル2の内容が入ります</p>
            <p><img src="img_sample01.jpg" alt="サンプル画像"></p>
          </div>
          <h3 class="bl_accordionTitle">タイトル3</h3>
          <div class="bl_accordionContent">
            <p>タイトル3の内容が入ります</p>
            <ul class="md_markList">
              <li>タイトル3の内容が入ります</li>
              <li>タイトル3の内容が入りますタイトル3の内容が入りますタイトル3の内容が入りますタイトル3の内容が入ります</li>
              <li>タイトル3の内容が入ります</li>
              <li>タイトル3の内容が入ります</li>
            </ul>
          </div>
        </div>
      </div>
    </section>
  </main>
  <script src="style.js"></script>
</body>
</html>

アコーディオンメニュー全体を囲うタグを作成

今回は、で全体を囲っている。

アコーディオンメニューの「タイトル」と「中身」のタグを作成

今回は下記で作成している。

<h3 class="bl_accordionTitle">タイトル1</h3>
<div class="bl_accordionContent”>・・・</div>

ちなみに必ず上記のようなタグで対応する必要はない。

例えば、全体をで囲って、タイトルを、中身をで対応するような時もある。

また、全体をで囲って、タイトルを、中身をにするような時もある。

これらはhtmlのタグの役割を考えて、その時の状況にあったものを使っていくと良いかと思う。

アコーディオンメニューの中身には自由なタグが入れられる

div class=”bl_accordionContent”の・・・の部分には、pタグやulタグ、imgタグ、tableタグなど、いろんなものが入る可能性が想定されるので、デモでは適当に選んで入れてみた。足りない部分はCSSで調整すればいいかと思う。

cssの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
img {
  width: 100%;
}

/* ==========================
  アコーディオンメニュー
========================== */
.bl_accordion {
  background-color: #fff;
}
.bl_accordionTitle {
  font-size: 28px;
  font-weight: bold;
  background-color: #d6a194;
  padding: 20px 90px 20px 40px;
  transition: 0.3s;
  cursor: pointer;
  position: relative;
}
.bl_accordionTitle::before {
  content: "";
  width: 40px;
  height: 3px;
  background-color: #000;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  right: 40px;
  transition: 0.3s;
}
.bl_accordionTitle::after {
  content: "";
  width: 3px;
  height: 40px;
  background-color: #000;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  right: 59px;
  transition: 0.3s;
}
.bl_accordionTitle.active::after {
  opacity: 0;
}
.bl_accordionTitle:hover::after,
.bl_accordionTitle:hover::before,
.bl_accordionTitle.active::after,
.bl_accordionTitle.active::before {
  background-color: #fff;
}
.bl_accordionTitle:hover,
.bl_accordionTitle.active {
  background-color: #6d2412;
  color: #fff;
}
.bl_accordionContent {
  padding: 0 40px;
  opacity: 0;
  height: 0;
  line-height: 0;
  transition: 0.3s;
}
.bl_accordionContent > * + * {
  margin-top: 0;
}
.bl_accordionContent img {
  height: 0;
}
.bl_accordionContent.open {
  padding: 40px;
  opacity: 1;
  line-height: normal;
  height: auto;
}
.bl_accordionContent.open > * + * {
  margin-top: 10px;
}
.bl_accordionContent.open img {
  height: auto;
}
/* ==========================
  タブ以外のコンテンツの中身
========================== */
.ly_inner {
  width: 100%;
  max-width: 1080px;
  margin: 100px auto;
  padding: 30px;
  background-color: #ccc;
}
.ly_inner h2 {
  font-size: 250%;
  font-weight: bold;
  margin-bottom: 30px;
}
.md_textblock > * + * {
  margin-top: 10px;
}
.md_markList {
  list-style-type: disc;
  margin-left: 1.4rem;
}

/* ====================================
  ここからPC幅
==================================== */
@media screen and (min-width: 768px) {
  .hp_displaySP {
    display: none !important;
  }
}

/* ====================================
  ここからスマホ幅
==================================== */
@media screen and (max-width: 767px) {
  .hp_displayPC {
    display: none !important;
  }
}

アコーディオンメニューのタイトル部分の展開される前の見た目について

クリックやホバーをする前の表示段階では、薄い色に背景を設定して、さらに➕マークで展開できることが視覚的に分かるように対応している。

背景色、文字色、余白、フォントサイズなどはそこまで難しくないため、解説は省略する。

ポイントは下記部分だ。

.bl_accordionTitle {
  transition: 0.3s;
  cursor: pointer;
  position: relative;
}

transitionをつけているのは、ホバーやクリックをした時に0.3秒かけてアニメーションされるように設定している。

cursorについては、今回はaタグではなく、h3タグで設定しているためpointerにすることで、要素の上にポインターを乗せたときに指マークに変わるよう対応している。

positionを設定しているのは、タイトル右側にある➕マークを設定するために、ralativeを充てている。

➕マーク部分は下記で対応している。

.bl_accordionTitle::before {
  content: "";
  width: 40px;
  height: 3px;
  background-color: #000;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  right: 40px;
  transition: 0.3s;
}
.bl_accordionTitle::after {
  content: "";
  width: 3px;
  height: 40px;
  background-color: #000;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  right: 59px;
  transition: 0.3s;
}

ポイントは、背景色に対して高さと幅を設定して表示させているところだ。表示させたい位置については、position、top、bottom、left、right、transformなどを使って調整している。

よく使われるのは、上下左右真ん中に配置したい時などに対応することが多い。これは調べれば結構出てくるので解説は省略する。

アコーディオンメニューの中身部分の展開される前の見た目について

中身の該当部分は下記ソースになる。

.bl_accordionContent {
  padding: 0 40px;
  opacity: 0;
  height: 0;
  line-height: 0;
  transition: 0.3s;
}
.bl_accordionContent > * + * {
  margin-top: 0;
}
.bl_accordionContent img {
  height: 0;
}

中身部分については、クリックされる前は隠れた状態にしなくてはならないため、opacityで透明にする。

透明にしただけだと、中身の要素が見えない状態で残っていることになるので、height、line-height、margin-topを0にすることで、アコーディオンメニュー展開前は中身の高さが0になるようにしている。

これでtransitionを設定することでアニメーションが効くようになる。

もし、アニメーションさせたくない場合は、display:none;とdisplay:block;で表示・非表示の対応をするといいかと思う。この方がパッと一瞬で切り替わるので、早く中身をみたい人にとってはあっているのかもしれない。

アコーディオンメニューのタイトル部分の展開された後やホバーした時の見た目について

これはそこまで難しくはない。javascript側でクリックした時にactiveというクラスがつくようになっているため、activeのクラスがついた時に、背景色や文字色を変更すればいいだけだ。また、hoverの時もactiveの時と同様に対応すればOKだ。

続いて、クリックされた後の➖マークについてだ。

.bl_accordionTitle.active::after {
  opacity: 0;
}

これはtranformをうまく活用して、アコーディオンメニューを展開した時に、➕の縦棒がふわっと消えて、最終的に➖の表示にするための実装にしている。

今回はこういった動きではあるが、アコーディオンメニューの➕マークのようなアイコンは、本当に様々な種類や動きをするものがあるため、日々色んなページを見て、どういったものがあるのか、また、どうやって実装されているのかなどをチェックしておくと幅が拡がるかと思う。

アコーディオンメニューの中身部分の展開された後の見た目について

.bl_accordionContent.open {
  padding: 40px;
  opacity: 1;
  line-height: normal;
  height: auto;
}
.bl_accordionContent.open > * + * {
  margin-top: 10px;
}
.bl_accordionContent.open img {
  height: auto;
}

アコーディオンメニューが展開される前の中身は、高さをなくす設定をしていたため、openのクラスが付与された時に、通常の高さが保持されるように設定を戻している。

これでCSSの準備の実装は完了となる。

JavaScriptの記述について

'use strict';

// アコーディオン
document.addEventListener('DOMContentLoaded', function(){

  const accordionTitle = document.querySelectorAll('.bl_accordionTitle');

  for (let i = 0; i < accordionTitle.length; i++){
    accordionTitle[i].addEventListener('click', function() {
      this.classList.toggle('active');
      this.nextElementSibling.classList.toggle('open');
    });
  }
});

bl_accordionTitleのクラスに対して、繰り返しfor文を用いて実装している。

アコーディオンメニューのタイトルは増えたり減ったりすることもあるので、変数iを使って対応。

あとはアコーディオンメニューのタイトル(bl_accordionTitle)をクリックした時に、activeのクラスがついたり消えたりするようtoggleで設定。

また、同時にbl_accordionTitleの次のタグ(アコーディオンメニューの中身)に対してopenのクラスが付与されるよう、nextElementSiblingを用いて設定している。

以上で終わりだが、アコーディオンメニューのタイトル部分をaタグで対応している場合は、リンクが遷移しないように対応しなければならないので注意しなければならない。

これについては、過去の記事でも触れているため省略したいと思う。

2 返信

コメントはクローズされています。