ブラウザの戻るボタンイベントを検出する方法-クロスブラウザ


218

ユーザーがブラウザの戻るボタンを押したかどうかを確実に検出するにはどうすればよいですか?

#URLシステムを使用して、単一ページのWebアプリケーション内でページ内戻るボタンの使用をどのように強制しますか?

ブラウザの戻るボタンで独自のイベントが発生しないのはなぜですか。


2
ナビゲーションイベント(後方/前方)がいつか追加されることを望みます。今のところ、これは作品にいるものですdeveloper.mozilla.org/en-US/docs/Web/API/...
llaaalu

回答:


184

(注:Sharkyのフィードバックに従って、バックスペースを検出するコードを含めました)

そのため、SOでこれらの質問を頻繁に確認し、最近、戻るボタンの機能を自分で制御する問題に遭遇しました。私のアプリケーションに最適なソリューション(ハッシュナビゲーションを使用した単一ページ)を数日間検索した後、戻るボタンを検出するための、ブラウザーを使用せず、ライブラリーを使用しないシンプルなシステムを思いつきました。

ほとんどの人は以下の使用を推奨します:

window.onhashchange = function() {
 //blah blah blah
}

ただし、この関数は、ユーザーが位置ハッシュを変更するページ内要素を使用するときにも呼び出されます。ユーザーがクリックしてページを前後に移動したときのユーザーエクスペリエンスは最適ではありません。

私のシステムの概要を説明するために、ユーザーがインターフェイスを移動するときに、配列を以前のハッシュで埋めます。次のようになります。

function updateHistory(curr) {
    window.location.lasthash.push(window.location.hash);
    window.location.hash = curr;
}

かなり簡単です。これを行うのは、クロスブラウザーのサポートと古いブラウザーのサポートを確実にするためです。新しいハッシュを関数に渡すだけで、そのハッシュが保存され、ハッシュが変更されます(これはブラウザーの履歴に保存されます)。

また、lasthash配列を使用してユーザーをページ間で移動するページ内戻るボタンを利用します。次のようになります。

function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}

したがって、これはユーザーを最後のハッシュに戻し、その最後のハッシュを配列から削除します(現在、転送ボタンはありません)。

そう。ユーザーがページはめ込み戻るボタンまたはブラウザボタンを使用したかどうかをどのように検出しますか?

最初はを調べましwindow.onbeforeunloadたが、役に立ちませんでした。これは、ユーザーがページを変更する場合にのみ呼び出されます。これは、ハッシュナビゲーションを使用する単一ページアプリケーションでは発生しません。

したがって、さらに掘り下げた後、フラグ変数を設定しようとする場合の推奨事項を確認しました。私の場合、これの問題は、設定しようとすることですが、すべてが非同期であるため、ハッシュ変更のifステートメントに合わせて常に設定されるとは限りません。.onMouseDownは常にクリックで呼び出されるわけではなく、onclickに追加しても、十分な速度でトリガーされることはありません。

これがdocument、との違いを見始めたときwindowです。私の最後の解決策はdocument.onmouseover、を使用してフラグを設定し、を使用してフラグを無効にすることdocument.onmouseleaveでした。

何が起こるかというと、ユーザーのマウスがドキュメント領域内にある間(読み取り:レンダリングされたページですが、ブラウザーフレームを除く)、ブール値はに設定されtrueます。マウスがドキュメント領域を離れると、ブール値はに変わりfalseます。

このようにして、次のように変更できますwindow.onhashchange

window.onhashchange = function() {
    if (window.innerDocClick) {
        window.innerDocClick = false;
    } else {
        if (window.location.hash != '#undefined') {
            goBack();
        } else {
            history.pushState("", document.title, window.location.pathname);
            location.reload();
        }
    }
}

のチェックに注意してください#undefined。これは、私の配列に利用可能な履歴がない場合、が返されるためundefinedです。私はこれを使用して、ユーザーにwindow.onbeforeunloadイベントの使用を終了するかどうかを尋ねます。

つまり、簡単に言えば、ページ内の戻るボタンや配列を使用して履歴を保存しているとは限らない人々のために:

document.onmouseover = function() {
    //User's mouse is inside the page.
    window.innerDocClick = true;
}

document.onmouseleave = function() {
    //User's mouse has left the page.
    window.innerDocClick = false;
}

window.onhashchange = function() {
    if (window.innerDocClick) {
        //Your own in-page mechanism triggered the hash change
    } else {
        //Browser back button was clicked
    }
}

そして、そこにあります。ハッシュナビゲーションに関して、戻るボタンの使用とページはめ込み要素を検出する簡単な3つの部分からなる方法。

編集:

ユーザーがバックスペースを使用してバックイベントをトリガーしないようにするために、以下を含めることもできます(この質問について@thetoolmanに感謝します)。

$(function(){
    /*
     * this swallows backspace keys on any non-input element.
     * stops backspace -> back
     */
    var rx = /INPUT|SELECT|TEXTAREA/i;

    $(document).bind("keydown keypress", function(e){
        if( e.which == 8 ){ // 8 == backspace
            if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
                e.preventDefault();
            }
        }
    });
});

5
+1いいアイデアですが、マウスがブラウザウィンドウ内にあるときにユーザーが「戻る」(Firefoxのバックスペースキー)のキーボードショートカットを使用すると失敗します
Sharky

3
やります-私はとにかく今オフィスにいます:)(編集:完了)
Xarus

7
モバイル(
iPadなど

5
MACのトラックパッドからイベントをスワイプするのはどうですか?それも捕まえることができますか?
Sriganesh Navaneethakrishnan 2015

6
進むボタンと戻るボタンをどのように区別できますか?
Mr_Perfect 16

79

popstateイベントハンドラを試すことができます。例:

window.addEventListener('popstate', function(event) {
    // The popstate event is fired each time when the current history entry changes.

    var r = confirm("You pressed a Back button! Are you sure?!");

    if (r == true) {
        // Call Back button programmatically as per user confirmation.
        history.back();
        // Uncomment below line to redirect to the previous page instead.
        // window.location = document.referrer // Note: IE11 is not supporting this.
    } else {
        // Stay on the current page.
        history.pushState(null, null, window.location.pathname);
    }

    history.pushState(null, null, window.location.pathname);

}, false);

注:最良の結果を得るには、他の予期しない問題を回避するためにロジックを実装する特定のページにのみこのコードをロードする必要があります。

popstateイベントは、現在の履歴エントリが変更されるたびに発生します(ユーザーが新しい状態に移動する)。これは、ブラウザの戻る/進むボタンまたはときにユーザーがクリックするときに発生history.back()history.forward()history.go()方法をプログラム的に呼ばれています。

event.stateイベントのisプロパティは、履歴状態オブジェクトと同じです。

jQuery構文の場合は、ラップしてください(ドキュメントの準備ができたらリスナーも追加します)。

(function($) {
  // Above code here.
})(jQuery);

参照:ページ読み込み時のwindow.onpopstate


シングルページアプリとHTML5 pushStateページの例もご覧ください。

<script>
// jQuery
$(window).on('popstate', function (e) {
    var state = e.originalEvent.state;
    if (state !== null) {
        //load content with ajax
    }
});

// Vanilla javascript
window.addEventListener('popstate', function (e) {
    var state = e.state;
    if (state !== null) {
        //load content with ajax
    }
});
</script>

これは、Chrome 5以降、Firefox 4以降、IE 10以降、Safari 6以降、Opera 11.5以降などと互換性があります。


2
ケノーブ、あなたはpopstateが変更を取得するために機能することは正しいですが、プログラムでイベントを呼び出すことと、ユーザーがブラウザーのボタンをクリックするときとを区別しません。
Xarus、

1
上の偉大なポストのシングルページアプリとHTML5 pushState。最新の「1ページレイアウト」または「シングルページアプリ」に最適です。リンクと回答をありがとう!
lowtechsun 2016年

1
最新のブラウザではe.stateは常に定義されていないようです
FAjir

ここで直接実行できるように、コードをスニペットに入れることをお勧めします。
FourCinnamon0

16

私はこの要件にかなり長い間取り組んでおり、上記のソリューションのいくつかを採用してそれを実装しました。しかし、私は観察に偶然出くわしました、そしてそれはChrome、FirefoxおよびSafariブラウザ+ AndroidおよびiPhoneで動作するようです

ページの読み込み時:

window.history.pushState({page: 1}, "", "");

window.onpopstate = function(event) {

  // "event" object seems to contain value only when the back button is clicked
  // and if the pop state event fires due to clicks on a button
  // or a link it comes up as "undefined" 

  if(event){
    // Code to handle back button or prevent from navigation
  }
  else{
    // Continue user action through link or button
  }
}

これで効果があるかどうかをお知らせください。何か足りない場合は、よろしくお願いします。


1
違います。event進むボタンにも価値がある
Daniel Birowsky Popeski

14

JavaScriptでは、ナビゲーションタイプ2はブラウザの[戻る]または[進む]ボタンがクリックされたことを意味し、ブラウザは実際にコンテンツをキャッシュから取得しています。

if(performance.navigation.type == 2)
{
    //Do your code here
}

1
これは単一ページのアプリケーションでは機能せず、結果は常に1です(つまり、リロードを意味します)。さらに、戻るボタンと進むボタンの違いはありません。これは非常に残念です。
パピヨン2018

「さらに、残念なことに、戻るボタンと進むボタンの間に違いはありません。」はい、データがキャッシュからフェッチされるときは常に、そのときにperformance.navigation.typeは2です
Hasan Badshah

すべてのブラウザでの動作が保証されているわけではありません。したがって、次のようなものを書く必要があります。
blank94

7

これは間違いなく機能します(戻るボタンのクリックを検出するため)

$(window).on('popstate', function(event) {
 alert("pop");
});

6

これを見てください:

history.pushState(null, null, location.href);
    window.onpopstate = function () {
        history.go(1);
    };

それはうまくいきます...


Chrome 78.0.3904.70(公式ビルド)(64ビット)では機能しません
Jeffz

Brave v1.3.118で動作確認済み>> Chromium:80.0.3987.116(公式ビルド)(64ビット)
ZeroThe2nd

外部サイトからページにアクセスした場合は機能しません。
Milan Vlach

5
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
  alert('hello world');
}

これが私にとって有効な唯一のソリューションです(1ページのWebサイトではありません)。Chrome、Firefox、Safariで動作します。


1
window.performance.navigationは非推奨になりました。docs developer.mozilla.org/en-US/docs/Web/API/Performance/navigation
llaaalu

これは私の.cshtmlページでうまくいきました。ChromeとIEで正常に動作しました。ありがとう
Justjyde

5

質問に答えるために、正解はすでにそこにあります。新しいJavaScript API PerformanceNavigationTimingについてお話ししたいと思います。これは、非推奨のperformance.navigationに代わるものです。

次のコードは、ユーザーが「戻る」または「進む」ボタンを使用してページに到達した場合に、コンソール「back_forward」にログインします。プロジェクトで使用する前に、互換性の表を確認してください。

var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
    console.log(perfEntries[i].type);
}

これは、このページで [戻る]ボタンがクリックされたかどうかを判断する方法ではありません。最後のページでクリックされたかどうかがわかります
Seph Reed

しかし、私が見つけた問題は、戻るボタンを押した後であって、重複する要求の問題を停止するいくつかの応答コードを組み込むことができる前ではないため、私が探していた実際の答えでした。ありがとう。
アンドリュー

1
これは質問に答えないかもしれませんが、まさに私が必要としたものです!いいですね
Hogsmill

4

ブラウザー:https : //jsfiddle.net/Limitlessisa/axt1Lqoz/

モバイルコントロールの場合:https : //jsfiddle.net/Limitlessisa/axt1Lqoz/show/

$(document).ready(function() {
  $('body').on('click touch', '#share', function(e) {
    $('.share').fadeIn();
  });
});

// geri butonunu yakalama
window.onhashchange = function(e) {
  var oldURL = e.oldURL.split('#')[1];
  var newURL = e.newURL.split('#')[1];

  if (oldURL == 'share') {
    $('.share').fadeOut();
    e.preventDefault();
    return false;
  }
  //console.log('old:'+oldURL+' new:'+newURL);
}
.share{position:fixed; display:none; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.8); color:white; padding:20px;
<!DOCTYPE html>
<html>

<head>
    <title>Back Button Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

</head>

<body style="text-align:center; padding:0;">
    <a href="#share" id="share">Share</a>
    <div class="share" style="">
        <h1>Test Page</h1>
        <p> Back button press please for control.</p>
    </div>
</body>

</html>


これは尋ねられた質問に答えません。問題は、ページ内の戻るボタンとブラウザのネイティブの戻るボタンの使用を検出することです。
Xarus、2016年

2
質問は、jqueryではなく、javascriptでタグ付けされています。
skwny 2017年

3

これが私の見解です。想定は、URLが変更されたが、document検出された内部にクリックがない場合、それはブラウザのバック(はい、またはフォワード)です。ユーザーのクリックは2秒後にリセットされ、Ajax経由でコンテンツをロードするページでこれが機能します。

(function(window, $) {
  var anyClick, consoleLog, debug, delay;
  delay = function(sec, func) {
    return setTimeout(func, sec * 1000);
  };
  debug = true;
  anyClick = false;
  consoleLog = function(type, message) {
    if (debug) {
      return console[type](message);
    }
  };
  $(window.document).click(function() {
    anyClick = true;
    consoleLog("info", "clicked");
    return delay(2, function() {
      consoleLog("info", "reset click state");
      return anyClick = false;
    });
  });
  return window.addEventListener("popstate", function(e) {
    if (anyClick !== true) {
      consoleLog("info", "Back clicked");
      return window.dataLayer.push({
        event: 'analyticsEvent',
        eventCategory: 'test',
        eventAction: 'test'
      });
    }
  });
})(window, jQuery);

2

私はこのスレッドのいくつかの回答とその他の回答を使用して、IEとChrome / Edgeで機能させることができました。私にとってのhistory.pushStateはIE11ではサポートされていませんでした。

if (history.pushState) {
    //Chrome and modern browsers
    history.pushState(null, document.title, location.href);
    window.addEventListener('popstate', function (event) {
        history.pushState(null, document.title, location.href);
    });
}
else {
    //IE
    history.forward();
}

Chrome 78.0.3904.70(公式ビルド)(64ビット)で試してみましたが、機能しませんでした。
ジェフズ

2

本格的なコンポーネントを実装できるのは、APIを再定義する場合(オブジェクトの履歴を変更する場合のみ)です。書いたばかりのクラスを共有します。ChromeとMozillaでテスト済みHTML5とECMAScript5-6のみをサポート

class HistoryNavigation {
    static init()
    {
        if(HistoryNavigation.is_init===true){
            return;
        }
        HistoryNavigation.is_init=true;

        let history_stack=[];
        let n=0;
        let  current_state={timestamp:Date.now()+n};
        n++;
        let init_HNState;
        if(history.state!==null){
            current_state=history.state.HNState;
            history_stack=history.state.HNState.history_stack;
            init_HNState=history.state.HNState;
        } else {
            init_HNState={timestamp:current_state.timestamp,history_stack};
        }
        let listenerPushState=function(params){
            params=Object.assign({state:null},params);
            params.state=params.state!==null?Object.assign({},params.state):{};
            let h_state={ timestamp:Date.now()+n};
            n++;
            let key = history_stack.indexOf(current_state.timestamp);
            key=key+1;
            history_stack.splice(key);
            history_stack.push(h_state.timestamp);
            h_state.history_stack=history_stack;
            params.state.HNState=h_state;
            current_state=h_state;
            return params;
        };
        let listenerReplaceState=function(params){
            params=Object.assign({state:null},params);
            params.state=params.state!==null?Object.assign({},params.state):null;
            let h_state=Object.assign({},current_state);
            h_state.history_stack=history_stack;
            params.state.HNState=h_state;
            return params;
        };
        let desc=Object.getOwnPropertyDescriptors(History.prototype);
        delete desc.constructor;
        Object.defineProperties(History.prototype,{

            replaceState:Object.assign({},desc.replaceState,{
                value:function(state,title,url){
                    let params={state,title,url};
                    HistoryNavigation.dispatchEvent('history.state.replace',params);
                    params=Object.assign({state,title,url},params);
                    params=listenerReplaceState(params);
                    desc.replaceState.value.call(this,params.state,params.title,params.url);
                }
            }),
            pushState:Object.assign({},desc.pushState,{
                value:function(state,title,url){
                    let params={state,title,url};
                    HistoryNavigation.dispatchEvent('history.state.push',params);
                    params=Object.assign({state,title,url},params);
                    params=listenerPushState(params);
                    return desc.pushState.value.call(this, params.state, params.title, params.url);
                }
            })
        });
        HistoryNavigation.addEventListener('popstate',function(event){
            let HNState;
            if(event.state==null){
                HNState=init_HNState;
            } else {
                HNState=event.state.HNState;
            }
            let key_prev=history_stack.indexOf(current_state.timestamp);
            let key_state=history_stack.indexOf(HNState.timestamp);
            let delta=key_state-key_prev;
            let params={delta,event,state:Object.assign({},event.state)};
            delete params.state.HNState;
            HNState.history_stack=history_stack;
            if(event.state!==null){
                event.state.HNState=HNState;
            }
            current_state=HNState;
            HistoryNavigation.dispatchEvent('history.go',params);
        });

    }
    static addEventListener(...arg)
    {
        window.addEventListener(...arg);
    }
    static removeEventListener(...arg)
    {
        window.removeEventListener(...arg);
    }
    static dispatchEvent(event,params)
    {
        if(!(event instanceof Event)){
            event=new Event(event,{cancelable:true});
        }
        event.params=params;
        window.dispatchEvent(event);
    };
}
HistoryNavigation.init();

// exemple

HistoryNavigation.addEventListener('popstate',function(event){
    console.log('Will not start because they blocked the work');
});
HistoryNavigation.addEventListener('history.go',function(event){
    event.params.event.stopImmediatePropagation();// blocked popstate listeners
    console.log(event.params);
    // back or forward - see event.params.delta

});
HistoryNavigation.addEventListener('history.state.push',function(event){
    console.log(event);
});
HistoryNavigation.addEventListener('history.state.replace',function(event){
    console.log(event);
});
history.pushState({h:'hello'},'','');
history.pushState({h:'hello2'},'','');
history.pushState({h:'hello3'},'','');
history.back();

    ```

素敵なアプローチ!これに追加していただきありがとうございます(何年も経ってからもこのスレッドでの会話があるのは驚くべきことです)
Xarus

これは巧妙な履歴追跡メカニズムのようです。ただし、コードはコメント化されていないため、理解するのはかなり困難です。誰かが現在のページから移動した場合、コードはそのボタンの押下を検出しません。これは、「ユーザーがブラウザーの戻るボタンを押したかどうかを明確に検出するにはどうすればよいですか?」という元の質問には答えません。ボタンを押すことと履歴の追跡は、一方が他方をトリガーする場合でも、2つの異なるものです。まだ会話があるという事実は、Webブラウザーの設計に大きな欠陥があることを示しています。
CubicleSoft

1

document.mouseoverはIEおよびFireFoxでは機能しません。しかし私はこれを試しました:

$(document).ready(function () {
  setInterval(function () {
    var $sample = $("body");
    if ($sample.is(":hover")) {
      window.innerDocClick = true;
    } else {
      window.innerDocClick = false;
    }
  });

});

window.onhashchange = function () {
  if (window.innerDocClick) {
    //Your own in-page mechanism triggered the hash change
  } else {
    //Browser back or forward button was pressed
  }
};

これはChromeとIEで機能し、FireFoxでは機能しません。FireFoxを正常に動作させるために引き続き作業しています。特にJQueryではなく、AngularJSまたはプレーンJavaScriptでも、ブラウザの戻る/進むボタンのクリックを検出する簡単な方法は歓迎されます。


1
 <input style="display:none" id="__pageLoaded" value=""/>


 $(document).ready(function () {
        if ($("#__pageLoaded").val() != 1) {

            $("#__pageLoaded").val(1);


        } else {
            shared.isBackLoad = true;
            $("#__pageLoaded").val(1);  

            // Call any function that handles your back event

        }
    });

上記のコードは私のために働いた。モバイルブラウザーでは、ユーザーが[戻る]ボタンをクリックしたときに、前回のアクセス時と同じようにページの状態を復元する必要がありました。


上記のコードは私にとってはモバイルブラウザで機能し、ユーザーが[戻る]ボタンをクリックしたときに、以前のアクセス
Ashwin

0

トリガーした元のイベントhashchange(スワイプ、クリック、ホイールなど)を追跡し、イベントが単純なページのランディングと間違われないようにし、追加のフラグを使用して解決しました各イベントバインディング。ブラウザはfalse、戻るボタンを押したときにフラグを再び設定しません。

var evt = null,
canGoBackToThePast = true;

$('#next-slide').on('click touch', function(e) {
    evt = e;
    canGobackToThePast = false;
    // your logic (remember to set the 'canGoBackToThePast' flag back to 'true' at the end of it)
}

-21

上記のオプションを試しましたが、どれもうまくいきませんでした。これが解決策です

if(window.event)
   {
        if(window.event.clientX < 40 && window.event.clientY < 0)
        {
            alert("Browser back button is clicked...");
        }
        else
        {
            alert("Browser refresh button is clicked...");
        }
    }

詳細については、このリンクhttp://www.codeproject.com/Articles/696526/Solution-to-Browser-Back-Button-Click-Event-Handliを参照してください


8
@MunamYousufこのコードが実際に機能しない理由は明らかです。ブラウザの戻るボタンを追跡しますが、ボタンはビューポートの外にあるため、clientXとclientYの座標は取得できません。それが機能すると仮定すると、iOSはどうですか?戻るボタンは下にあります...また、それが書かれた方法は非常に非効率的です、それはトリガーされるすべての単一のイベントに対して条件付きです...
James Wong-モニカを

これは恐ろしいです!なんでそんなことをするの?:/そして、ジェームズが言ったようにそれは機能しません。
msqar 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.