classList.toggle で切り替えた class を sessionStorage で維持
使用ケース
執筆時点の Web 版の Youtube や Gmail は、左側にメニューがあります。このメニューは、ハンバーガーアイコンをクリックで縮小します。
このような「ユーザーのクリックで表示を変える」メニューで、「その変更された表示をページを移動してもページを再読み込みしても維持する」方法の紹介です。
classList.toggle
で切り替えた class を Web Storage API の sessionStorage
で維持します。この方法で実装すると、再度ハンバーガーアイコンをクリックするか、ブラウザを閉じない限り、ユーザーが選んだ状態を維持し続けます。
尚、ブラウザを閉じてもデータを維持する localStorage
でもやることは同じです。
JavaScript のコード
今回の方法では、このようなことを行います。
- ボタンのクリックで
<body>
タグの class に change を付ける。 - もう 1 度ボタンをクリックすれば、
<body>
タグの class から change を削除する。 <body>
タグの class に change がある場合に、sessionStorage
にデータを保存する。sessionStorage
にデータがあれば、ページを移動した時やブラウザを再読み込みした際に<body>
タグの class に change を付ける。
実際のコードは、このようなものです。まず、HTML の、<button>
に id が付いているとします。
<body>
<button id="button" type="button">ボタン</button>
</body>
そして、JavaScript です。
const { body } = document;
const button = document.getElementById('button');
const key = 'class';
const value = 'change';
const data = sessionStorage.getItem(key);
if (data) {
body.classList.add(value);
}
if (button) {
button.addEventListener('click', () => {
body.classList.toggle(value);
if (body.classList.contains(value)) {
sessionStorage.setItem(key, value);
} else {
sessionStorage.removeItem(key);
}
});
}
以下の部分は、ページを移動した時やブラウザを再読み込みした時に <body>
タグに class を付ける処理です。sessionStorage
に保存したデータがあれば class を付けます。データがなければ class は付けません。
if (data) {
body.classList.add(value);
}
それ以降は、ボタンをクリックした際の処理です。以下の部分でボタンをクリックする度に <body>
タグの class に change の追加と削除をしています。
body.classList.toggle(value);
以下の部分で <body>
タグの class に change があるかを判定し、sessionStorage
の処理を変えています。<body>
タグの class に change があれば、sessionStorage
にキーが class
、値が change
のデータを保存します。<body>
タグの class に change がなければ、sessionStorage
からデータを削除します。
if (body.classList.contains(value)) {
sessionStorage.setItem(key, value);
} else {
sessionStorage.removeItem(key);
}
あとは class の change を使い、CSS を指定します。これでページを移動しても再読み込みをしてもサイトの状態を維持できます。
ブラウザを閉じても状態を維持する場合は、3 ヶ所の sessionStorage
を localStorage
に変更します。
尚、sessionStorage
や localStorage
は、デベロッパーツールのアプリケーションで検証できます。
AMP でも可能
今回の方法は、AMP でも使えます。使用するコンポーネントは amp-script です。
AMP での JavaScript は、WorkerDOM の仕様に沿う必要があります。WorkerDOM で許可されている DOM API は、WorkerDOM compatibility で確認できます。
ただ、注意点が 3 つあります。
1 つ目の注意点が、<amp-script>
タグで <body>
タグは囲めません。そのため、<body>
タグの class を付け替えるのではなく、<body>
タグ内の要素の class を付け替える必要があります。
<!-- これはダメ -->
<amp-script src="JavaScript ファイルのパス" layout="container">
<body></body>
</amp-script>
2 つ目の注意点が、「Eleventy(11ty) + AMP プラグイン」と「WordPress + AMP プラグイン」で試した限り、sessionStorage.removeItem()
の動作が通常と異なります。キーが残り、値が #document
になります。
そのため、この部分を…。
sessionStorage.removeItem(key);
このように変更し空の値で更新するのがいいかもしれません。
sessionStorage.setItem(key, '');
3 つ目の注意点が、amp-script の layout 属性で container が使えないことです。もし使えば AMP エラーは出ませんが、デベロッパーツールのコンソールで以下のエラーが出ます。
[amp-script] Blocked 1 attempts to modify DOM element attributes or styles. For variable-sized <amp-script> containers, a user action has to happen first.
Google 翻訳:[amp-script]ブロックされた1は、DOM 要素の属性またはスタイルを変更しようとします。可変サイズの <amp-script> コンテナの場合、最初にユーザーアクションを実行する必要があります。
このエラーは、<amp-script>
タグに layout="container"
を指定しつつ、ユーザーのクリック操作なしで属性やスタイルを変更する際に起こります。Cumulative Layout Shift(CLS)の発生を防ぐのが目的です。
参考
今回のように sessionStorage
で class を追加する場合は、子要素によって高さが自動的に調整される amp-list などを <amp-script>
タグで囲む際に工夫が必要です。