Vanilla JSで作るアコーディオン
多階層のアコーディオンも可能です。
See the Pen アコーディオン by __m-sato__ (@__m-sato__) on CodePen.
html
<dl class="accordion_wrap">
<div class="accordion_item">
<dt class="accordion_title js-accordion-trigger">質問文などのテキストが入ります。</dt>
<dd class="accordion_content">
回答テキストが入ります。
</dd>
</div>
<div class="accordion_item">
<dt class="accordion_title js-accordion-trigger">多階層にもできます。</dt>
<dd class="accordion_content">
<dl class="accordion_wrap">
<div class="accordion_item">
<dt class="accordion_title js-accordion-trigger">多階層にもできます。</dt>
<dd class="accordion_content">
<dl class="accordion_wrap">
<div class="accordion_item">
<dt class="accordion_title js-accordion-trigger">質問文などのテキストが入ります。</dt>
<dd class="accordion_content">
回答テキストが入ります。
</dd>
</div>
</dl>
</dd>
</div>
</dl>
<dl class="accordion_wrap">
<div class="accordion_item">
<dt class="accordion_title js-accordion-trigger">質問文などのテキストが入ります。</dt>
<dd class="accordion_content">
回答テキストが入ります。回答テキストが入ります。回答テキストが入ります。回答テキストが入ります。回答テキストが入ります。回答テキストが入ります。回答テキストが入ります。
</dd>
</div>
</dl>
</dd>
</div>
</dl>
js-accordion-trigger
クラスの次の兄弟要素をアコーディオンのコンテンツとして開閉している処理をjsで書いています。なのでjs-accordion-trigger
クラスがアコーディオンスイッチ、次の兄弟要素が開閉される要素の構造で記述していただければokです。
sass
@charset "UTF-8";
/* 簡易リセットCSS */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* アコーディオン全体 */
.accordion_wrap {
max-width: 800px;
margin: 0 auto;
width: 95%;
}
/* アコーディオン */
.accordion_item {
border: 1px solid #ccc;
margin-top: 10px;
}
/* アコーディオンのタイトル */
.accordion_title {
position: relative;
padding: 15px 60px 15px 20px;
font-weight: bold;
cursor: pointer;
/* (+)アイコン */
&::before,
&::after {
content: "";
position: absolute;
right: 20px;
top: 0;
bottom: 0;
margin: auto 0;
background-color: black;
width: 20px;
height: 2px;
transition: transform ease 0.2s, opacity ease 0.2s;
}
&::after {
transform: rotate(90deg);
}
}
/* アコーディオン展開時の(-)アイコン */
.accordion_title.is-active {
&::before {
transform: rotate(180deg);
}
&::after {
transform: rotate(180deg);
opacity: 0;
}
}
/* アコーディオンのコンテンツ */
.accordion_content {
padding: 0 20px 15px 20px;
display: none;
}
js
/* =================================================== */
// slideUp, slideDown, slideToggle関数を定義
/* =================================================== */
// 要素をスライドしながら非表示にする関数(jQueryのslideUpと同じ)
const slideUp = (el, duration = 300) => {
el.style.height = el.offsetHeight + "px";
el.style.transitionProperty = "height, margin, padding";
el.style.transitionDuration = duration + "ms";
el.style.transitionTimingFunction = "ease";
el.style.overflow = "hidden";
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
el.style.marginTop = 0;
el.style.marginBottom = 0;
setTimeout(() => {
el.style.display = "none";
el.style.removeProperty("height");
el.style.removeProperty("padding-top");
el.style.removeProperty("padding-bottom");
el.style.removeProperty("margin-top");
el.style.removeProperty("margin-bottom");
el.style.removeProperty("overflow");
el.style.removeProperty("transition-duration");
el.style.removeProperty("transition-property");
el.style.removeProperty("transition-timing-function");
}, duration);
};
// 要素をスライドしながら表示する関数(jQueryのslideDownと同じ)
const slideDown = (el, duration = 300) => {
el.style.removeProperty("display");
let display = window.getComputedStyle(el).display;
if (display === "none") {
display = "block";
}
el.style.display = display;
let height = el.offsetHeight;
el.style.overflow = "hidden";
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
el.style.marginTop = 0;
el.style.marginBottom = 0;
el.offsetHeight;
el.style.transitionProperty = "height, margin, padding";
el.style.transitionDuration = duration + "ms";
el.style.transitionTimingFunction = "ease";
el.style.height = height + "px";
el.style.removeProperty("padding-top");
el.style.removeProperty("padding-bottom");
el.style.removeProperty("margin-top");
el.style.removeProperty("margin-bottom");
setTimeout(() => {
el.style.removeProperty("height");
el.style.removeProperty("overflow");
el.style.removeProperty("transition-duration");
el.style.removeProperty("transition-property");
el.style.removeProperty("transition-timing-function");
}, duration);
};
// 要素をスライドしながら交互に表示/非表示にする関数(jQueryのslideToggleと同じ)
const slideToggle = (el, duration = 100) => {
if (window.getComputedStyle(el).display === "none") {
return slideDown(el, duration);
} else {
return slideUp(el, duration);
}
};
/* =================================================== */
// DOM操作
/* =================================================== */
// Triggerを全て取得
const accordionTriggers = document.querySelectorAll(".js-accordion-trigger");
for (let accordionTrigger of accordionTriggers) {
accordionTrigger.addEventListener("click", () => {
const accordionContent = accordionTrigger.nextElementSibling
slideToggle(accordionContent);
accordionTrigger.classList.toggle("is-active");
})
}
jqueryで書いた場合
$('.js-accordion-trigger').on('click', function() {
$(this).toggleClass('is-active');
$("+.accordion_content", this).slideToggle(100);
return false;
})
jqueryでアコーディオンをする場合はこれだけです。html構造も同じのまま、
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
をheadタグ内に記述するなどしてjqueryを読み込ませて上記記述をすれば完了です。
htmlだけでもできる
<details>
<summary>質問文</summary>
<p>回答文</p>
</details>
これだけでも一応アコーディオンできます。