Accordion Menu|アコーディオンメニューをVanillaJs(脱jQuery)で実装
アコーディオンメニューを「jQuery」を使用しないでVanillaJS(通常のJavaScript)で実装していきます。
目次
実装物
今回実装したものをコードペンで確認してみましょう!
HTML
ポイント①「li」でアコーディオンのボタン(AcBtn)と詳細(AcData)が隣同士に並んでいる。
ポイント② 「AcData」内に余白をつける時は「inner」要素のように内側からスタイルを当てる。
<ul class="AcItem">
<li class="AcBtn">
Accordion|01
</li>
<li class="AcData">
<div class="inner">
<span>・accordion menu・accordion menu</span>
</div>
</li>
</ul>
CSS(Scss)
ポイント① ボタン(AcBtn)のレイヤーを上(z-index:1;)に、その下に詳細(AcData)のレイヤーを設置(z-index:0;)。
※ 非表示にした際に詳細のテキスト情報等がボタンの上にかぶらないようにするため。
ポイント② 詳細(AcData)に「transition」を付与し開閉時にアニメーションを実装。
ポイント③ 詳細(AcData)に「overflow: hidden」を付与し閉じている時にテキスト情報等を隠す。
ポイント④ 開閉時にアコーディオン全体を囲っている要素(.AcItem)にCSSアニメーションクラス「.-open」を付与。
ポイント⑤ ボタンの「 border-radius: 8px;」分、詳細(AcData)を上にずらして隙間を埋める。
.AcWrap{
padding: 80px 0;
height: auto;
width: 80vw;
max-width: 400px;
margin: auto;
}
.AcItem{
margin: 24px 0;
}
.AcBtn{
position: relative;
z-index: 1;
padding: 16px;
background: #2b4055;
cursor: pointer;
border-radius: 8px;
transition: .4s ease background;
&:hover{
background: #3d4d5c;
}
}
.AcData{
position: relative;
z-index: 0;
overflow: hidden;
background: #fff;
color: #161616;
border-radius: 0 0 8px 8px;
margin-top: -8px;
transition: .4s ease height;
.inner{
padding: 24px 16px 16px;
transition: 1.2s ease opacity;
opacity: 0;
}
}
.AcItem.-open{
.inner{
opacity: 1;
}
}
JavaScript
ポイント① 詳細(AcData)の子要素の高さを取得し、配列に保存、その後自身の高さを「0」に更新。
ポイント② ボタンがクリックされた際にCSSアニメーションクラス「-open」を親(AcItem)に付与。
ポイント③ ボタンがクリックされた際に詳細(AcData)に保存しておいた高さを付与。
ポイント④ 別のアコーディオンを開く際に「onAllSwitch()」で開いているアコーディオンを閉じる。
※ 開いた状態のままにする場合は「onSwitch」内の「onAllSwitch」を削除する。
ポイント⑤ 横のリサイズの際に詳細の子要素の高さを再取得し可変。
window.addEventListener("DOMContentLoaded", () => {
class Accordion {
constructor(AcBtns, AcDatas){
this.isResize = true;
this.w = window.innerWidth;
this.HeightArray = [];
this.AcBtns = AcBtns;
this.AcDatas = AcDatas;
this.onSwitch = this.onSwitch.bind(this);
this.init();
}
setStyle(){
this.AcDatas.forEach(ele => {
let h = ele.children[0].clientHeight
ele.style.height = 0;
this.HeightArray.push(h);
});
}
onAllSwitch(){
this.AcBtns.forEach(ele => {
var parent = ele.parentNode;
var next = ele.nextElementSibling;
if (parent.classList.contains("-open")) {
next.style.height = 0;
parent.classList.remove("-open");
}
});
}
onSwitch(ele, i){
var parent = ele.parentNode;
var next = ele.nextElementSibling;
if (parent.classList.contains("-open")) {
this.onAllSwitch();
next.style.height = 0;
parent.classList.remove("-open");
} else {
this.onAllSwitch();
parent.classList.add("-open");
next.style.height = this.HeightArray[i] + "px";
}
}
onResize(){
this.HeightArray.length = 0;
this.AcDatas.forEach(ele => {
let h = ele.children[0].clientHeight
if (ele.parentNode.classList.contains("-open")) ele.style.height = h + "px";
this.HeightArray.push(h);
});
this.w = window.innerWidth;
}
init(){
this.setStyle();
window.addEventListener("resize", e => {
if (this.isResize) {
requestAnimationFrame(() => {
this.isResize = true;
if (this.w != window.innerWidth) {
this.onResize();
};
});
this.isResize = false;
};
});
this.AcBtns.forEach((ele, i) => {
ele.addEventListener("click", e => {
this.onSwitch(ele, i);
});
});
}
}
const AcBtns = document.querySelectorAll(".AcBtn");
const AcDatas = document.querySelectorAll(".AcData")
new Accordion(AcBtns, AcDatas);
});
まとめ
アコーディオンは比較的「jQuery」で実装してあるケースが大半を占めている印象です。
「jQuery」だと数行で終わる… 偉大ですね。
逆に「JavaScript」だけで実装してあるものは基本的に「display:none;」を使用して表示非表示を切り替えている実装が多く、CSSアニメーションに苦戦する印象です。(個人的に)