すごくピンポイントな内容ですが
とあるWebサービス(スマホ)の案件にて
**「モーダル表示時に、ユーザがブラウザバックしてしまうのを禁止したい、もしくはブラウザバックしてもモーダルが消えるだけで遷移しないようにしたい」**という要望がありいろいろ調査したので備忘録。
##仕組み
結論から言うと、ブラウザバックを実行した!というイベントは取れない。
なのでブラウザバック自体をdisableみたいな事は出来ない。
なので、ブラウザバック実行時に発生するイベントを取得可能なので、そちらで対応するやり方が一般的っぽい。
どうやら履歴というのは、ブラウザのHistoryというスタックの仕組みで動いている。
そのHistoryさんのAPI(下記参考サイト参照)は、追加(push)や変更(replace)は出来るようだが、取り出し(pop)や削除(delete)は出来ない模様。
そしてブラウザバックが実行された際に、Historyからpopする際のイベントが取れる。
それがよく「ブラウザバック禁止」とかでぐぐると出てくる下記サンプルのようなソースの概要のようだ。
window.addEventListener("popstate", function() {
// ごにょごにょ
});
##safariのスワイプでのブラウザバックにハマる(失敗したやり方)
// ①遷移時にhistoryへ履歴を追加
history.pushState(null, null, null);
// popのイベント時(ブラウザバック時)に
window.addEventListener("popstate", function() {
// 呼ばれたタイミングで①で追加した履歴が読まれている模様( = 現在のページへ遷移)
// ②このままだと①が消化されている状態なので
// 2回目以降のブラウザバック用にこのタイミングでもう1個追加する
history.pushState(null, null, null);
// ③モーダルを閉じる処理
// ごにょごにょ
// ④モーダルが開いてなかった場合はそのまま前画面へ遷移させたいが
// ②で1個追加しているので2個分バックさせる
(!modal_flg) && history.go(-2);
});
※書きながら思ったのだが④の後ろに②持ってきて、④でhistory.go(-1)ってしても良いような。でもそんなにやる事変わらないからまぁいっかと思ったり。
このやり方で概ね良い感じに動く。
が、スマホ実機端末のsafariには画面左端からスワイプでブラウザバック出来、しかも(脱獄しない限り)禁止出来ないという凶悪な機能がある。
前画面をA
現在の画面(モーダルを開いている画面)をB
とすると、モーダルからスワイプをしようとした際にチラ見えする画面がAになり、さらにシュッと実際にスワイプでのブラウザバックを実行すると、一瞬Aが表示されてからBに戻ってくる、という挙動になる。
前述のようにこのスワイプは禁止出来ない。
詰んだ\(^o^)/
##解決策
とあるサイトを見ていたときにインスパイアされて解決した。
そう、パクろうとしたけどフロント側のフレームワーク違うしコードも難読化されててきつかったから渋々挙動を分析して苦戦しながらも実装したとかそういう事ではなく、あくまでもインスピレーションを受けただけなのだ。
やった事は3つ
①現在の画面にページ内遷移情報を付与
②モーダル表示時にpushstateで現在の画面URLに別の遷移情報を付与
③popstateの処理ではモーダルを閉じる処理だけ
元の画面URLが「https://xxx.com/list」だとすると
①遷移時のURLを「https://xxx.com/list#aaa」にして
②モーダル表示時にpushstate(null,null,"https://xxx.com/list#bbb")で履歴追加
という感じになる。
③も
// popのイベント時(ブラウザバック時)に
window.addEventListener("popstate", function() {
// モーダルを閉じる処理
// ごにょごにょ
});
これだけ。
なんだか狐につままれたような感じだが…
同一URLではリロードが走ってしまうが、ページ内遷移にする事でリロードも無し。
スワイプ時にチラ見えする画面もモーダル元の画面になっている。
よし。