こんにちは!キャメルです!
今回はアコーディオンメニューの実装方法について解説をしていきます!
アコーディオンメニューってなんだっけ…?
今回の記事ではアコーディオンメニューの基本から実装方法まで丁寧に解説をしていきますので、ぜひ最後までお読みください^^
- アコーディオンメニューとは?
- アコーディオンメニューの実装方法
それではさっそく解説をしていきます。
アコーディオンメニューとは?
アコーディオンメニューは、クリックやタップをすることで開閉するメニューのことです。
下の画像のようなメニューです。皆さんも一度は見たことがあるのではないでしょうか。
アコーディオンメニューはその名の通り、アコーディオンのように「広がったり閉じたりする」動きから来ているようですね^^
アコーディオンメニューのメリット
アコーディオンメニューのメリットはこちら。
スペースの有効活用
メニューの開閉により画面スペースを効率的に活用できるため、特にスマートフォンなどの画面領域が限られているデバイスでは、情報をコンパクトに整理できるためとても効果的です。
直感的に操作ができる
クリックやタップで簡単に操作ができるため直感的に使いやすいです。
「+」アイコンなどを使い、よりユーザーに伝わりやすく工夫することも大切です^^
ユーザーにとって使いやすいサイト設計に役立つ
メニュー項目が増えてくると「ごちゃごちゃして分かりづらい…」など、ユーザビリティの低下につながる可能性があります。
アコーディオンメニューを使うことでスッキリと整理することができるため、ユーザーにとっても見やすく使いやすいサイトを作ることが可能です。
アコーディオンメニューのデメリット
続いてデメリットについても解説をしていきます。
メリットばかりを考えがちですが、デメリットを知ることでよりユーザーが使いやすいサイト制作を意識できるようになるのでしっかり確認しておきましょう!
特に意識をするべきはこの2つ。
アクセシビリティの問題
これはアコーディオンメニューに限った話ではないですが、アクセシビリティを意識する必要があります。
ドロワーメニューの時にも説明しましたが、<button>タグを使うなどしてスクリーンリーダーやキーボード操作を行なっているユーザーでも使えるような設計にしましょう。
アコーディオンが開かない…ということはないように気をつけましょう!
ユーザー体験が低下する可能性がある
ユーザー体験の低下としては以下のようなものが挙げられます。
- クリックやタップする回数が増え煩わしさを感じる
- 過度なアニメーションは、早く操作をしたいユーザーにとってはストレスに感じる
- どの項目が開いていて、どの項目が閉じているのかを把握しづらい
- スマートフォンなどの小さいデバイスで操作がしづらい
アコーディオンメニューのメリットとデメリットをしっかり考慮した上で使っていきましょう!
アコーディオンメニューの実装方法:JavaScript編
それではここからアコーディオンの作り方について解説をしていきます。
今回はJavaScriptとjQueryを使った2つの実装方法について解説をしていきます!
一つひとつ丁寧に解説をしていくのでぜひ最後までお読み下さい^^
参考コードはこちら。赤字のポイント部分を中心に解説をしていきますね。
-- HTML --
<section class="faq">
<div class="faq_inner">
<h2 class="faq__title">よくある質問</h2>
<div class="faq__boxes">
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">1つ目の質問です</span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">1つ目の回答です。1つ目の回答です。1つ目の回答です。1つ目の回答です。1つ目の回答です。</span>
</div>
</div>
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">2つ目の質問です</span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">2つ目の回答です。2つ目の回答です。2つ目の回答です。2つ目の回答です。2つ目の回答です。</span>
</div>
</div>
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">3つ目の質問です</span>
<span class="faq-box__question-"></span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">3つ目の回答です。3つ目の回答です。3つ目の回答です。3つ目の回答です。3つ目の回答です。</span>
</div>
</div>
</div>
</div>
</section>
-- CSS --
.faq {
padding: 80px 0;
}
.faq_inner {
padding-left: 40px;
padding-right: 40px;
}
.faq__title {
text-align: center;
font-size: 24px;
font-weight: bold;
}
.faq__boxes {
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 40px;
padding: 30px;
background-color: #e3e3e3;
border-radius: 5px;
}
.faq-box {
border-radius: 5px;
background-color: #fff;
}
.faq-box__question {
padding: 10px;
width: 100%;
}
.faq-box__question-icon {
display: grid;
place-items: center;
width: 30px;
height: 30px;
background-color: #65AF36ff;
color: #fff;
border-radius: 50%;
}
.faq-box__question-text {
position: relative;
display: block;
margin-top: 10px;
padding: 5px 0;
text-align: left;
}
.faq-box__question-text::after {
position: absolute;
right: 0;
content: "+";
transition: transform 0.3s ease;
}
.faq-box.appear .faq-box__question-text::after {
transform: rotate(45deg);
}
.faq-box__answer {
display: none;
margin-top: 20px;
padding: 10px;
}
.faq-box.appear .faq-box__answer {
display: block;
animation: accordion 0.3s;
}
@keyframes accordion {
0% {
transform: translateY(-30px);
}
100% {
transform: none;
}
}
.faq-box__answer-icon {
display: grid;
place-items: center;
width: 30px;
height: 30px;
background-color: #ef7e28;
color: #fff;
border-radius: 50%;
}
.faq-box__answer-text {
display: block;
margin-top: 10px;
padding: 5px 0;
}
-- JavaScript --
{
const accordions = document.querySelectorAll('#js-accordion');
accordions.forEach((accordion) => {
accordion.addEventListener('click', () => {
accordion.parentNode.classList.toggle('appear');
accordions.forEach((el) => {
if (accordion !== el) {
el.parentNode.classList.remove('appear');
}
})
});
});
}
1. アコーディオンメニューの形を作る
まずはHMTLとCSSで見た目の形を作っていきます。
HTML
-- HTML --
<div class="faq__boxes">
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">1つ目の質問です</span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">1つ目の回答です。1つ目の回答です。1つ目の回答です。1つ目の回答です。1つ目の回答です。</span>
</div>
</div>
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">2つ目の質問です</span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">2つ目の回答です。2つ目の回答です。2つ目の回答です。2つ目の回答です。2つ目の回答です。</span>
</div>
</div>
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">3つ目の質問です</span>
<span class="faq-box__question-"></span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">3つ目の回答です。3つ目の回答です。3つ目の回答です。3つ目の回答です。3つ目の回答です。</span>
</div>
</div>
</div>
① .faq__boxesでアコーディオン全体を囲います。
② .faq_boxでそれぞれの質問と回答のボックスを作ります。
③ <button>タグを使って質問ボックスを作っていきます。
ここで<button>タグを使うのはtabキーでフォーカスが当たるようにするためです。Enterボタンを押すことでアコーディオンが開閉するようになります。
④ .faq-box__answerで回答のボックスを作っていきます。
ざっとこのような構成になっています。HTMLに関してはシンプルな構造なのでそれほど難しくないですよね^^
-- HTML --
<div class="faq__box faq-box">
ちなみにこの部分。なぜクラス名をふたつ付けているかというと
.faq-boxを一つのパーツとして作っているからですね。
「1つ目の質問と回答」「2つ目の質問と回答」「3つ目の質問と回答」は構造は同じです^^
そのような場合は使いまわせるパーツとしてブロック化してしまうことがあります。
この辺りはCSS設計を学ぶと理解できるようになります。今回は本筋とズレてしまうので詳しい解説は割愛します。
CSS
続いてCSSです。
-- CSS --
.faq {
padding: 80px 0;
}
.faq_inner {
padding-left: 40px;
padding-right: 40px;
}
.faq__title {
text-align: center;
font-size: 24px;
font-weight: bold;
}
.faq__boxes {
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 40px;
padding: 30px;
background-color: #e3e3e3;
border-radius: 5px;
}
.faq-box {
border-radius: 5px;
background-color: #fff;
}
.faq-box__question {
padding: 10px;
width: 100%;
}
.faq-box__question-icon {
display: grid;
place-items: center;
width: 30px;
height: 30px;
background-color: #65AF36ff;
color: #fff;
border-radius: 50%;
}
.faq-box__question-text {
position: relative;
display: block;
margin-top: 10px;
padding: 5px 0;
text-align: left;
}
.faq-box__question-text::after {
position: absolute;
right: 0;
content: "+";
transition: transform 0.3s ease;
}
.faq-box.appear .faq-box__question-text::after {
transform: rotate(45deg);
}
.faq-box__answer {
display: none;
margin-top: 20px;
padding: 10px;
}
.faq-box.appear .faq-box__answer {
display: block;
animation: accordion 0.3s;
}
@keyframes accordion {
0% {
transform: translateY(-30px);
}
100% {
transform: none;
}
}
.faq-box__answer-icon {
display: grid;
place-items: center;
width: 30px;
height: 30px;
background-color: #ef7e28;
color: #fff;
border-radius: 50%;
}
.faq-box__answer-text {
display: block;
margin-top: 10px;
padding: 5px 0;
}
それでは上から順番にいきましょう。
-- CSS --
.faq-box__question-text::after {
position: absolute;
right: 0;
content: "+";
transition: transform 0.3s ease;
}
今回は簡単に「+」マークは疑似要素で作りました。
fontawesomeなどを使ってアイコンを準備してももちろんOKです^^
transitionでアニメーションを設定しているのはクリックした時に回転させて「✖︎」マークにするためです。
-- CSS --
.faq-box.appear .faq-box__question-text::after {
transform: rotate(45deg);
}
ここでは先ほど作った疑似要素に、アコーディオンが開いたときのスタイルを当てています。
.faq-boxにappearクラスが付いた時(メニューがクリックされたとき)に回転させてね
という意味です。このappearクラスのつ付け外しに関してはJavaScriptで制御していきます。
-- CSS --
.faq-box__answer {
display: none;
margin-top: 20px;
padding: 10px;
}
.faq-box.appear .faq-box__answer {
display: block;
animation: accordion 0.3s;
}
@keyframes accordion {
0% {
transform: translateY(-30px);
}
100% {
transform: none;
}
}
.faq-box__answer(回答)については最初は非表示にしておきたいため、display:none;として
appearクラスが付いたとき(メニューがクリックされたとき)に表示させています。
そしてここでanimationプロパティを使っていますが
こんな疑問が出てきそうです。
transitionではなくanimationを使っているのはなぜ?
display:none; は要素を消してしまうため transition が適用されないという特徴があります。
そのため@keyframesを使ってaccordionという名前(任意)のアニメーションを作って
appearクラスが付いたときにアニメーションが動くように設定しています。
faq-boxfaq-boxfaq-bo
2. アコーディオンメニューに動きを実装していく
ここからJavaScriptを使って、クリックに応じてアコーディオンが動くように作っていきます。
-- JavaScript --
{
const accordions = document.querySelectorAll('.js-accordion');
accordions.forEach((accordion) => {
accordion.addEventListener('click', () => {
accordion.parentNode.classList.toggle('appear');
accordions.forEach((el) => {
if (accordion !== el) {
el.parentNode.classList.remove('appear');
}
})
});
});
}
上から順番に見ていきましょう。
const accordions = document.querySelectorAll('.js-accordion');
この構文は本当によく出てくるので覚えておいてください。
accordions(任意の名前)という変数に js-accordion というクラス属性を持つ要素を代入しています。
今回は.js-accordionを持つ要素は3つあるので3つ全てを取得しています。
今回のケースを例にすると
- querySelectorAllは.js-accordion属性を持つすべての要素を取得する
- querySelectorは.js-accordion属性を持つ一つ目の要素だけを取得する
続いて上から順番に見ていきましょう。
-- JavaScript --
accordions.forEach((accordion) => {
accordion.addEventListener('click', () => {
accordion.parentNode.classList.toggle('appear');
accordions.forEach((el) => {
if (accordion !== el) {
el.parentNode.classList.remove('appear');
}
})
});
});
accordions.forEach((accordion) => {
これは「accordions に入っている要素(今回は3つ)を一つずつ取り出して一つずつaccordionに入れる」という意味になるよ
forEachでは複数形の変数名の単数形の名前を付けると分かりやすいです。
今回の場合は変数名を accordions としたので単数形の accordion と名前を付けました。
-- JavaScript --
accordion.addEventListener('click', () => {
accordion.parentNode.classList.toggle('appear');
クリックするたびに、accordionの親要素に対してappearクラスをtoggle(付け外し)するよ
という意味になります。ここでもう一度HTMLを見てみましょう。
-- HTML --
<div class="faq__boxes">
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">1つ目の質問です</span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">1つ目の回答です。1つ目の回答です。1つ目の回答です。1つ目の回答です。1つ目の回答です。</span>
</div>
</div>
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">2つ目の質問です</span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">2つ目の回答です。2つ目の回答です。2つ目の回答です。2つ目の回答です。2つ目の回答です。</span>
</div>
</div>
<div class="faq__box faq-box">
<button class="faq-box__question js-accordion">
<span class="faq-box__question-icon">Q</span>
<span class="faq-box__question-text">3つ目の質問です</span>
<span class="faq-box__question-"></span>
</button>
<div class="faq-box__answer">
<span class="faq-box__answer-icon">A</span>
<span class="faq-box__answer-text">3つ目の回答です。3つ目の回答です。3つ目の回答です。3つ目の回答です。3つ目の回答です。</span>
</div>
</div>
</div>
accordionは.js-accordionでしたね。
その親要素なので、以下の部分がaccordionの親要素ということになります。
<div class="faq__box faq-box">
そしてここで先ほど作ったCSSをみてみましょう。
-- CSS --
.faq-box__answer {
display: none;
margin-top: 20px;
padding: 10px;
}
.faq-box.appear .faq-box__answer {
display: block;
animation: accordion 0.3s;
}
@keyframes accordion {
0% {
transform: translateY(-30px);
}
100% {
transform: none;
}
}
appearクラスが付いたとき、つまりクリックされたときに display:block; として表示させています。
そして最後です!これは「他の質問をクリックしたときに、今開いている回答を閉じる」ための実装になります。
メニュー項目が多い時などには便利なものになりますので、ぜひ理解しておきましょう。
-- JavaScript --
accordions.forEach((accordion) => {
accordion.addEventListener('click', () => {
accordion.parentNode.classList.toggle('appear');
accordions.forEach((el) => {
if (accordion !== el) {
el.parentNode.classList.remove('appear');
}
})
});
});
まずは再度 accordions から一つずつ要素を取り出します。先ほどは accordion という名前にしたので、別の任意の名前をつけます。今回はelement(要素)の頭文字をとって el としました。
そして、「el とaccordion(クリックされたボタン)が異なるのであれば、elの親要素からappearクラスを削除する(回答を閉じる)」という意味になります。
これでクリックされた質問以外の回答が閉じられます。
このあたりは慣れるまでは少し難しく感じるかもしれませんが、一つずつ理解していけば確実にできるようになるので焦らずゆっくり頑張っていきましょう^^
アコーディオンメニューの実装方法:jQuery編
さぁJavaScript編が終わったので、次はjQuery編ですが、ぶっちゃけjQueryで書いた方が断然楽です!笑
じゃあなんでJavaScriptやったんだよっ!
という声も聞こえてきそうですが、jQueryはあくまでJavaScriptのライブラリです。
あくまで僕の考えですが、JavaScriptを理解できているからこそ、jQueryの理解が深まったり、応用が効かせられると思ってます。
なので、このブログではなるべくjQueryで簡単にできる実装だとしてもJavaScriptも一緒に解説していきます。
なるべくわかりやすく解説をしていくので一緒に頑張りましょう^^
JavaScriptをjQueryで書いてみよう!
jQueryの参考コードはこちら。
上から解説をしていきます。
-- jQuery --
jQuery('.js-accordion').on('click', function(e){
e.preventDefault();
if (jQuery(this).parent().hasClass('appear')) {
jQuery(this).parent().removeClass('appear');
jQuery(this).next().slideUp();
} else {
jQuery(this).parent().addClass('appear');
jQuery(this).next().slideDown();
}
});
.js-accordionをクリックしたときに以下のイベントが発生しますよ〜
以下のイベントはこの部分。
-- jQuery --
jQuery('.js-accordion').on('click', function(e){
e.preventDefault();
if (jQuery(this).parent().hasClass('appear')) {
jQuery(this).parent().removeClass('appear');
jQuery(this).next().slideUp();
} else {
jQuery(this).parent().addClass('appear');
jQuery(this).next().slideDown();
}
});
-- jQuery --
jQuery('.js-accordion').on('click', function(e){
e.preventDefault();
if (jQuery(this).parent().hasClass('appear')) {
jQuery(this).parent().removeClass('appear');
jQuery(this).next().slideUp();
} else {
jQuery(this).parent().addClass('appear');
jQuery(this).next().slideDown();
}
});
もしクリックした要素の親要素がappearクラスを持っていたら、以下のコードを実行しますよ〜
以下のコードはこちら。その意味は
-- jQuery --
jQuery('.js-accordion').on('click', function(e){
e.preventDefault();
if (jQuery(this).parent().hasClass('appear')) {
jQuery(this).parent().removeClass('appear');
jQuery(this).next().slideUp();
} else {
jQuery(this).parent().addClass('appear');
jQuery(this).next().slideDown();
}
});
- クリックした要素の親要素からappearクラスを削除する
- クリックした要素の次の兄弟要素をスライドアップする(非表示にする)
-- jQuery --
jQuery('.js-accordion').on('click', function(e){
e.preventDefault();
if (jQuery(this).parent().hasClass('appear')) {
jQuery(this).parent().removeClass('appear');
jQuery(this).next().slideUp();
} else {
jQuery(this).parent().addClass('appear');
jQuery(this).next().slideDown();
}
});
ちなみにこの this はクリックした要素を指します。
続いてelse以下はこちら。
-- jQuery --
jQuery('.js-accordion').on('click', function(e){
e.preventDefault();
if (jQuery(this).parent().hasClass('appear')) {
jQuery(this).parent().removeClass('appear');
jQuery(this).next().slideUp();
} else {
jQuery(this).parent().addClass('appear');
jQuery(this).next().slideDown();
}
});
もしクリックした要素の親要素がappearクラスを持っていなければ、以下のコードを実行しますよ〜
- クリックした要素の親要素にappearクラスを追加する
- クリックした要素の次の兄弟要素をスライドダウンする(表示する)
- parent():その要素の親要素
- next():その要素の直後の兄弟要素
- addClass()/removeClass():その要素にクラスを追加/削除する
- slideDown()/slideUp():スライドしながら表示/非表示にする
CSSの調整
最後に一部だけCSSを修正します。
-- CSS --
以下コードは不要なため削除
.faq-box.appear .faq-box__answer {
display: block;
animation: accordion 0.3s;
}
@keyframes accordion {
0% {
transform: translateY(-30px);
}
100% {
transform: none;
}
}
上記コードは「ボタンをクリックしたらアニメーションで回答を表示する」記述ですが、
jQueryではslideDown()/slideUp()で表示/非表示の切り替えをしているため、機能がかぶってしまうので削除します。
これで完了です!^^
まとめ:JavaScriptとjQueryを使ってアコーディオンメニューを作ってみよう
いかがでしたか?
今回はJavaScriptとjQueryを使ってアコーディオンメニューを作る方法について解説をしました。
- アコーディオンメニューとは?
- JavaScriptによるアコーディオンメニューの実装方法
- jQueryによるアコーディオンメニューの実装方法
アコーディオンを上手に使うことで、サイトをスッキリ見せることができたり、ユーザビリティを向上させることができます。
基本的にはjQueryを使って作ればいいと思いますが、jQueryの大元であるJavaScriptを理解しておくことは大事です^^
基本はjQueryで作るけどJavaScriptでも作ろうと思えば作れる
というスタンスが大切かなと思います。
一つひとつ基礎から丁寧にしっかりと学んでいく。それが今後学んでいく上でも理解度の向上に役立つはずです。
この記事を読んである程度理解できたら、まずは一度自分で作ってみましょうね^^
以上、アコーディオンメニューの実装方法についての解説でした!
また次の記事でお会いしましょう。それでは!