超メモ帳(Web式)@復活

小説書いたり、絵を描いたり、プログラムやったりするブログ。統失プログラマ。


SPONSORED LINK

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

f:id:yuki_2021:20191106212443j:plain

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


なんか、この日の徒然でjavascriptの処理を作るのに苦しんでる旨書いてたじゃないですか?


www.ituki-yu2.net


スマホページで戻るボタンを押したときに確認処理をポップアップさせて、フォームを書きかけで離脱しないように防ぐ処理だ。


随分と苦労したが、一応形をつくることができたので、情報共有の意味で公開しておこう。


<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">&times;</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ページ増やしてそこで待ち受けて処理を発火させるしかできないのである。


unitopi.com


また、注意点としてはスマホのブラウザではpopstateやhashchangeをトリガーにして動かす処理ではalertやconfirmは動作しません。ここでも随分と詰まりました。なのでbootstrapのmodalを使って警告文を出力しています。


teratail.com


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


qiita.com


一応動作確認はしていますけど、iossafarifirefox、edge。androidchromeで動作します。


まだまだ課題があるソースなので、ios chromeなどでの対応方法などアドバイスがあればご連絡お願いしますm(_ _)m

追記 2019/11/11


iosChromeでもなんとか動かす方法を探しました。history.jsを使えば対応できるようです。


github.com


history.jsは古いブラウザではpushStateやpopstateが対応していないのでそれを使えるようにするためにHistoryライブラリをオーバライドするライブラリです。これをつかえばひとまずiosChromeでも上記とおんなじような処理で離脱防止処理を作れます。

<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>

今日の徒然



ほーんとさ、今日の朝は会社行く前に筋トレしにジムに行く予定だったんだけど、目覚ましがなってるのに無意識で弄って止めたらしくて、起きたらとっくに会社行く準備の時間まで寝てた。多分、前日、午前1時まで起きてたからそのせいだと思う。その埋め合わせに明日ジム行く予定だから今日はさっさと寝ようと思う。


会社でのタスクも微妙に修羅場っててメンタル低飛行だから明日こそはジムに行ってストレス解消してこようと思う。というわけで、今日はさっさと英語勉強して寝る。

プライバシーポリシー免責事項