web-dev-qa-db-ja.com

my AJAX applicationの[戻る]ボタンへの呼び出しをインターセプトする

AJAXアプリ。ユーザーがボタンをクリックすると、ページの表示が変更されます。元の状態に戻ることを期待して、戻るボタンをクリックしますが、前のページに移動します。ブラウザで。

戻るボタンイベントをインターセプトして再割り当てするにはどうすればよいですか?私はRSHのようなライブラリを調べました(これは機能しませんでした...)。

72
Zack Burt

ああ、戻るボタン。 「バック」がJavaScriptイベントを発生させると想像するかもしれません。

document.onHistoryGo = function() { return false; }

そうではありません。単にそのようなイベントはありません。

web app(いくつかのajaxy機能を備えたWebサイトとは対照的に)が本当にある場合は、戻るボタン(先ほど述べたURLのフラグメント)を引き継ぐのが妥当です。 Gmailはこれを行います。あなたのWebアプリがすべて1つのページにあり、すべてが自己完結している場合について話しています。

この手法は簡単です。ユーザーが物事を変更するアクションを実行するたびに、既にアクセスしている同じURLにリダイレクトしますが、ハッシュフラグメントは異なります。例えば。

window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";

Webアプリの全体的な状態がハッシュ可能な場合、これはハッシュを使用するのに最適な場所です。メールのリストがあり、すべてのIDと既読/未読のステータスを連結し、それをフラグメント識別子として使用してMD5ハッシュを取得するとします。

この種のリダイレクト(ハッシュのみ)はサーバーから何も取得しようとしませんが、ブラウザーの履歴リストにスロットを挿入します。したがって、上記の例では、ユーザーは「戻る」を押して、アドレスバーに#deleted_somethingを表示しています。彼らは再び反撃し、それらはまだあなたのページにありますが、ハッシュはありません。その後、再び戻って、彼らは実際に彼らがどこから来たとしても戻ってください。

ここで難しいのは、ユーザーが戻ってきたときにJavaScriptが検出するようにすることです(したがって、状態を元に戻すことができます)。あなたがすることは、ウィンドウの場所を見て、それがいつ変わるかを見るだけです。ポーリングあり。 (うん、ポーリングを知っている。まあ、今のところクロスブラウザより良いものはない。)ただし、それらが前に進んだのか後ろに戻ったのかを判断することはできないため、ハッシュ識別子を使用してクリエイティブを取得する必要があります。 (おそらく、シーケンス番号と連結されたハッシュ...)

これがコードの要点です。 (これはjQuery Historyプラグインの動作方法でもあります。)

var hash = window.location.hash;
setInterval(function(){
    if (window.location.hash != hash) {
        hash = window.location.hash;
        alert("User went back or forward to application state represented by " + hash);
    }
}, 100);
115
jpsimons

古い(しかし人気のある)質問に最新の回答を与えるには:

HTML5ではhistory.pushState()メソッドとhistory.replaceState()メソッドが導入され、それぞれ履歴エントリを追加および変更できます。これらのメソッドは、_window.onpopstate_イベントと連動して機能します。

history.pushState()を使用すると、状態の変更後に作成されたXMLHttpRequestオブジェクトのHTTPヘッダーで使用されるリファラーが変更されます。リファラーは、thisオブジェクトの作成時にウィンドウがXMLHttpRequestであるドキュメントのURLになります。

ソース: ブラウザ履歴の操作 Mozilla Developer Networkから。

49
gitaarik

JQueryを使用して、簡単なソリューションを作成しました。

$(window).on('hashchange', function() {
    top.location = '#main';
    // Eventually alert the user describing what happened
});

これまでのところ、GoogleでしかテストされていませんChrome.

これにより、AJAXベースのWebアプリの問題も解決しました。

それはおそらく少しハックっぽいです-しかし、私はそれをエレガントなハッキングと呼びます;-)後方にナビゲートしようとするたびに、技術的には後方にナビゲートしようとするハッシュ部分をURIにポップします。

ブラウザボタンとマウスボタンの両方のクリックをインターセプトします。また、1秒間に数回クリックすることによって後方にブルートフォースすることはできません。これは、setTimeoutまたはsetIntervalに基づいたソリューションで発生する問題です。

darkporter's answer の説明は本当にありがたいですが、 "hashchange" event を使用することで改善できると思います。 darkporterが説明したように、すべてのボタンがwindow.location.hash値を変更することを確認する必要があります。

  • 1つの方法は、<button>要素を使用し、それらにイベントを添付してwindow.location.hash = "#!somehashstring";を設定することです。
  • これを行う別の方法は、<a href="#!somehashstring">Button 1</a>のようなボタンのリンクを使用することです。これらのリンクがクリックされると、ハッシュは自動的に更新されます。

ハッシュ記号の後に感嘆符が付いているのは、Googleの「ハッシュバン」パラダイムを満たすためです( それについてもっと読む )。これは、検索エンジンでインデックスを作成する場合に便利です。ハッシュ文字列は通常、#!color=blue&shape=triangleのような名前/値のペアまたは#!/blue/triangleのようなリストになります-Webアプリにとって意味のあるものなら何でも。

次に、ハッシュ値が変更されるたびに起動するこのコードを追加するだけで済みます(戻るボタンが押されたときを含む)。ポーリングループは必要ないようです。

window.addEventListener("hashchange", function(){
    console.log("Hash changed to", window.location.hash);
    // .... Do your thing here...
});

Chrome 36以外はテストしていませんが、 caniuse.comによる 、これはIE8 +、FF21 +、Chrome21 +、および他のほとんどのブラウザで利用可能です。 Opera Mini。

8
Luke

以下のJQueryコードのように、ブラウザーの戻るボタンを無効にするのは非常に簡単です。

// History API 
if( window.history && window.history.pushState ){

  history.pushState( "nohb", null, "" );
  $(window).on( "popstate", function(event){
    if( !event.originalEvent.state ){
      history.pushState( "nohb", null, "" );
      return;
    }
  });
}

あなたはそれが動作していること、そしてより多くの例をここで見ることができます dotnsf そしてここで thecssninja

ありがとう!

プライバシーの問題により、戻るボタンを無効にしたり、ユーザーの履歴を調べたりすることはできませんが、ページを変更せずにこの履歴に新しいエントリを作成することは可能です。 AJAXアプリケーションが状態を変更するたびに、top.locationの新しい RIフラグメント

top.location = "#new-application-state";

これにより、ブラウザの履歴スタックに新しいエントリが作成されます。多数のAJAXライブラリは Really Simple History などの退屈な詳細をすべて処理済みです。

4
Matthew

私の状況では、[戻る]ボタンがユーザーを最後のページに送信するのを防ぎ、代わりに別のアクションを実行したかったのです。 hashchangeイベントを使用して、私に合った解決策を考え出しました。このスクリプトは、私の場合のように、使用しているページがまだハッシュを使用していないことを前提としています。

var hashChangedCount = -2;
$(window).on("hashchange", function () {
    hashChangedCount++;
    if (top.location.hash == "#main" && hashChangedCount > 0) {
        console.log("Ah ah ah! You didn't say the magic Word!");
    }
    top.location.hash = '#main';
});
top.location.hash = "#";

私が他の回答のいくつかで抱えていた問題は、hashchangeイベントが発生しないことです。あなたの状況によっては、これもあなたのために働くかもしれません。

2
Swisher Sweet

HTML5履歴APIを使用して、通常の履歴動作をオーバーライドし、個別のbackおよびforwardボタンイベントを作成する方法を考え出しました(IEでは機能しません) = 9)。これは非常にハッキーですが、戻るボタンと進むボタンイベントをインターセプトして、必要に応じて処理する場合に効果的です。これは、リモートデスクトップウィンドウを表示していて必要な場合など、多くのシナリオで役立ちますリモートマシンで戻るボタンと進むボタンのクリックを再現します。

以下は、戻るボタンと進むボタンの動作をオーバーライドします。

var myHistoryOverride = new HistoryButtonOverride(function()
{
    console.log("Back Button Pressed");
    return true;
},
function()
{
    console.log("Forward Button Pressed");
    return true;
});

これらのコールバック関数のいずれかでfalseを返した場合、ブラウザが通常の戻る/進む操作を続行してページを離れることを許可することになります。

必要な完全なスクリプトは次のとおりです。

function HistoryButtonOverride(BackButtonPressed, ForwardButtonPressed)
{
    var Reset = function ()
    {
        if (history.state == null)
            return;
        if (history.state.customHistoryStage == 1)
            history.forward();
        else if (history.state.customHistoryStage == 3)
            history.back();
    }
    var BuildURLWithHash = function ()
    {
        // The URLs of our 3 history states must have hash strings in them so that back and forward events never cause a page reload.
        return location.Origin + location.pathname + location.search + (location.hash && location.hash.length > 1 ? location.hash : "#");
    }
    if (history.state == null)
    {
        // This is the first page load. Inject new history states to help identify back/forward button presses.
        var initialHistoryLength = history.length;
        history.replaceState({ customHistoryStage: 1, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.pushState({ customHistoryStage: 2, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.pushState({ customHistoryStage: 3, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.back();
    }
    else if (history.state.customHistoryStage == 1)
        history.forward();
    else if (history.state.customHistoryStage == 3)
        history.back();

    $(window).bind("popstate", function ()
    {
        // Called when history navigation occurs.
        if (history.state == null)
            return;
        if (history.state.customHistoryStage == 1)
        {
            if (typeof BackButtonPressed == "function" && BackButtonPressed())
            {
                Reset();
                return;
            }
            if (history.state.initialHistoryLength > 1)
                history.back(); // There is back-history to go to.
            else
                history.forward(); // No back-history to go to, so undo the back operation.
        }
        else if (history.state.customHistoryStage == 3)
        {
            if (typeof ForwardButtonPressed == "function" && ForwardButtonPressed())
            {
                Reset();
                return;
            }
            if (history.length > history.state.initialHistoryLength + 2)
                history.forward(); // There is forward-history to go to.
            else
                history.back(); // No forward-history to go to, so undo the forward operation.
        }
    });
};

これは単純な概念で機能します。ページが読み込まれると、3つの異なる履歴状態(1、2、および3の番号)を作成し、ブラウザーを状態番号2にナビゲートします。状態2は中央にあるため、次の履歴ナビゲーションイベントによって状態1または3、これからユーザーが押した方向を判断できます。そのように、戻るまたは進むボタンイベントをインターセプトしました。その後、必要に応じて処理し、次の履歴ナビゲーションイベントをキャプチャできるように状態番号2に戻ります。

もちろん、HistoryButtonOverrideスクリプトを使用している間、history.replaceStateメソッドとhistory.pushStateメソッドを使用しないようにする必要があります。

2
Brian

ブラウザの戻るボタンは通常、以前のアドレスに戻ろうとします。 ネイティブデバイスの戻るボタンが押されたイベントはありませんブラウザのJavaScriptでは、場所とハッシュを使ってハッキングしてアプリの状態を変更できます。

2つのビューを持つシンプルなアプリを想像してください。

  • フェッチボタンのあるデフォルトビュー
  • 結果ビュー

実装するには、次のサンプルコードを使用します。

function goToView (viewName) {
  // before you want to change the view, you have to change window.location.hash.
  // it allows you to go back to the previous view with the back button.
  // so use this function to change your view instead of directly do the job.
  // page parameter is your key to understand what view must be load after this.

  window.location.hash = page
}

function loadView (viewName) {
    // change dom here based on viewName, for example:

    switch (viewName) {
      case 'index':
        document.getElementById('result').style.display = 'none'
        document.getElementById('index').style.display = 'block'
        break
      case 'result':
        document.getElementById('index').style.display = 'none'
        document.getElementById('result').style.display = 'block'
        break
      default:
        document.write('404')
    }
}

window.addEventListener('hashchange', (event) => {
  // oh, the hash is changed! it means we should do our job.
  const viewName = window.location.hash.replace('#', '') || 'index'

  // load that view
  loadView(viewName)
})


// load requested view at start.
if (!window.location.hash) {
  // go to default view if there is no request.
  goToView('index')
} else {
  // load requested view.
  loadView(window.location.hash.replace('#', ''))
}
1
Nainemom

私はこのようなことをします。以前のアプリの状態で配列を保持します。

として開始:

var backstack = [];

次に、ロケーションハッシュの変更をリッスンし、変更されたときにこれを実行します。

if (backstack.length > 1 && backstack[backstack.length - 2] == newHash) {
    // Back button was probably pressed
    backstack.pop();
} else
    backstack.Push(newHash);

この方法で、ユーザー履歴を追跡する簡単な方法があります。ブラウザーボタンではなくアプリに戻るボタンを実装する場合は、次のようにします。

window.location.hash = backstack.pop();
0
Trenskow