detailsとsummaryタグでtoggleを作る
今回の目的はdetails
とsummary
を使ってtoggleを作ろうと思います。あまりdetails
とsummary
はうまく使われていないことが多い。HTMLのデフォルトのUIでtoggleが作れる優れものになります。
toggleはスライドのアニメーションで動くものを作成します。
jQueryのslideToggle
のイメージがつきやすいと思います。
HTMLテンプレート
<details class="c-toogle">
<summary>Toggleの開閉ボタン</summary>
<div class="c-toogle_body">
<!-- Toggleの内容 -->
</div>
</details>
JavaScriptテンプレート
document.addEventListener("DOMContentLoaded", () => {
const TOGGLE_SELECTOR = ".c-toggle";
const CONTENT_SELECTOR = ".c-toogle_body";
const ANIMATION_SETTINGS = {
duration: 300,
easing: "ease-out",
};
const generateKeyframes = (element, isOpening) => {
const height = `${element.scrollHeight}px`;
return isOpening
? [
{ height: "0", opacity: 0 },
{ height, opacity: 1 },
]
: [
{ height, opacity: 1 },
{ height: "0", opacity: 0 },
];
};
const runAnimation = (element, isOpening) => {
return element.animate(
generateKeyframes(element, isOpening),
ANIMATION_SETTINGS
);
};
const setDetailsState = (details, isOpen) => {
if (isOpen) {
details.setAttribute("open", "");
} else {
details.removeAttribute("open");
}
};
const setupToggle = (details) => {
const summary = details.querySelector("summary");
const content = details.querySelector(CONTENT_SELECTOR);
if (!summary || !content) {
console.warn("Required elements not found", details);
return;
}
summary.addEventListener("click", (event) => {
event.preventDefault();
const isOpening = !details.hasAttribute("open");
if (isOpening) {
setDetailsState(details, true);
} else {
content.style.height = `${content.scrollHeight}px`;
content.offsetHeight; // Trigger reflow
}
const animation = runAnimation(content, isOpening);
animation.onfinish = () => {
if (isOpening) {
content.style.height = "auto";
} else {
setDetailsState(details, false);
content.style.height = "";
}
};
});
};
const init = () => {
const allDetails = document.querySelectorAll(TOGGLE_SELECTOR);
allDetails.forEach(setupToggle);
};
init();
});
JavaScriptのコード説明
- 初期化:
- DOMコンテンツが読み込まれた後に実行されます。
.c-toggle
クラスを持つ全ての要素に対してトグル機能をセットアップします。
- トグル機能:
- ユーザーが
summary
要素をクリックすると、関連するコンテンツが開閉します。 - 開閉の状態は
details
要素のopen
属性で管理されます。
- ユーザーが
- アニメーション:
- コンテンツの開閉時にスムーズなアニメーションを適用します。
- Web Animations APIを使用して、高さと不透明度のアニメーションを実現しています。
- キーフレーム生成:
generateKeyframes
関数で、開閉状態に応じたアニメーションのキーフレームを動的に生成します。
- アニメーション実行:
runAnimation
関数で、生成されたキーフレームを使用してアニメーションを実行します。
- 状態管理:
setDetailsState
関数で、details
要素のopen
属性を適切に設定または削除します。
- トグルのセットアップ:
- 各トグル要素に対して、クリックイベントリスナーを設定します。
- クリック時に、コンテンツの開閉状態を切り替え、適切なアニメーションを実行します。
- アニメーション完了後の処理:
- 開く場合: コンテンツの高さを
auto
に設定し、自然な高さを維持します。 - 閉じる場合:
open
属性を削除し、高さのスタイルをリセットします。
- 開く場合: コンテンツの高さを
- エラー処理:
- 必要な要素(
summary
や.c-toogle_body
)が見つからない場合、警告をコンソールに出力します。
- 必要な要素(
このコードは、アコーディオンやエクスパンダブルセクションなど、開閉可能なUI要素を実装する際に使用できます。アニメーションを用いることで、ユーザーエクスペリエンスを向上させています。