メインコンテンツまで移動する

classList.toggle で切り替えた class を sessionStorage で維持

  • 公開日
  • 更新日

Akira Web クリエイター

使用ケース

執筆時点の Web 版の Youtube や Gmail は、左側にメニューがあります。このメニューは、ハンバーガーアイコンをクリックで縮小します。

YouTube の開いているメニュー
YouTube の閉じているメニュー

このような「ユーザーのクリックで表示を変える」メニューで、「その変更された表示をページを移動してもページを再読み込みしても維持する」方法の紹介です。

classList.toggle で切り替えた class を Web Storage API の sessionStorage で維持します。この方法で実装すると、再度ハンバーガーアイコンをクリックするか、ブラウザを閉じない限り、ユーザーが選んだ状態を維持し続けます。

尚、ブラウザを閉じてもデータを維持する localStorage でもやることは同じです。

JavaScript のコード

今回の方法では、このようなことを行います。

  1. ボタンのクリックで <body> タグの class に change を付ける。
  2. もう 1 度ボタンをクリックすれば、<body> タグの class から change を削除する。
  3. <body> タグの class に change がある場合に、sessionStorage にデータを保存する。
  4. 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 ヶ所の sessionStoragelocalStorage に変更します。

尚、sessionStoragelocalStorage は、デベロッパーツールのアプリケーションで検証できます。

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> タグで囲む際に工夫が必要です。

フォローする