・Q&Aのアコーディオン作成したい
・jQueryを使用しないでアコーディオン作成したい
・実践でよく使われるQ&Aのアコーディオンのテンプレを持っておきたい
こんな疑問や悩みに答えます。
今回作ったのは以下のようなものです。
コードだけわかればいいという人は、以下のコードを参考に作成したり、自分用に修正して見てください。
See the Pen Untitled by 森寅治 (@moritoraji) on CodePen.
このブログ「とらまるブログ」の運営者。2019年からエンジニアとしてサイト制作やシステム開発を行いつつ、ブログ情報発信を行っている。これまでのWeb制作実績は40件以上。
こちらの記事を書こうと思った背景は、アコーディオンメニューは実践の「Q&A」でよく使われているので、テンプレにして持っておくとコーディングの効率化をはかれます。
ぜひ、自分のテンプレを参考に独自のテンプレを保存しておくことをオススメします。
アコーディオンメニューとは?
クリックした際に開閉するメニューのことです。 操作したときの開閉する動きが、アコーディオンの蛇腹部分に似ていることから、アコーディオンメニューと呼ばれているそうです。
上記のCodepenで作成したもので確認できます。テキストあたりをクリックすると回答が下に表示されたり、非表示になったりします。
jQueryを使用しないで制作する背景
自分は、基本的にサイトを制作するときはjQueryを使用しないようにしています。理由は以下です。
・jQueryのファイルはそれなりの重さがあるので、サイトのページ表示のスピードに影響するから。
既存のサイトなどでは使用されていることがあるので、覚えておくことに越したことはありませんが、個人的には素のJavaScriptで書くことに慣れた方がいいと思っています。
コード内容の解説
解説に入る前に、正直自分はこういった記事を見る際にごちゃごちゃ色々書かれてもあまり理解できなかった経験があります。
ですので一旦コードをコピペして、簡単なデモを実装した後に解説を読むことをオススメします。
自分の手で動かしていく方が確実に理解できるスピードが上がります。
HTMLとCSSで見た目部分を作成
HTML
<div class="faq-inner__block">
<div class="block-q js-accordion">
<img src="Qの画像URL" alt="">
<p class="block-p">ダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
<div class="block-a">
<img src="Aの画像URL" alt="">
<p class="block-p">ダミーテキストダミーテキストダミーテキストダミーテキスト</p>
</div>
</div>
上記は、アコーディオンのQ&Aの要素を一つだけ取り出したHTMLの記述になります。
・faq-inner__blockクラス名でQ&Aの要素を囲み、その子要素にblock-qクラス名「Q」とblock-aクラス名「A」をおきます。
・基本的に左にQ、Aの画像、右にダミーテキストの場合がほとんどなので、imgタグとpタグをそれぞれblock-qクラスとblock-aクラスの子要素に入れます。
・jsで制御するためにjs-accordionクラスを付与しておきます。
クラス名は、自由に変えてください。
CSS
.block-q {
position: relative;
display: flex;
align-items: center;
width: 100%;
padding: 20px 0;
line-height: 150%;
}
.block-q:hover {
cursor: pointer;
}
.block-q img {
align-self: flex-start;
width: 60px;
height: 60px;
}
.block-q p.block-p {
width: calc(100% - 30px);
margin-left: 20px;
font-size: 18px;
font-weight: 700;
line-height: 153%;
color: #34004f;
}
.block-q p.block-p::before {
position: absolute;
top: 58px;
width: 24px;
right: 0;
height: 2px;
content: "";
background-color: #34004f;
transition: 0.2s;
transform: rotate(-90deg);
}
.block-q p.block-p::after {
position: absolute;
top: 58px;
right: 0;
width: 24px;
height: 2px;
content: "";
background-color: #34004f;
transition: 0.3s;
}
/* jsでis-openクラスが付与された時の挙動 */
.block-q.is-open p.block-p::before {
transform: rotate(0deg);/* クリック時に、棒を横にする */
}
.block-a {
position: relative;
display: flex;
align-items: flex-start;
height: 0;
padding: 0;
overflow: hidden;
transition: all 0.3s;
-ms-overflow-style: none;
scrollbar-width: none;
}
.block-a::-webkit-scrollbar {
display: none;
}
.block-a img {
width: 60px;
height: 60px;
}
.block-a p.block-p {
margin-left: 20px;
font-size: 16px;
font-weight: 400;
line-height: 173%;
color: #000;
}
/* jsでis-openクラスが付与された時の挙動 */
.block-a.is-open {
box-sizing: content-box;
padding-bottom: 20px;
overflow-y: auto;
transition: all 0.3s;
}
・プラスボタンは、擬似要素::beforeと::afterで作成します。
クリック時にマイナスになるようにis-openクラスをJSで付与するので、is-openクラスが付与された時のcssも準備しておきます。
・block-aクラスの要素を隠す際にdisplay:noneでなく、overflow: hidden;で要素を非表示にしている理由は、display:noneで行うとtransitionがうまく効かなくなるためです。
JavaScriptで動き部分を作成
javascript
/**
* アコーディオン
*/
const faq = document.querySelectorAll('.js-accordion')
function showToggle() {
const content = this.nextElementSibling /* block-aクラスが付与されたdivタグを指す。 */
content.classList.toggle('is-open')
const faq = this
faq.classList.toggle('is-open')
if (content.style.height) {
content.style.height = null
} else {
content.style.height = content.scrollHeight + 'px'
}
}
for (let i = 0; i < faq.length; i++) {
faq[i].addEventListener('click', showToggle)
}
最初にfaqで、HTMLでjs-accordionクラスを付与した要素を全て取得しています。
一つの想定でなく、複数想定でquerySelectorAllを使用しています。一つであればquerySelectorで取得する方法がありますがQ&Aは基本複数になります。
次にアコーディオンはクリック時に発火するので、addEventListenerでclickの際にshowToggle関数が発生するようにしています。
for文で囲っているのは、faqが複数あるので、どのfaqのボタンがクリックしたかを判定するようにしています。
最後にshowToggle関数の中身については、
js-accordionクラスのdivタグとその隣接する次のdivタグにis-openクラスの切り替えをtoggleで行っています。
nextElementSiblingプロパティは、次の要素を含みます。「次」とは具体的に、同じ親要素を持って隣接する次の要素です。
今回であれば、js-accordionのクラスの隣接する次の要素は、block-aクラスが付与されているdivタグになります。
content.scrollHeightで取得した要素の高さを与えています。これがないとblock-aクラスのタグに高さがないので、以下のように高さが0でクリックしても何も表示されない状態になってしまいます。
コメント