Webサイトで構築されてるアコーディオンメニューの実装としては、JavascriptのライブラリであるjQueryを利用したものがありますが、今回の記事では、Javascriptの初学者やjQueryを使わずにjavascriptだけでアコーディオンメニューを実装したい方向けに基礎的な実装方法や拡張方法をご紹介したいと思います。
アコーディオンメニューとは
アコーディオンメニューとは、Webページ上で複数のコンテンツを一度に表示せず、必要に応じてユーザーがクリックすることで展開・閉じることができるUIコンポーネントのことです。代表的な動作として、1つのアイテムをクリックした時、他のアイテムが閉じ、クリックしたアイテムが開く動作をします。
メリット
アコーディオンメニューのメリットは、スペースの節約や情報を階層的に表示することができる点です。また、一度に表示するコンテンツを絞ることで、ユーザーが必要な情報に素早くアクセスできるようになります。
デメリット
デメリットとしては、すべてのコンテンツを閉じているときには、ユーザーが全ての情報を確認できないという点が挙げられます。また、複数のアイテムを開いた状態で画面が小さい場合、スクロールが必要になり、使い勝手が悪くなる可能性があります。
シンプルな実装方法
jQueryを使わずにHTML、CSSと純粋なJavaScriptを使用してアコーディオンメニューを実装する方法を紹介します。
HTML
<div class="accordion">
<div class="accordion-item">
<div class="accordion-header">Header 1</div>
<div class="accordion-content">Content 1</div>
</div>
<div class="accordion-item">
<div class="accordion-header">Header 2</div>
<div class="accordion-content">Content 2</div>
</div>
<div class="accordion-item">
<div class="accordion-header">Header 3</div>
<div class="accordion-content">Content 3</div>
</div>
</div>
アコーディオンメニューの各アイテムをaccordion-itemクラスに記述し、その配下にaccordion-headerとaccordion-contentを記述します。
CSS
.accordion {
border: 1px solid #ccc;
margin: 10px 0;
}
.accordion-item {
border-bottom: 1px solid #ccc;
}
.accordion-header {
padding: 10px;
background-color: #f5f5f5;
font-weight: bold;
cursor: pointer;
}
.accordion-content {
padding: 10px;
display: none;
}
.accordion-content.active{
display: block;
}
accordion-contentクラスには、display: none;で通常時は非表示のスタイルを設定します。activeクラスが付与された時だけ、表示のスタイルが適用されます。
Javascript
const accordionItems = document.querySelectorAll('.accordion-item');
accordionItems.forEach(item => {
const header = item.querySelector('.accordion-header');
const content = item.querySelector('.accordion-content');
header.addEventListener('click', () => {
content.classList.toggle('active');
});
});
- accordion-itemクラスを持つHTML要素を全て取得します
- 続いて、繰り返し処理で、accordion-headerとaccordion-contentクラスの要素を定数に格納します
- イベントリスナーでheaderがクリックされた際に、contentのactiveクラスを切り替えます(付与or削除)
これで、アコーディオンメニューが実装されました。クリックするとコンテンツが開閉します。
アコーディオンメニューの拡張機能
フィルタリング機能の追加
フィルタリング機能をアコーディオンメニューに追加することで、特定のカテゴリに属するアイテムだけを表示することができます。フィルタリング機能を実装するには、JavaScriptを使用する必要があります。
HTML
<input type="text" id="search-box" placeholder="Search...">
<ul class="accordion-menu">
<li>
<h3 class="accordion-header">Category 1</h3>
<ul class="accordion-content">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</li>
<li>
<h3 class="accordion-header">Category 2</h3>
<ul class="accordion-content">
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</li>
</ul>
先ほどのアコーディオンメニュー同様に、各アイテムをaccordion-headerとaccordion-contentに記述をしていきます。
Javascript
const searchBox = document.getElementById("search-box");
const accordionMenu = document.querySelector(".accordion-menu");
//serchボックスがクリックされた時にイベント実行
searchBox.addEventListener("input", function() {
const searchTerm = searchBox.value.toLowerCase();//小文字に変換
//アイテム毎に繰り返し処理
for (let i = 0; i < accordionMenu.children.length; i++) {
const categoryHeader = accordionMenu.children[i].querySelector(".accordion-header");
const categoryContent = accordionMenu.children[i].querySelector(".accordion-content");
let categoryMatches = false;//検索結果false
//コンテントの子要素を繰り返し処理
for (let j = 0; j < categoryContent.children.length; j++) {
const item = categoryContent.children[j];
//入力Wordとアイテムの比較
if (item.textContent.toLowerCase().includes(searchTerm)) {
categoryMatches = true;
item.style.display = "";
} else {
item.style.display = "none";
}
}
//検索結果がヒットした場合、表示する
if (categoryMatches) {
categoryHeader.style.display = "";
categoryContent.style.display = "";
} else {
categoryHeader.style.display = "none";
categoryContent.style.display = "none";
}
}
});
- search-box、accordion-menuクラスのHTML要素を取得します。
- search-boxに入力されるとイベントが発火します。
- search-boxに入力されたワードをtoLowerCase()で小文字に変換し、accordion-menuの子要素で繰り返し処理を行い、headerとcontentのHTML要素を取得します。
- 更に、contentの子要素で繰り返し処理を行い、textcontent.includes()で検索ワードを含んでいるか判定を行い、スタイル(表示・非表示)を適用します。
- 最後に、categoryMatchesの判定結果により、headerとcontentのスタイル(表示・非表示)を設定します。
このコードは、検索ボックスに入力されたテキストと一致するアイテムのみを表示するようにアコーディオンメニューをフィルタリングします。
ネスト(入れ子)機能の追加
アコーディオンメニューにネスト(入れ子)機能を追加することで、サブカテゴリのアイテムを親カテゴリに表示することができます。以下のコードを使用すると、アコーディオンメニューにネスト機能を追加することができます。
HTML
<ul class="accordion-menu">
<li>
<h3 class="accordion-header">Category 1</h3>
<ul class="accordion-content">
<li>
<h4 class="accordion-subheader">Subcategory 1</h4>
<ul class="accordion-subcontent">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</li>
<li>
<h4 class="accordion-subheader">Subcategory 2</h4>
<ul class="accordion-subcontent">
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</li>
</ul>
</li>
<li>
<h3 class="accordion-header">Category 2</h3>
<ul class="accordion-content">
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
</ul>
</li>
</ul>
Javascript
const accordionHeaders = document.querySelectorAll(".accordion-header");
const accordionSubheaders = document.querySelectorAll(".accordion-subheader");
for (let i = 0; i < accordionHeaders.length; i++) {
accordionHeaders[i].addEventListener("click", function() {
this.nextElementSibling.classList.toggle("active");
});
}
for (let i = 0; i < accordionSubheaders.length; i++) {
accordionSubheaders[i].addEventListener("click", function() {
this.nextElementSibling.classList.toggle("active");
});
}
このコードは、サブカテゴリのアイテムを親カテゴリに表示するために、アコーディオンメニューのネスト機能を拡張します。
基本的な記述としては、先ほどご紹介したアコーディオンメニューの実装方法と同様です。
各親カテゴリとサブカテゴリのアイテムに対してクリックイベントを設定し、クリックされたアイテムの次の要素にactiveクラスを付けることで、サブカテゴリのアイテムを表示することができます。
・this:現在、選択されている要素を指しています。
・nextElementSibling:HTMLでの一つ隣の要素を指しています。
アコーディオンメニューのデザインパターン
アコーディオンメニューの基本的なデザインパターンと、アイコンやアニメーションを追加する方法について解説します。
Copy code
.accordion-content {
max-height: 0;
overflow: hidden;
transition:all 2s ease-out;
}
.accordion-content.active{
max-height: 1000px;
transition: all 2s ease-in;
}
このCSSでは、先ほど記述したアコーディオンメニューの.accordion-content要素のmax-heightを0に設定し、overflowをhiddenに設定して、詳細コンテンツを非表示にします。.activeクラスが適用されると、max-heightが1000pxに変更され、詳細コンテンツが表示されます。transitionプロパティは、アコーディオンメニューの開閉にアニメーションを追加します。
まとめ
この記事では、jQueryを使わずにアコーディオンメニューを作成する方法をご紹介しました。
実装としては、jQueryを使ったほうが、簡単だとは思いますが、Javascriptで書いてみて動きの基本を押さえておくことも大事だと思います。初学者の方は特に、素のJavascriptで書いてみるのも良いと思います。