無駄と文化

実用的ブログ

history.back() でページを戻ったときの JavaScript の挙動

「ページ A」から「ページ B」へ遷移した後に「ページ B」で hidtory.back() が実行され「ページ A」に戻ってきたとき、「ページ A」で走っていたスクリプトの挙動について考えよう。

f:id:todays_mitsui:20220214191042p:plain

ちなみにブラウザの「戻る」ボタンでページを戻ったときにも全く同じ議論が通じる。

 

TL;DR

  • ページ A で window.addEventListener('unload', ...) されているときは、JavaScript の状態はリセットされ再実行される
  • ページ A で window.addEventListener('unload', ...) されていないときは、JavaScript の状態はリセットされずに再開される
  • 下記の参考ページを読んで

nmi.jp

 

hidtory.back() 後に JavaScript の挙動おかしくなりがち

長年 hidtory.back() 後の JavaScript の挙動に悩まされてきた。

例えばあるとき、 Vue.js 製のフォームで hidtory.back() 後にフォームの入力がリセットされていることが問題になった。
しかも厄介なのは、この問題が再現する場合としない場合があった。

というわけで本腰入れて調べた結果、上記に挙げた参考サイトにたどり着いた。
ページ A に window.addEventListener('unload', ...) があるか否かによって hidtory.back() 後のスクリプトの挙動が変わる。

 

window.addEventListener('unload', ...) があるとき

hidtory.back() 後には JavaScript の状態がリセットされて、最初からスクリプトが再実行される。
Vue.js や React のような 要素を動的に作るライブラリ を使用している場合、状態をリセットして <input> タグを生成しなおすので入力内容は消えてしまう。

 

window.addEventListener('unload', ...) が無いとき

hidtory.back() 後に JavaScript の状態リセットはされず、ページ遷移した直後からスクリプトが再開するように見える。
<input> タグが生成された後の状態から再開するので入力内容は生きる。

 

ちなみに

window.addEventListener('unload', ...) ではなく、古き良き window.onunload = function() { ... } の形でリスナーを設定していても、同じ挙動になる。

 

誰が window.addEventListener('unload', ...) していたのか

個人の観測範囲では、 unload イベントにリスナーを設定することは滅多にしないことだ。
私の手元の環境では Facebook ピクセルwindow.addEventListener('unload', ...) していた。そのため Facebook 広告を配信しているか否かによって hidtory.back() 後の挙動が違って見えていた。

 

まとめ

挙動がまちまちだと問題になるが、挙動を均一化してしまえば何らかの対処ができると思う。
私の場合は問題がおこるページに window.addEventListener('unload', () => {}) を設定し、 window.addEventListener('unload', ...) があるときの挙動に寄せて対処した。

ほんと勘弁して。

 

私からは以上です。

フォント読み込み完了のタイミングで処理を実行する

ブラウザにフォントが読み込まれたタイミングで JavaScript の処理を実行したいと思ったとき、以前であればサードパーティー製のライブラリを使っていた *1 けど、最近はブラウザの標準 API だけで読み込みタイミングの判定ができるようだ。
Document.fonts を使う。

使用例

document.fonts.ready を参照すると Promise が取れる。

document.fonts.ready.then(function(fontFaceSet) {
  // フォント読み込み完了後に実行される
  console.info({ fontFaceSet });
});

ブラウザ対応

f:id:todays_mitsui:20220204201402p:plain
ブラウザ対応状況

Document API: fonts | Can I use... Support tables for HTML5, CSS3, etc

例によって IE11 ではサポートされていない。

IE11 の時にかぎって DOMContentLoaded イベントをリッスンすることで、それっぽい動きをさせることはできるだろう。

if ('fonts' in document) {
  document.fonts.ready.then(function(fontFaceSet) {
    // Do something.
  });
} else {
  // IE11 のとき
  window.addEventListener('DOMContentLoaded', (event) => {
     // Do something.
  });
}

まとめ

Document.fonts はブラウザの標準 API なのでサードパーティー製のライブラリを読み込むことなく即時に使えるのがいい。

というか、標準 API が Promise 返すことがあるのか。
Promise 自体が標準仕様に組み込まれたから妥当か。

 

私からは以上です。

*1:例えば Web Font Loader など