ヒストリバック禁止が特定ブラウザでうまく動作しない原因を調べた。
ブラウザには「ヒストリバック」と呼ばれる、前の画面に戻る機能があります。
そして、これを禁止する以下の手法があります。
1 2 3 4 5 6 7 8 9 |
$(document).ready(function() { history.pushState(null, null, null); $(window).on("popstate", function() { history.go(1); }); }); |
ところが、これがうまくいかないケースがありました。
なんでそうなるのか調べてみたところ、様々な要因がありました。
忘備録として記録いたします。
現象が発生するのはchromeとEdge
こちらのサンプルをchrome、Edgeで開いていただき、緑色のボタンを押した後ヒストリバックすると、ヒストリバック禁止処理が入っているにも関わらずヒストリバックしてしまいます。
ちなみにfirefoxでやると、ヒストリバック禁止になりました。
ヒストリバック禁止は、ヒストリバック実行をトリガーとして発火します。
ところがブラウザによっては発火しないことがあるようです。
原因1:バックフォワードキャッシュ
chromeで現象を調べてみたところ、コンソールに次のような記述を見つけました。
バックフォワードキャッシュって何?
と思って調べてみたところ、次のようなことがわかりました。
- 2021年11月にchromeに導入されました。
- ブラウザ高速化処理のひとつ
- ひとつ前の画面を完コピキャッシュし、ヒストリバックで一つ前の状態をそのまま表示する
通常のヒストリバックは、履歴から前のページへジャンプしますが、バックフォワードキャッシュは、保存されたページを復元する処理なので、通常のヒストリバック処理は動作しません。
これがヒストリバック禁止が動作しない原因になっていました。
ちなみに、バックフォワードキャッシュが実装されていないブラウザでは発生しません。
バックフォワードキャッシュを一時的に無効化する方法がありました
呼び出し元に次の記述をすることで、バックフォワードキャッシュを一時的に無効化することができました。
1 2 |
window.addEventListener("unload", function() {}); window.addEventListener("pageshow", function evt(){ if (evt.persisted) {location.reload(); }}); |
ヒストリバック禁止処理側に入れる記述するのではなく、ヒストリバック禁止処理の入ったページを呼び出す側に記述します。
“unload”ハンドラを使用するとバックフォワードキャッシュが動作しなくなる特性を利用し、”unload”を実行しています。
ただ、一部のブラウザでうまく動作しないことがあるため、バックフォワードキャッシュで表示された場合、ページをリロードし”unload”が確実に実行されるようにしています。
これで大丈夫と思いましたが、落とし穴がありました。
chromeのコンソールログには、「バックフォワードキャッシュからの復元」と表示されなくなりましたが、それでもヒストリバック禁止が動作しないようでした。
調べてみると、次のことが原因でした。
chromeでは1度画面クリックしないとヒストリバックが動作を認識できない
理屈はよくわからないのですが、chromeやEdgeではヒストリバック禁止ページを開いた後、ブラウザに表示されている画面を1度でもクリックすれば、ヒストリバック禁止処理が動作するようになりました。
先ほどのサンプルも、緑ボタンを押した後に表示される「ブラウザのヒストリバックで戻ってください。」と表示されるページで、画面のどこでもよいから一度クリックすると、ヒストリバック禁止処理がどうさしました。
本当に謎理屈ですが、そういうもののようです。
ただ、画面を1度クリックすれば解決できるということが分かったので、javascriptで画面を触るということができないかやってみましたがうまく行きませんでした。
そこで奥の手で無理やりヒストリバック禁止と同等の動きをするようにしてみました。
上記のサンプルは、力ずくで仮想ヒストリバックををやってみました。
不格好ですが、確かに戻ることはできません。
理屈は、ヒストリバックで戻ってきた場合は強制的に元のページへジャンプするとい記述をいれました。
1 2 3 4 |
if (window.performance.navigation.type == 2) { history.forward(); } |
一瞬呼び出し元のページが表示されますが、すぐにページが戻ります。
なので、次の3つの処理を入れることで、力ずくでもヒストリバック禁止処理ができました。
- 従来のヒストリバック禁止処理をいれる
- リンク元ページにバックフォワードキャッシュ禁止処理をいれる
- リンク元ページにヒストリバックで戻った場合、ヒストリバックしたページへ戻る処理をいれる
3重の対策を入れることで何が何でもヒストリバックをガードできました。
サンプルソースはこちらです。
リンク呼び出し元ページサンプル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<!doctype html> <html> <head> <meta charset="shift_jis"> <title>リンク元</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> $(document).ready(function() { if (window.performance.navigation.type == 2) { history.forward(); } window.addEventListener("unload", function() {}); window.addEventListener("pageshow", function evt(){ if (evt.persisted) {location.reload(); }}); }); </script> <style type="text/css"> body { text-align: center; } a { display: inline-block; padding: 20px; color: #FFF; background-color: #6C3; text-decoration: none; } a:hover { background-color: #84D75B; } </style> </head> <body> <a href="history.html">ヒストリバックを禁止したページへジャンプします</a> </body> </html> |
ヒストリバック禁止処理を入れたページ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<!doctype html> <html> <head> <meta charset="shift_jis"> <title>ヒストリバック禁止</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> $(document).ready(function() { history.pushState(null, null, null); $(window).on("popstate", function() { history.go(1); }); }); </script> <style type="text/css"> body { text-align: center; } </style> </head> <body> <p>ブラウザのヒストリバックで戻ってください。</p> </body> </html> |
また本記事がお役に立てば幸いです。