ショーケース・ティービーではいくつかのASPサービスをご提供させていただいているわけですが、ウェブサイトのトレンドに合わせて、各商品で大小様々なアップデートを実施し、そしてその時々のカスタマイズ対応を繰り返してご活用いただいております。
今回は久々に個人的に結構手こずった印象のあるカスタマイズ内容についてご紹介させていただきます。
まず要望はJavascriptだけで、ブラウザバックボタンを押した時に確認メッセージを表示させ、ユーザが了承することで初めて、本来のブラウザバックを起動させたいというものです。
つまりユーザにブラウザバックを思いとどまらせるための機能実装を要望されているということですね。
動作確認の対象
今回のカスタマイズ対象にしたのはAndroid端末、かつChromeブラウザです。なお、対象の識別はユーザエージェントで判断しました。ちなみに標準ブラウザでは確認メッセージを表示させなくとも良いとのこと。
調査したところ、標準ブラウザとして判断できるトリガーは以下の3点でした。
本来であればAndroid端末、かつChromeブラウザだけを対象とする、いわゆる “ホワイトリスト方式” を採用し、以下のような書き方でカスタマイズ対応ができれば良かったのですが……
1 2 3 |
if(navigator.userAgent.indexOf('Android') != -1 && navigator.userAgent.indexOf('Chrome') != -1){ ここに実行したい内容を記載 } |
下記の「標準ブラウザの例」を見る限りでは、なんと “Chrome” という文字列が入っていました。そのため、標準ブラウザとして判断できるトリガーで、さらにユーザエージェントを絞り込む必要が出てきたわけです。
標準ブラウザの例:
Mozilla/5.0 (Linux; U; Android 4.0.3; ja-jp; SC-02C Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
Mozilla/5.0 (Linux; Android 4.2.2; ja-jp; SC-04E Build/JDQ39) AppleWebkit/535.19 (KHTML,like Gecko) Version/1.0 Chrome/18.0.1025.308 Mobile Safari/535.19
Mozilla/5.0 (Linux; Android 6.0.1; SCV33 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36
なお、なぜ上記の「標準ブラウザの例」が3点もあるのかというと、厄介なことに「Linux; U;」が入っていると「Version」の識別ができない端末があったためです。当初は「Version」と「SamsungBrowser」だけで判断できると高をくくっていたのですが……。
ちなみにこの原因は未だに不明です。誰かご存知の方がいらっしゃるのであれば教えて欲しいです……。私は読み込みタイミングの問題なのかと予想はしているのですが。。。
紆余曲折はありましたが、今回は「Linux; U;」「Version」「SamsungBrowser」の3点で判断するようにしました。
また、大文字小文字が混在していることもあったので、最終的に以下のような書き方になりました。
1 2 3 |
if(navigator.userAgent.toLowerCase().indexOf('linux; u;') == -1 || navigator.userAgent.toLowerCase().indexOf('Version') == -1 || navigator.userAgent.toLowerCase().indexOf('SamsungBrowser') == -1){ ここに実行したい内容を記載 } |
ユーザエージェントの切り分けに悩まされるのは毎度のことなのですが、まさかここまで手こずるとは…。
さて、気を取り直して次に進めます。
「戻る」ボタンのカスタマイズって簡単なの?
下記のページを参考に、ブラウザの「history操作」と「pushState」と「WindowEventHandlers.onbeforeunload」の組み合わせでなんとかできるんじゃないかな、というイメージで考えました。
■参考サイト
<MOZILLA DEVELOPER NETWORK『Manipulating the browser history』>
<MOZILLA DEVELOPER NETWORK『WindowEventHandlers.onbeforeunload』>
なお、手順は以下のようなフローで対処していこうと思います。
- 戻るボタンを無効化(pushState)
- 戻るボタン押下(popState)で確認メッセージを表示するイベント発動(onbeforeunload)
- 了承で前ページへ移動(history)、留まるで現在のページに戻る
『history.pushState(state, title, url)』でURLを操作する
<pushStateとは>
「pushState」とは、HTML5から追加されたHistory APIのメソッドであり、ブラウザ履歴に指定したURLを追加できるというものです。なお、「pushState」の引数を指定して使用することになります。
ここで引数のご説明も併せてご紹介いたします。
・state
履歴に関連付けする任意のオブジェクトを渡すことができ、そのオブジェクトは「popstate」イベントハンドラから参照することができます。……とは書いたものの、かなり分かりづらい。。。
カンタンに説明すると、ユーザがブラウザ「戻る」ボタンなどを使用すると、履歴のスタックがpop(「popstate」イベント発動)されます。この時、イベントハンドラに渡される「state」プロパティに入ってくるのが、この引数になります。この引数を利用することで、以前の状態を復元させることができるようになる、というものです。
・title
履歴のタイトルを指定できるというものです。しかし、現在どのブラウザでも使用されていないです。
・url
履歴に指定したURLを追加するものです。これの特徴としては、現在のURLと異なるURLを指定しても、ページのリロードは発生しません。nullを入れた場合は自分自身のurlが入ります。
<「戻る」ボタンの無効化をするには”null”>
「history.pushState(null, null, null);」のように記述すれば、ブラウザの「戻る」ボタンは本来の機能を失います。つまり、自分自身を現在の1つ前のページ(履歴)にストックするという仕組みです。
「戻る」ボタンを押した時にイベントを発動させる方法
では、本来のブラウザバック機能を失った「戻る」ボタンに新たな機能を負荷していきましょう。
「戻る」ボタンを押した時にイベントを発動させるには、「popState」を使います。「addEventListener」を用いてイベントハンドラを登録します。
■参考サイト
<MOZILLA DEVELOPER NETWORK『popstate』>
なお、記述をする場合は以下のような感じになります。
1 2 3 |
window.addEventListener("popstate", function() { ここにイベント内容を記述 } |
確認メッセージを表示するイベント
次は「このページを離れますか?」というあのメッセージをダイアログで表示させていきましょう。
■参考サイト
<MOZILLA DEVELOPER NETWORK『WindowEventHandlers.onbeforeunload』>
ちなみに、Chromeの仕様でバージョン51以降はこの文言で統一されており、「return」の後に出したい文言を入れても変わりません。
なお、書き方は以下のような感じになります。
1 2 3 |
window.onbeforeunload = function(e) { return 'このページから離れますか?'; }; |
今回はAndroid端末のChromeブラウザのみの対応が大前提ですが、IEに限りこの「return」内の文言が反映されます。また、仮にChromeであったとしても、バージョンが51以前の場合、「return」で空っぽを返したとしてもダイアログ自体が表示されませんでした。そのため、「return」の中には古いバージョン対策のために、表示したい文言を入れています。
了承したら前のページに移動させる
ここまでで1つ前の履歴を追加して、「戻る」ボタンを無効化させています。
つまり、もう1つ前の履歴には、本来「戻る」ボタンを押した時に表示されるページがストックされています。よって、了承したら「history」をさらに1つ戻すという記述を追加してあげればいいわけです。
1 |
history.go(-1); |
これだけです。
これって「history.back()」と同じじゃん! と思って記述したら、どういう理由でかうまくいかなかったのです。
不思議……。
完成した記述がコチラ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// AndroidのChromeブラウザか確認 if(navigator.userAgent.toLowerCase().indexOf('linux; u;') == -1 || navigator.userAgent.toLowerCase().indexOf('Version') == -1 || navigator.userAgent.toLowerCase().indexOf('SamsungBrowser') == -1){ // History APIが使用可能ブラウザか確認 if(history && history.pushState && history.state != undefined){ // ブラウザの履歴に戻る無効を追加 history.pushState(null, null, null); // 戻るボタン押下でイベント発動 window.addEventListener("popstate", function() { // 確認メッセージ表示 window.onbeforeunload = function(e) { return 'このページから離れますか?'; }; // このページを離れるを押した場合さらに1つ履歴を戻る // (通常のブラウザバックと同じ挙動) // ページを離れない場合は再度ブラウザ戻るボタンを押した時用に // 履歴無効を追加 history.go(-1); history.pushState(null, null, null); }); } } |
なんとなく腑に落ちない疑問点がいくつかありましたが、未だ分からずモヤモヤ中。
とはいえ、上記記述で挙動に問題がなく、要件をすべて満たせていることを確認しましたので、今回はこれで実装完了としました。
またカスタマイズや調査で個人的に大変だったことを共有していければと思います。
それでは、また!