スマホ向けブラウザで戻る処理をトリガーにしてフォーム離脱防止処理を作った。

スマホ向けブラウザで戻る処理をトリガーにしてフォーム離脱防止処理を作った。
なんか、この日の徒然でjavascriptの処理を作るのに苦しんでる旨書いてたじゃないですか?

スマホページで戻るボタンを押したときに確認処理をポップアップさせて、フォームを書きかけで離脱しないように防ぐ処理だ。
随分と苦労したが、一応形をつくることができたので、情報共有の意味で公開しておこう。
<html lang="ja">
<head>
<title>test page</title>
<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet" async="">
</head>
<body>
<style>
.modal-dialog {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 100%;
margin: auto 15px;
pointer-events: none;
}
.modal-content {
pointer-events: all;
}
</style>
<form>
<input type="text" />
</form>
<!-- モーダルのパーツ -->
<div class="modal fade" id="modal1" tabindex="-1"
role="dialog" aria-labelledby="label1" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
入力途中ですがキャンセルしてよろしいですか?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">いいえ</button>
<a href="#" id="back_button" class="btn btn-primary">はい</a>
</div>
</div>
</div>
</div>
<script src="/js/jquery-3.3.1.min.js"></script>
<script src="/bootstrap/js/bootstrap.min.js"></script>
<script>
$(function() {
var ua = navigator.userAgent;
if((ua.indexOf('iPhone') !== -1) && (ua.indexOf("Chrome") !== -1 || ua.indexOf("CriOS") !== -1)) {
// iPhoneのChromeは対応する処理がない
} else {
var hash = location.hash;
if(hash != '#message') {
// pushStateで現在のURLを履歴にスタックする
history.pushState(null,null,location.href);
// URLにtagを設置する
location.hash = '#message';
}
// URLが変更されるのを監視
$(window).on('popstate',function(event){
if(location.hash != '#message') {
// モーダルを実行
$('#modal1').modal('show');
}
});
}
});
// 戻るボタン用
$('#back_button').click(function() {
history.back();
return false;
});
</script>
</body>
</html>
処理的には一旦ページがロードされると、#messageのタグを付けてpushStateで履歴をスタックする。popstateでurlが変更されたときに処理を発火させて、戻るで#messageが消えたときにモーダル発動。その中でページを戻るかそのページにとどまるかを選択。
なんでこんなにめんどくさい方法を使ってるか訝る人もいるだろう。PCのモダンブラウザだと以下の処理を使うだけで同様の機能を作ることができる。スマホのブラウザは、ページを閉じるときに発火するbeforeunloadのトリガーが使えないのである。
<script>
$(function(){
var loc=false;
$(window).bind("beforeunload", function(e) {
// 確認メッセージに表示させたい文字列
if (!loc) {
return "入力は完了していません。";
}
});
$("form").submit(function(){loc=true;});
});
</script>
なので一度、スマホのブラウザではタグを利用して仮想的に1ページ増やしてそこで待ち受けて処理を発火させるしかできないのである。
また、注意点としてはスマホのブラウザではpopstateやhashchangeをトリガーにして動かす処理ではalertやconfirmは動作しません。ここでも随分と詰まりました。なのでbootstrapのmodalを使って警告文を出力しています。

あと、なぜかios版のChromeだけhistory.pushStateが使えないように挙動が変更されているので、今回作った処理はios Chromeでは動作しません。どうしても同じ処理をios chromeで作りたいなら、javascriptだけでは無理で、サーバ上に別のページを用意してそこで待ち受けて分岐させるぐらいしか方法が思いつきません・・・。

一応動作確認はしていますけど、iosはsafari、firefox、edge。androidはchromeで動作します。
まだまだ課題があるソースなので、ios chromeなどでの対応方法などアドバイスがあればご連絡お願いしますm(_ _)m
追記 2019/11/11
ios版Chromeでもなんとか動かす方法を探しました。history.jsを使えば対応できるようです。
history.jsは古いブラウザではpushStateやpopstateが対応していないのでそれを使えるようにするためにHistoryライブラリをオーバライドするライブラリです。これをつかえばひとまずios版Chromeでも上記とおんなじような処理で離脱防止処理を作れます。
<script src="/js/history.js/scripts/bundled/html4+html5/jquery.history.js"></script>
<script>
$(function() {
var hash = location.hash;
if(hash != '#message') {
history.pushState(null,null,location.href);
history.replaceState(null,null,'#message');
}
$(window).on('hashchange',function(event){
if(location.hash != '#message') {
$('#modal1').modal('show');
}
});
});
$('#back_button').click(function() {
history.back(-1);
return false;
});
</script>
今日の徒然
おはよう諸君! 今日は朝は筋トレ行こうとしたら、無意識で目覚ましを止めていて行けなかった。ほんと無意識で何が起きてるのか理解も不能。明日、今日の替わりに行ってくる。
— ゆきにー@雑文書き (@yuki_20211) 2019年11月6日
ほーんとさ、今日の朝は会社行く前に筋トレしにジムに行く予定だったんだけど、目覚ましがなってるのに無意識で弄って止めたらしくて、起きたらとっくに会社行く準備の時間まで寝てた。多分、前日、午前1時まで起きてたからそのせいだと思う。その埋め合わせに明日ジム行く予定だから今日はさっさと寝ようと思う。
会社でのタスクも微妙に修羅場っててメンタル低飛行だから明日こそはジムに行ってストレス解消してこようと思う。というわけで、今日はさっさと英語勉強して寝る。
あわせてよみたい


