【js】アコーディオンスニペット

# Vanilla JSで作るアコーディオン
多階層のアコーディオンも可能です。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="dymdERj" data-user="__m-sato__" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/__m-sato__/pen/dymdERj">
  アコーディオン</a> by __m-sato__ (<a href="https://codepen.io/__m-sato__">@__m-sato__</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## html

```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
```scss
@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

```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
$('.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だけでもできる

```html
	<details>
		<summary>質問文</summary>
		<p>回答文</p>
	</details>
```
これだけでも一応アコーディオンできます。