CSSとjQueryでアコーディオンメニューを実装する方法を解説します。

結論から書くと、いま新しく作るならHTMLの <details><summary> だけで作れます。JavaScriptもjQueryも要りません。キーボード操作とスクリーンリーダーへの対応も最初から備わっています。

既存サイトにjQueryが入っている場合や、開閉の挙動を細かく制御したい場合は、この記事のjQuery版が使えます。両方の作り方を載せるので、状況に合うほうを選んでください。

アコーディオンメニューの作成例

今回作成するアコーディオンメニューです。

See the Pen
アコーディオンメニュー
by りん@エンジニア (@rinblog0408)
on CodePen.

 

アコーディオンの基本構造を作成する

HTMLの準備

まず、アコーディオンを実装するための基本的なHTML構造を作成します。以下は、3つのセクションからなるアコーディオンの例です。各セクションには見出しとコンテンツが含まれます。

<div class="accordion">
  <div class="accordion-section">
    <div class="accordion-header">セクション1の見出し</div>
    <div class="accordion-content">
      セクション1のコンテンツがここに入ります。
    </div>
  </div>
  <div class="accordion-section">
    <div class="accordion-header">セクション2の見出し</div>
    <div class="accordion-content">
      セクション2のコンテンツがここに入ります。
    </div>
  </div>
  <div class="accordion-section">
    <div class="accordion-header">セクション3の見出し</div>
    <div class="accordion-content">
      セクション3のコンテンツがここに入ります。
    </div>
  </div>
</div>

 

CSSを使ってアコーディオンのスタイリングを行う

CSSの適用

アコーディオンのスタイリングを行います。以下のCSSコードは、基本的なスタイルを設定するもので、デザインに合わせてカスタマイズできます。

/* アコーディオンセクションのスタイル */
.accordion-section {
  border: 1px solid #ddd;
  margin-bottom: 10px;
}

/* アコーディオンヘッダーのスタイル */
.accordion-header {
  background-color: #f1f1f1;
  padding: 10px;
  cursor: pointer;
}

/* アコーディオンコンテンツのスタイル */
.accordion-content {
  display: none;
  padding: 10px;
}

/* アクティブなアコーディオンのスタイル */
.active {
  display: block;
}

 

jQueryを使ってアコーディオンの動作を追加する

jQueryの導入

jQueryを使用して、アコーディオンを動的に操作します。以下のコードをHTMLファイル内に挿入します。

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
  $(document).ready(function() {
    $('.accordion-header').on('click', function() {
      const $header = $(this);
      const $content = $header.next('.accordion-content');
      const isOpen = $header.hasClass('active');

      // いったんすべて閉じる
      $('.accordion-content').slideUp();
      $('.accordion-header').removeClass('active').attr('aria-expanded', 'false');

      // 閉じていたものだけを開く(開いていたものはそのまま閉じる)
      if (!isOpen) {
        $header.addClass('active').attr('aria-expanded', 'true');
        $content.slideDown();
      }
    });
  });
</script>

 

開いている項目をもう一度押しても閉じないとき

結論:「すべて閉じる」処理と slideToggle() を続けて書くと、開いている項目を押したときに閉じてから開き直してしまいます。

slideUp() はアニメーションなので、実行した直後はまだ閉じ切っていません。その状態で slideToggle() を呼ぶと「今は開いている」と判定され、もう一度開いてしまうことがあります。

上のコードでは、クリック前の状態を isOpen に控えてからすべて閉じ、閉じていた場合だけ開くようにしています。これで、開いている項目を押せば確実に閉じます。

キーボードで操作できるようにする

結論:<div> のままではキーボードで操作できません。見出し部分を <button> にしてください。

<div class="accordion-header"> はクリックには反応しますが、Tabキーでフォーカスできず、Enterキーでも開きません。マウスが使えない人には操作できない状態になります。

<div class="accordion-section">
  <button type="button" class="accordion-header" aria-expanded="false" aria-controls="panel1">
    セクション1の見出し
  </button>
  <div class="accordion-content" id="panel1">
    セクション1のコンテンツがここに入ります。
  </div>
</div>

<button> にするとブラウザ標準の見た目が付くので、CSSで打ち消します。

.accordion-header {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  font: inherit;
  background-color: #f1f1f1;
  padding: 10px;
  cursor: pointer;
}

aria-expanded は、開いているかどうかをスクリーンリーダーに伝える属性です。上のJavaScriptで開閉に合わせて切り替えています。

JavaScriptを使わずにdetailsで作る

結論:HTMLの <details><summary> を使えば、CSSもJavaScriptも書かずにアコーディオンになります。キーボード操作とスクリーンリーダー対応も最初から入っています。

<details name="faq">
  <summary>セクション1の見出し</summary>
  <p>セクション1のコンテンツがここに入ります。</p>
</details>

<details name="faq">
  <summary>セクション2の見出し</summary>
  <p>セクション2のコンテンツがここに入ります。</p>
</details>

ポイントは name 属性です。同じ name を付けた <details> は排他的になり、1つ開くと他が自動で閉じます。この記事のjQuery版と同じ動きが、属性1つで実現できます。

見た目を整えるCSSはこれだけです。

details {
  border: 1px solid #ddd;
  margin-bottom: 10px;
}

summary {
  background-color: #f1f1f1;
  padding: 10px;
  cursor: pointer;
}

details > *:not(summary) {
  padding: 0 10px 10px;
}

detailsに開閉アニメーションを付ける

結論:::details-content 擬似要素で中身の高さを遷移させると、スライドしながら開きます。

/* height: auto へのアニメーションを許可する */
:root {
  interpolate-size: allow-keywords;
}

details::details-content {
  height: 0;
  overflow: hidden;
  transition: height 0.3s, content-visibility 0.3s;
  transition-behavior: allow-discrete;
}

details[open]::details-content {
  height: auto;
}

interpolate-size: allow-keywords は、height: 0 から height: auto へのアニメーションを可能にする指定です。これが無いと、高さが分からないため一瞬で開きます。

transition-behavior: allow-discrete は、閉じるアニメーションの最中も中身を表示したままにするための指定です。これが無いと、閉じ始めた瞬間に中身が消えます。

対応状況は Chromium 系のブラウザのみです(2026年9月時点)。ただし未対応のブラウザでも、アニメーションが付かないだけでアコーディオンとしては正しく動きます。そのまま書いて問題ありません。

どちらの作り方を選ぶか

結論:新しく作るなら <details>、既存サイトに手を入れるならjQuery版です。

▼2つの作り方の比較

details / summary CSS + jQuery
JavaScript 不要 必要
キーボード操作 最初から対応 buttonにする必要がある
スクリーンリーダー 最初から対応 aria属性を自分で付ける
1つだけ開く動作 name 属性1つ JavaScriptで制御
開閉アニメーション Chromium系のみ 全ブラウザで動く
細かい挙動の制御 しづらい 自由にできる

アニメーションが必須要件でなければ <details> を選んでください。書く量が減るうえに、アクセシビリティの対応漏れが起きません。開閉に合わせて他の処理を走らせたい、といった要求が出てきた段階でJavaScriptを足せば十分です。

要素の表示・非表示の切り替え方そのものを整理したい場合は、jQueryでドロップダウンメニューを実装する5つの方法もあわせてどうぞ。同じ考え方でメニューの開閉を作れます。

要素を辿る処理でつまずいたら、jQueryで親要素を取得する方法.closest() の使い方をまとめています。

▼HTML/CSS/JavaScriptの学習本を探してる方はこちらの記事をどうぞ

2023年版 Webコーディングにオススメの学習本【HTML/CSS/JavaScript】
Webコーディングにオススメの学習本4冊【HTML/CSS/JavaScript】WebコーディングのおすすめHTML/CSS/JavaScript学習本4冊を、著者・出版社・刊行年まで明記して紹介。HTML Living Standard準拠かどうかを基準に選ぶ理由と、floatだけで段組みを説明する古い本を避ける判断方法もまとめました。...

 

ABOUT ME
りん
このブログでは、Web開発やプログラミングに関する情報を中心に、私が日々感じたことや学んだことをシェアしています。技術と生活の両方を楽しめるブログを目指して、日常で触れた出来事や本、グルメの話題も取り入れています。気軽に覗いて、少しでも役立つ情報や楽しいひとときを見つけてもらえたら嬉しいです。