ブラウザの戻るボタンで戻る場所を変えたい
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ replaceState/pushState でがんばる
1a.html には 2 つのリンクがあってどちらも遷移先は 2.html です
1 つめのリンクを押したときは 普通に 2.html に移動して戻るボタンで 1a.html に戻ります
2 つめのリンクを押したときは 2.html に移動しますが 戻るボタンを押すと 1b.html に移動させたいです
[1a.html]
[1b.html]
[2.html]
btn2 を押したときに replaceState で今の URL を戻りたい URL に書き換えます
location.replace と違って ページ遷移とかはなく単純に URL バーの表示を置き換えるだけです
1a.html から 2.html を開いたわけですが ブラウザの履歴には 1b.html から 2.html を開いたと記録されます
ページ遷移する前に元のページの URL が変わっているので ブラウザの戻るを押したときにブラウザは書き換えられた URL を開こうとします
これで 1b.html に戻ってこれますね
書き換え後すぐに a タグのページ遷移が起きるので変わったことには通常気がつかないと思います
historyAPI では 今の state を置き換えたり 追加することは可能ですが これまでの遷移履歴を変更することはできません
なので 今のページが開かれた時点で前のページというのは確定しています
そうなるとできることは ブラウザバックを検出して目的のページに遷移させることです
一つ前のページは履歴に残り続けますが これはどうしようもないので諦めます
また ページ遷移させるときに 戻るを押したのにページ遷移してしまっては 履歴が増える上に 進むボタンで最初にページ遷移したページに移動できなくなります
そうならないようにちょっと工夫します
目的は
「1a.html が 2.html を開いて 2.html の処理で ブラウザバックすると 1b.html を開くようにする」
ということ
最終的に履歴が
となるようにします
[2.html]
2.html が開かれたときの履歴は
こうなっています
ここで pushState を使って 2.html をもうひとつ履歴に追加します
また replaceState で 1b.html に置き換えるという情報を付与しておきます
ブラウザバックが実行された時のイベントで 1b.html に置き換えるという情報を受け取って location.replace をします
これで 2.html がロード読み込まれたとき
戻るを押した時
となります
別タブなのでもう履歴や戻るボタンは関係ないです
自身を location.replace で置き換えるだけです
[1a.html]
[2.html]
開く側で noopener をつけていると opener が使えないのでつけないようにする必要があります
1 つめのリンクを押したときは 普通に 2.html に移動して戻るボタンで 1a.html に戻ります
2 つめのリンクを押したときは 2.html に移動しますが 戻るボタンを押すと 1b.html に移動させたいです
[1a.html]
<!doctype html>
<h1>page 1a</h1>
<a id="btn1" href="2.html">button 1</a>
<br />
<a id="btn2" href="2.html">button 2</a>
<h1>page 1a</h1>
<a id="btn1" href="2.html">button 1</a>
<br />
<a id="btn2" href="2.html">button 2</a>
[1b.html]
<!doctype html>
<h1>page 1b</h1>
<h1>page 1b</h1>
[2.html]
<!doctype html>
<h1>page 2</h1>
<h1>page 2</h1>
replaceState
一見無理そうにも思えますが SPA アプリケーションでよく使われる historyAPI を使えば簡単にできますbtn2 を押したときに replaceState で今の URL を戻りたい URL に書き換えます
document.getElementById("btn2").addEventListener("click", eve => {
history.replaceState({}, "", "1b.html")
})
history.replaceState({}, "", "1b.html")
})
location.replace と違って ページ遷移とかはなく単純に URL バーの表示を置き換えるだけです
1a.html から 2.html を開いたわけですが ブラウザの履歴には 1b.html から 2.html を開いたと記録されます
ページ遷移する前に元のページの URL が変わっているので ブラウザの戻るを押したときにブラウザは書き換えられた URL を開こうとします
これで 1b.html に戻ってこれますね
書き換え後すぐに a タグのページ遷移が起きるので変わったことには通常気がつかないと思います
遷移後のページで戻り先を変える
ページを開くときに戻り先を置き換えるのは簡単でしたが 開かれたページでその制御するのはちょっとむずかしいですhistoryAPI では 今の state を置き換えたり 追加することは可能ですが これまでの遷移履歴を変更することはできません
なので 今のページが開かれた時点で前のページというのは確定しています
そうなるとできることは ブラウザバックを検出して目的のページに遷移させることです
一つ前のページは履歴に残り続けますが これはどうしようもないので諦めます
また ページ遷移させるときに 戻るを押したのにページ遷移してしまっては 履歴が増える上に 進むボタンで最初にページ遷移したページに移動できなくなります
そうならないようにちょっと工夫します
目的は
「1a.html が 2.html を開いて 2.html の処理で ブラウザバックすると 1b.html を開くようにする」
ということ
最終的に履歴が
1a.html --> 1b.html --> 2.html
となるようにします
[2.html]
<!doctype html>
<h1>page 2</h1>
<script>
history.replaceState({location_replace: "1b.html"}, "")
history.pushState({}, "", "2.html")
window.addEventListener("popstate", eve => {
if(eve.state && eve.state.location_replace){
location.replace(eve.state.location_replace)
}
})
</script>
<h1>page 2</h1>
<script>
history.replaceState({location_replace: "1b.html"}, "")
history.pushState({}, "", "2.html")
window.addEventListener("popstate", eve => {
if(eve.state && eve.state.location_replace){
location.replace(eve.state.location_replace)
}
})
</script>
2.html が開かれたときの履歴は
1a.html --> 2.html
こうなっています
ここで pushState を使って 2.html をもうひとつ履歴に追加します
また replaceState で 1b.html に置き換えるという情報を付与しておきます
ブラウザバックが実行された時のイベントで 1b.html に置き換えるという情報を受け取って location.replace をします
これで 2.html がロード読み込まれたとき
1a.html
↓
2.html (ここに戻ってきたら 1b.html に置き換え)
↓
2.html ◀ いまここ
↓
2.html (ここに戻ってきたら 1b.html に置き換え)
↓
2.html ◀ いまここ
戻るを押した時
1a.html
↓
1b.html ◀ いまここ
↓
2.html
↓
1b.html ◀ いまここ
↓
2.html
となります
target="_blank" の場合
ついでに target="_blank" で別タブで開く場合も考えてみます別タブなのでもう履歴や戻るボタンは関係ないです
開く元で制御する場合
開く元で制御する場合は単純です自身を location.replace で置き換えるだけです
[1a.html]
document.getElementById("btn2").addEventListener("click", eve => {
location.replace("1b.html")
})
location.replace("1b.html")
})
開かれた側で制御する場合
開かれた側で開いた window を取得するには window.opener を使います[2.html]
window.opener.location.replace("1b.html")
開く側で noopener をつけていると opener が使えないのでつけないようにする必要があります