皆さん、JavaScriptを使ったアコーディオンの実装について悩んだことはありませんか?
「アコーディオンって簡単そうに見えるけど、実際どうやって作るの?」
「UIのデザインはできたけど、動きをつけるのが難しい…」
実は多くの方がこのように感じています。
アコーディオンは、限られたスペースを有効活用できるUIパターンで、特にスマホ対応のサイトでは必須とも言える要素です。
この記事では、初心者でも理解できるよう、JavaScriptを使ったアコーディオン実装の基本から応用まで丁寧に解説します。
読み進めることで、あなたも自分のサイトに使いやすいアコーディオンを実装できるようになります!

- 現役のフルスタックエンジニアとして活躍中
- 開発チームリーダーとして複数プロジェクトをリード
- 副業プログラミングスクール講師として数百名以上を指導してきた教育のプロ
- プログラミングスクールのカリキュラム執筆経験あり
この記事を読むとわかること
- アコーディオンの基本的な仕組みと実務での活用シーン
- HTMLとCSSでアコーディオンの基本構造を作る方法
- JavaScriptを使ったアコーディオンの開閉処理のコーディング方法
- スマホ対応やアクセシビリティなど実務で役立つポイント
アコーディオンとは何か
アコーディオンの概要
アコーディオンというのは、見出しをクリックすると中身が開いたり閉じたりする仕組みのことです。
楽器のアコーディオンが伸び縮みするのに似ていることからこの名前がついています。
例えば、FAQページでよく見かける「質問をクリックすると回答が表示される」というのがアコーディオンの代表的な使い方です。
アコーディオンが必要とされる理由
UIの見やすさを向上させる
アコーディオンを使う最大のメリットは情報の整理ができる点です。
FAQページなどで全ての質問と回答をいっぺんに表示してしまうと、ページが長くなりすぎて目的の情報を探しにくくなります。
でも、アコーディオンを使えば、最初は質問だけを表示して、ユーザーが知りたい項目だけを展開できるようになります。
こうすることで、ユーザーは必要な情報だけをピンポイントで確認できるんです。
スペースの有効活用
もうひとつの大きなメリットは、限られたスペースを有効活用できる点です。
特にスマートフォンのような画面の小さいデバイスでは、情報量が多いとユーザーが迷子になりやすいんですよね。
アコーディオンを使えば、最初は必要最低限の情報だけを表示しておいて、詳細は必要に応じて展開することで、スマホ画面でも快適に閲覧できるUIを実現できます。
実務での利用シーン
アコーディオンが活躍する場面はたくさんあります。代表的なものをいくつか紹介しますね。
- FAQページ(質問をクリックすると回答が開く)
- サイドメニュー(カテゴリをクリックするとサブメニューが開く)
- 商品詳細ページ(「仕様を見る」などのボタンをクリックするとスペックが表示される)
- プロフィールページ(経歴や活動履歴を折りたたむ)
どれも「すべての情報をいっぺんに見せるよりも、まとめておいた方が使いやすい」という場面ですね。
アコーディオンの基本構造
HTML要素の考え方
アコーディオンは基本的に「見出し」と「内容」という2つの部分で構成されています。
HTMLで考えると、以下のような構造になります。
<div class="accordion">
  <div class="accordion-item">
    <button class="accordion-header">見出し1</button>
    <div class="accordion-content">
      <p>ここに詳細内容が入ります</p>
    </div>
  </div>
  
  <div class="accordion-item">
    <button class="accordion-header">見出し2</button>
    <div class="accordion-content">
      <p>ここにも詳細内容が入ります</p>
    </div>
  </div>
</div>この構造では、
- accordion– アコーディオン全体を囲む親要素
- accordion-item– 個々のアコーディオン項目
- accordion-header– クリックして開閉する見出し部分
- accordion-content– 開閉する中身の部分
という役割分担になっています。
見出し部分にはbutton要素を使うことをおすすめします。これはアクセシビリティの観点から重要で、キーボード操作や支援技術に対応しやすくなります。
CSSの設定例
次に、CSSでアコーディオンの表示・非表示を制御します。基本のCSSはこんな感じです。
/* アコーディオン全体のスタイル */
.accordion {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
}
/* 各アイテムの余白など */
.accordion-item {
  margin-bottom: 10px;
  border: 1px solid #ddd;
}
/* 見出し部分のスタイル */
.accordion-header {
  width: 100%;
  padding: 15px;
  background-color: #f5f5f5;
  border: none;
  text-align: left;
  cursor: pointer;
  font-weight: bold;
}
/* コンテンツ部分のスタイル(初期状態では閉じている) */
.accordion-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}
/* アクティブ状態のコンテンツ部分 */
.accordion-item.active .accordion-content {
  max-height: 300px; /* コンテンツの最大高さ(適宜調整) */
  padding: 15px;
}ここでポイントになるのはアコーディオンの開閉方法です。
このコードでは、max-heightプロパティを使っています。
- 閉じている状態:max-height: 0に設定
- 開いている状態:max-height: 300pxなどの値に設定
そしてtransitionプロパティで、開閉がアニメーションするようにしています。
display: noneを使って開閉する方法もありますが、その場合はアニメーションが効かなくなるので注意が必要です。
JavaScriptでの基本的な動作管理
最後に、JavaScriptでアコーディオンの開閉を制御します。
基本的には「見出しがクリックされたら、親要素にactiveクラスを追加または削除する」という処理を行います。
document.addEventListener("DOMContentLoaded", () => {
  // すべてのアコーディオンヘッダーを取得
  const accordionHeaders = document.querySelectorAll(".accordion-header");
  // 各ヘッダーにクリックイベントを設定
  accordionHeaders.forEach((header) => {
    header.addEventListener("click", () => {
      // クリックされたヘッダーの親要素(accordion-item)を取得
      const accordionItem = header.parentElement;
      
      // activeクラスをトグル(あれば削除、なければ追加)
      accordionItem.classList.toggle("active");
    });
  });
});このコードでは、ページの読み込みが完了してから、すべての.accordion-header要素を取得し、それぞれにクリックイベントを設定しています。
クリックされると、その親要素(.accordion-item)のactiveクラスをトグルすることで、CSSで設定した開閉スタイルが適用されるという仕組みです。
アコーディオン実装のコード例
シンプルなアコーディオンの例
ここからは、実際に使えるアコーディオンのコード例を見ていきましょう。
まずは最もシンプルなアコーディオンの実装例です。
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>シンプルなアコーディオン例</title>
  <style>
    /* アコーディオン全体のスタイル */
    .accordion {
      width: 100%;
      max-width: 600px;
      margin: 0 auto;
    }
    
    /* 各アイテムの余白など */
    .accordion-item {
      margin-bottom: 10px;
      border: 1px solid #ddd;
    }
    
    /* 見出し部分のスタイル */
    .accordion-header {
      width: 100%;
      padding: 15px;
      background-color: #f5f5f5;
      border: none;
      text-align: left;
      cursor: pointer;
      font-weight: bold;
    }
    
    /* コンテンツ部分のスタイル(初期状態では閉じている) */
    .accordion-content {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease;
    }
    
    /* アクティブ状態のコンテンツ部分 */
    .accordion-item.active .accordion-content {
      max-height: 300px; /* コンテンツの最大高さ(適宜調整) */
      padding: 15px;
    }
  </style>
</head>
<body>
  <div class="accordion">
    <div class="accordion-item">
      <button class="accordion-header">見出し1</button>
      <div class="accordion-content">
        <p>コンテンツ1の詳細な内容がここに表示されます。</p>
      </div>
    </div>
    
    <div class="accordion-item">
      <button class="accordion-header">見出し2</button>
      <div class="accordion-content">
        <p>コンテンツ2の詳細な内容がここに表示されます。</p>
      </div>
    </div>
    
    <div class="accordion-item">
      <button class="accordion-header">見出し3</button>
      <div class="accordion-content">
        <p>コンテンツ3の詳細な内容がここに表示されます。</p>
      </div>
    </div>
  </div>
  <script>
    document.addEventListener("DOMContentLoaded", () => {
      const headers = document.querySelectorAll(".accordion-header");
      headers.forEach(header => {
        header.addEventListener("click", () => {
          const item = header.parentElement;
          item.classList.toggle("active");
        });
      });
    });
  </script>
</body>
</html>このシンプルな実装例では、各アコーディオンアイテムが独立して開閉します。クリックすると、そのアイテムだけが開閉するという動作です。
一つだけ開くようにするバリエーション
「複数のアコーディオンを同時に開きたくない」「常に1つだけ開いた状態にしたい」という場合は、JavaScriptを少し修正します。
document.addEventListener("DOMContentLoaded", () => {
  const headers = document.querySelectorAll(".accordion-header");
  headers.forEach(header => {
    header.addEventListener("click", () => {
      // まず、すべてのアコーディオンアイテムからactiveクラスを削除
      const allItems = document.querySelectorAll(".accordion-item");
      allItems.forEach(item => {
        item.classList.remove("active");
      });
      // クリックされたアイテムだけにactiveクラスを追加
      const thisItem = header.parentElement;
      thisItem.classList.add("active");
    });
  });
});この実装では、アコーディオンのヘッダーがクリックされると、まずすべてのアイテムからactiveクラスを削除してから、クリックされたアイテムだけにactiveクラスを追加します。
これにより、常に1つのアイテムだけが開いた状態になります。
開閉状態を視覚的に示す
ユーザーにアコーディオンの開閉状態をわかりやすく伝えるために、アイコンを追加すると親切です。よく使われるのは「+」と「-」や、矢印の回転などです。
例えば、CSSを使って矢印アイコンを作成する方法を見てみましょう。
<button class="accordion-header">
  見出し
  <span class="accordion-icon"></span>
</button>/* アイコンの基本スタイル */
.accordion-icon {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-left: 10px;
  border-right: 2px solid #333;
  border-bottom: 2px solid #333;
  transform: rotate(45deg); /* 下向き矢印 */
  transition: transform 0.3s ease;
}
/* アクティブ状態のアイコン */
.accordion-item.active .accordion-icon {
  transform: rotate(-135deg); /* 上向き矢印に変更 */
}このようにすると、アコーディオンの状態に応じて矢印の向きが変わり、ユーザーに開閉状態を視覚的に伝えることができます。
ちなみに最近では、Font Awesome や Google Material Icons などのアイコンフォントを使うことも多いです。これらを使えば、もっと簡単にきれいなアイコンを表示できます。
実務でありがちなアコーディオンの注意点
UXとアニメーション
アコーディオンを実装するときは、ユーザー体験(UX)を意識することが大切です。
特にアニメーションの速度は重要な要素です。
- 遅すぎると: ユーザーがイライラする
- 速すぎると: 何が起きたのかわからない
経験上、transitionの時間は0.2秒〜0.5秒くらいが使いやすいと感じることが多いです。
.accordion-content {
  /* 0.3秒くらいが一般的に使いやすい */
  transition: max-height 0.3s ease;
}また、開閉時にアイコンが動いたり色が変わったりするような視覚的なフィードバックも、ユーザーには親切です。
アクセシビリティを意識する
Webサイトを作る上で、アクセシビリティ(誰もが使いやすいこと)は非常に重要です。
見た目だけでなく、スクリーンリーダーを使っている方や、キーボードだけで操作している方でも使いやすいアコーディオンにするには、以下の点に注意しましょう。
- 見出し部分にはbutton要素を使用する
- aria-expanded属性で開閉状態を伝える
- aria-controlsで関連する内容の要素を示す
アクセシビリティに配慮したコード例はこのようになります。
<div class="accordion-item">
  <button 
    class="accordion-header" 
    aria-expanded="false" 
    aria-controls="content-1"
  >
    見出し1
    <span class="accordion-icon"></span>
  </button>
  <div id="content-1" class="accordion-content">
    <p>コンテンツ1の詳細な内容がここに表示されます。</p>
  </div>
</div>JavaScriptでも、開閉状態に合わせてaria-expanded属性を更新する必要があります。
document.addEventListener("DOMContentLoaded", () => {
  const headers = document.querySelectorAll(".accordion-header");
  headers.forEach(header => {
    header.addEventListener("click", () => {
      const item = header.parentElement;
      const isActive = item.classList.toggle("active");
      
      // アクセシビリティ属性を更新
      header.setAttribute("aria-expanded", isActive);
    });
  });
});アクセシビリティに配慮することで、より多くの人があなたのサイトを問題なく利用できるようになります。特に公共性の高いサイトや、幅広いユーザー層を対象としたサイトでは必須の配慮と言えるでしょう。
内容量が大きい場合の対応
アコーディオンの中に大量のコンテンツが入る場合、いくつか注意点があります。
まず、max-heightを使ったアニメーションの場合、コンテンツの高さより大きな値を設定する必要があります。
でも、コンテンツの量が予測できない場合は、どれくらいのmax-heightを設定すればいいか悩みますよね。
そんなときは、JavaScriptを使って動的に高さを計算する方法も検討できます。
document.addEventListener("DOMContentLoaded", () => {
  const headers = document.querySelectorAll(".accordion-header");
  headers.forEach(header => {
    header.addEventListener("click", () => {
      const item = header.parentElement;
      const content = item.querySelector(".accordion-content");
      
      if (item.classList.contains("active")) {
        // 閉じるとき
        content.style.maxHeight = "0px";
        item.classList.remove("active");
      } else {
        // 開くとき(実際の高さを計算)
        content.style.maxHeight = content.scrollHeight + "px";
        item.classList.add("active");
      }
      
      // アクセシビリティ属性も更新
      header.setAttribute("aria-expanded", item.classList.contains("active"));
    });
  });
});この方法では、scrollHeightプロパティを使って要素の実際の高さを取得し、それをmax-heightに設定しています。
これなら、コンテンツがどれだけ長くても正しく表示できます。
アコーディオンの実務的な活用シーン
サイトのFAQセクション
アコーディオンの最も一般的な使用例は、やはりFAQページです。
質問をクリックすると回答が表示されるというシンプルな構造が、ユーザーにとって使いやすいUIになります。
以下は、FAQページでのアコーディオン実装例です。
<div class="faq-accordion">
  <div class="accordion-item">
    <button class="accordion-header" aria-expanded="false" aria-controls="faq-1">
      Q: プログラミングを始めるのに最適な言語は何ですか?
    </button>
    <div id="faq-1" class="accordion-content">
      <p>A: 初心者にとって最適な言語は目的によって異なります。Webサイト制作ならHTML/CSS/JavaScript、データ分析ならPython、アプリ開発ならSwiftやKotlinがおすすめです。まずは自分が作りたいものを明確にしてから言語を選ぶと良いでしょう。</p>
    </div>
  </div>
  
  <div class="accordion-item">
    <button class="accordion-header" aria-expanded="false" aria-controls="faq-2">
      Q: プログラミングスクールと独学、どちらがおすすめですか?
    </button>
    <div id="faq-2" class="accordion-content">
      <p>A: これも個人の学習スタイルによります。自分で調べながら進められる方は独学も十分可能です。一方、体系的に学びたい、挫折せずに継続したい、質問できる環境が欲しい方はスクールが適しています。予算や時間の制約も考慮して選ぶと良いでしょう。</p>
    </div>
  </div>
</div>FAQページでは、質問と回答がセットになっているので、アコーディオンUIがとても相性がいいんです。
商品詳細ページ
ECサイトでも、商品詳細ページにアコーディオンが多く活用されています。
商品の「素材」「サイズ」「使い方」「注意事項」などの情報をアコーディオンにまとめることで、ページを長くしすぎずに必要な情報を提供できます。
また、「目的別に見る」という形で、製品の用途ごとの説明をアコーディオンにすることもよくあります。
スマートフォン向けメニュー
スマートフォンサイトでは、ハンバーガーメニューの中にアコーディオンを入れることが多いです。
例えば、メインメニューをクリックするとサブメニューが開くという構造です。
スマホの画面は狭いので、このようにメニューを階層化することで、多くの選択肢をコンパクトに収めることができます。
スマホ向けUIを作る場合は、タッチしやすいサイズを意識することが大切です。44px四方くらいの大きさがあると、指で快適にタップできるようになります。
JavaScriptアコーディオン実装で押さえるポイント
クリックイベントの管理方法
アコーディオンの数が多い場合、すべての要素に個別にイベントリスナーを設定するのは効率が悪くなります。
そんなときは、イベント委譲(Event Delegation)という手法を使うと良いでしょう。
イベント委譲とは、親要素にイベントリスナーを設定し、子要素のイベントを捕捉する方法です。
document.addEventListener("DOMContentLoaded", () => {
  // アコーディオン全体の親要素にイベントリスナーを設定
  const accordionContainer = document.querySelector(".accordion");
  
  accordionContainer.addEventListener("click", (e) => {
    // クリックされた要素またはその親がヘッダーかどうかをチェック
    const header = e.target.closest(".accordion-header");
    
    if (header) {
      const item = header.parentElement;
      item.classList.toggle("active");
      header.setAttribute("aria-expanded", item.classList.contains("active"));
    }
  });
});この方法を使えば、アコーディオンの数が増えても、JavaScriptのコードを変更する必要がなくなります。
コンポーネント化の検討
大規模なプロジェクトでは、アコーディオンをコンポーネント化して再利用しやすくすることも検討してみましょう。
例えば、ReactやVue.jsのようなフレームワークを使っている場合は、アコーディオンコンポーネントを作成すると便利です。
Reactでのシンプルなアコーディオンコンポーネントの例:
import React, { useState } from 'react';
function AccordionItem({ title, children }) {
  const [isOpen, setIsOpen] = useState(false);
  
  return (
    <div className={`accordion-item ${isOpen ? 'active' : ''}`}>
      <button 
        className="accordion-header"
        onClick={() => setIsOpen(!isOpen)}
        aria-expanded={isOpen}
      >
        {title}
      </button>
      <div className="accordion-content">
        {children}
      </div>
    </div>
  );
}
// 使用例
function FAQ() {
  return (
    <div className="accordion">
      <AccordionItem title="質問1">
        <p>回答1の内容</p>
      </AccordionItem>
      <AccordionItem title="質問2">
        <p>回答2の内容</p>
      </AccordionItem>
    </div>
  );
}このようにコンポーネント化すると、繰り返し使う部分を簡潔に書けるようになります。
CSSフレームワークを用いる場合
BootstrapやTailwind CSSなどのCSSフレームワークを使っている場合は、これらのフレームワークが提供するアコーディオンコンポーネントを利用することも検討しましょう。
例えば、Bootstrapでは以下のようにアコーディオンを実装できます。
<div class="accordion" id="accordionExample">
  <div class="accordion-item">
    <h2 class="accordion-header" id="headingOne">
      <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
        アコーディオンアイテム #1
      </button>
    </h2>
    <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
      <div class="accordion-body">
        ここにコンテンツが入ります。
      </div>
    </div>
  </div>
  
  <div class="accordion-item">
    <h2 class="accordion-header" id="headingTwo">
      <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
        アコーディオンアイテム #2
      </button>
    </h2>
    <div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
      <div class="accordion-body">
        ここにコンテンツが入ります。
      </div>
    </div>
  </div>
</div>Bootstrap使用時は、別途JavaScriptを書かなくても、データ属性でアコーディオンの動作を定義できます。
ただし、フレームワークを使うとHTML構造が複雑になったり、カスタマイズの自由度が下がったりする場合もあるので、プロジェクトの要件に応じて選択しましょう。
よくあるトラブルと対処法
開閉アニメーションが一瞬で終わってしまう
アコーディオンを実装していると、「アニメーションが設定したはずなのに、一瞬で開閉してしまう」ということがあります。
これはよくある問題で、主に以下の原因が考えられます。
- display: none;を使っている(- displayプロパティはアニメーションできない)
- max-heightの値がコンテンツの高さより小さい
- transitionプロパティの指定が間違っている
対処法としては、
- display: none;ではなく- max-heightや- opacityでアニメーションする
- max-heightは必ずコンテンツの高さより大きい値を設定する
- JavaScriptでscrollHeightを使って動的に高さを計算する
などの方法があります。
スマートフォンでの動作が不安定
スマートフォンでアコーディオンがうまく機能しない場合、以下のポイントをチェックしてみましょう。
- タップ領域が小さすぎないか(最低44px四方くらい必要)
- タッチイベントを適切に処理しているか
- スマホ表示時に要素がずれていないか
特に、PCでは問題なく動作していても、スマホでは動作が違うことがよくあります。例えば、hoverの効果がスマホでは期待通りに動かなかったりします。
スマホでの動作を安定させるためには、メディアクエリを使って画面サイズごとにスタイルを調整したり、スマホでのテスト環境を整えておくことが大切です。
多段アコーディオンの管理
アコーディオン内にさらにアコーディオンがある「多段アコーディオン」を実装する場合、イベントの伝播(バブリング)に注意する必要があります。
例えば、子アコーディオンをクリックすると、親アコーディオンのクリックイベントも発火してしまう場合があります。
これを防ぐには、event.stopPropagation()を使ってイベントの伝播を止める方法があります。
// 子アコーディオンのクリックイベント
childHeader.addEventListener("click", (event) => {
  // イベントの伝播を止める
  event.stopPropagation();
  
  // アコーディオンの開閉処理
  const item = childHeader.parentElement;
  item.classList.toggle("active");
});また、CSSのセレクタも工夫して、親と子で異なるクラス名を使うなど、明確に区別することも大切です。
アコーディオンとSEOの関係
アコーディオンを使うとコンテンツの一部が隠れるわけですが、これがSEO(検索エンジン最適化)にどう影響するか気になりますよね。
結論から言うと、適切に実装されたアコーディオンはSEOにほとんど悪影響を与えません。
現代の検索エンジン(特にGoogleのクローラー)は、JavaScriptを実行して隠れたコンテンツも読み取ることができます。
ただし、いくつか注意点があります。
- 重要なコンテンツをアコーディオンの中に隠しすぎない
- 適切なHTML構造とセマンティックマークアップを使用する
- アコーディオンの見出しには関連するキーワードを含める
特に、ページの主要なコンテンツ(ユーザーがそのページに来た目的となる情報)はアコーディオンの外に置くか、デフォルトで開いた状態にしておくことをおすすめします。
最終的には、ユーザー体験(UX)とSEOのバランスが大切です。ユーザーがストレスなく情報を見つけられるなら、それはSEOにもプラスに働くと考えて間違いないでしょう。
まとめ
この記事では、JavaScriptを使ったアコーディオンUIの実装方法について解説してきました。
アコーディオンは、限られたスペースで多くの情報を整理して表示できる便利なUI要素です。
基本的な実装ステップは以下のとおりです。
- HTMLで基本構造を作る(見出し部分と内容部分)
- CSSで開閉時のスタイルを定義する(max-heightなどを使ったアニメーション)
- JavaScriptでクリックイベントを設定し、クラスの付け替えで開閉を制御する
また、実務でアコーディオンを実装する際には、以下のポイントにも注意しましょう。
- アクセシビリティに配慮する(aria-expanded属性など)
- スマートフォンでの操作性を考慮する
- アニメーションの速度はユーザー体験に影響する
- 多段アコーディオンを作る場合はイベントの伝播に注意する
アコーディオンUIは、FAQページ、商品詳細ページ、スマホメニューなど、さまざまな場面で活用できます。
ぜひこの記事を参考に、あなたのWebサイトに使いやすいアコーディオンを実装してみてください!
コスパよく JavaScript を学習できる Udemy 講座人気ランキング
ちなみに姉妹サイトの Learning Next では、JavaScript を独学で学べる Udemy 講座の人気ランキングや、各講座の受講生レビューをもとにした分析スコアを公開しています。
「学習にあまりお金をかけたくない…」「スクールに通う時間がない」という方は、こちらを参考に Udemy 講座の学習も検討してはいかがでしょうか?
▶️ UdemyでJavaScriptを学べる講座の人気ランキング – Learning Next
ひとめで良い点・悪い点、さらにおすすめポイントが分かりますので、講座選びで失敗したくない方はぜひ活用してみてください!

