22

コンテンツのajaxロードでHTML5履歴APIを試しています。

相対リンクで接続されたテストページがたくさんあります。私はこれらのリンクのクリックを処理するこのJSを持っています。リンクがクリックされると、ハンドラーはそのhref属性を取得し、それをajaxLoadPage()に渡します。この属性は、要求されたページから現在のページのコンテンツ領域にコンテンツをロードします。(私のPHPページは、通常どおりリクエストすると完全なHTMLページを返すように設定されていますが、リクエストのURLに?fragment = trueが追加されている場合は、コンテンツのチャンクのみが返されます。)

次に、クリックハンドラーがhistory.pushState()を呼び出して、アドレスバーにURLを表示し、ブラウザーの履歴に追加します。

$(document).ready(function(){

    var content = $('#content');

    var ajaxLoadPage = function (url) {
        console.log('Loading ' + url + ' fragment');
        content.load(url + '?fragment=true');
    }

    // Handle click event of all links with href not starting with http, https or #
    $('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){

        e.preventDefault();
        var href = $(this).attr('href');
        ajaxLoadPage(href);
        history.pushState({page:href}, null, href);

    });

    // This mostly works - only problem is when popstate happens and state is null
    // e.g. when we try to go back to the initial page we loaded normally
    $(window).bind('popstate', function(event){
        console.log('Popstate');
        var state = event.originalEvent.state;
        console.log(state);
        if (state !== null) {
            if (state.page !== undefined) {
                ajaxLoadPage(state.page);
            }
        }
    });

});

pushStateを使用して履歴にURLを追加する場合は、戻るボタンまたは進むボタンのクリックを処理するために、popstateイベントのイベントハンドラーも含める必要があります。(これを行わない場合、戻るをクリックすると、アドレスバーに履歴にプッシュしたURLが表示されますが、ページは更新されません。)したがって、私のpopstateハンドラーは、作成した各エントリのstateプロパティに保存されたURLを取得します。そしてそれをajaxLoadPageに渡して、適切なコンテンツをロードします。

これは、クリックハンドラーが履歴に追加されたページで問題なく機能します。しかし、「通常」にリクエストしたときにブラウザが履歴に追加したページはどうなりますか?通常どおり最初のページにアクセスし、ajaxの読み込みを行うクリックでサイト内を移動するとします。その後、履歴を最初のページに戻そうとすると、最後のクリックで最初のページのURLが表示されますが、表示されません。ブラウザにページをロードしません。何故ですか?

これは、最後のpopstateイベントのstateプロパティと関係があることがわかります。値を与えることができるのはpushState()またはreplaceState()によって履歴に追加されたエントリのみであるため、そのイベントの状態プロパティはnullです。しかし、ページの最初の読み込みは「通常の」リクエストでした。ブラウザが元のURLに戻って通常どおりに読み込まないのはなぜですか?

4

6 に答える 6

15

これは古い質問ですが、この問題にネイティブJavaScriptを使用するとはるかに簡単な答えがあります。

初期状態では、使用するのではなく、を使用する必要がありhistory.pushStateますhistory.replaceState

すべての引数は両方の方法で同じですが、唯一の違いはpushState、新しい履歴レコードを作成するため、問題の原因となることです。replaceStateその履歴レコードの状態を置き換えるだけで、期待どおりに動作します。つまり、最初の開始ページに戻ります。

于 2014-05-21T20:19:46.457 に答える
6

元の質問と同じ問題が発生しました。この行

var initialPop = !popped && location.href == initialURL;

に変更する必要があります

var initialPop = !popped;

これは、最初のポップをキャッチするのに十分です。その後、元のページをに追加する必要はありませんpushState。つまり、以下を削除します。

var home = 'index.html';
history.pushState({page:home}, null, home);

AJAXタブに基づく(そしてMootoolsを使用する)最終的なコード:

if ( this.supports_history_api() ) {
    var popped = ('state' in window.history && window.history.state !== null)
    ,   changeTabBack = false;

    window.addEvent('myShowTabEvent', function ( url ) {
       if ( url && !changingTabBack )
           setLocation(url);
       else
           changingTabBack = false;
       //Make sure you do not add to the pushState after clicking the back button
    });

   window.addEventListener("popstate", function(e) {
       var initialPop = !popped;
       popped = true;
       if ( initialPop )
           return;

       var tabLink = $$('a[href="' + location.pathname + '"][data-toggle*=tab]')[0];
       if ( tabLink ) {
           changingTabBack = true;
           tabLink.tab('show');
       }
   });
}
于 2013-11-29T12:37:58.207 に答える
5

戻るボタンがこのように動作する理由はまだわかりません。ブラウザは、通常のリクエストで作成されたエントリに戻って喜んでくれると思っていました。pushStateを使用して他のエントリを挿入すると、履歴が通常の方法で動作しなくなる可能性があります。しかし、コードをより良く機能させる方法を見つけました。ステップバックするURLを含むstateプロパティに常に依存できるとは限りません。ただし、履歴をさかのぼると、期待どおりにアドレスバーのURLが変更されるため、window.locationに基づいてコンテンツをロードする方が信頼性が高い場合があります。このすばらしい例に従って、popstateハンドラーを変更して、stateプロパティでURLを探す代わりに、アドレスバーのURLに基​​づいてコンテンツをロードするようにしました。

注意しなければならないことの1つは、一部のブラウザ(Chromeなど)は、最初にページにアクセスしたときにpopstateイベントを発生させることです。これが発生すると、最初のページのコンテンツを不必要にリロードする可能性があります。そこで、最初のポップを無視するために、優れたpjaxからコードをいくつか追加しました。

$(document).ready(function(){

    // Used to detect initial (useless) popstate.
    // If history.state exists, pushState() has created the current entry so we can
    // assume browser isn't going to fire initial popstate
    var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href;

    var content = $('#content');

    var ajaxLoadPage = function (url) {

        console.log('Loading ' + url + ' fragment');
        content.load(url + '?fragment=true');

    }

    // Handle click event of all links with href not starting with http, https or #
    $('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){

        e.preventDefault();
        var href = $(this).attr('href');
        ajaxLoadPage(href);
        history.pushState({page:href}, null, href);

    });

    $(window).bind('popstate', function(event){

        // Ignore inital popstate that some browsers fire on page load
        var initialPop = !popped && location.href == initialURL;
        popped = true;
        if (initialPop) return;

        console.log('Popstate');

        // By the time popstate has fired, location.pathname has been changed
        ajaxLoadPage(location.pathname);

    });

});

このJSに加えることができる改善の1つは、ブラウザーが履歴APIをサポートしている場合にのみクリックイベントハンドラーをアタッチすることです。

于 2013-01-09T12:15:49.757 に答える
1

私は実際に今日同様のニーズを抱えていることに気づき、あなたが提供したコードが非常に役立つことに気づきました。私はあなたがしたのと同じ問題に遭遇しました、そしてあなたが見逃しているのはあなたがすべての後続のページであるのと同じ方法であなたのインデックスファイルまたはホームページを履歴にプッシュすることだけだと思います。

これを解決するために私が行ったことの例を次に示します(これが正しい答えかどうかはわかりませんが、単純で機能します!):

var home = 'index.html';
history.pushState({page:home}, null, home);

お役に立てれば!

于 2013-01-08T21:02:45.323 に答える
1

これは古い質問だと思いますが、このように状態を簡単に管理しようとする場合は、次のアプローチをとる方がよい場合があります。

$(window).on('popstate',function(e){
    var state = e.originalEvent.state;
    if(state != null){
        if(state.hasOwnProperty('window')){
            //callback on window
            window[state.window].call(window,state);
        }
    }
});

このようにして、履歴に追加するときに状態オブジェクトにオプションのコールバック関数を指定できます。その後、popstateがトリガーされると、この関数は状態オブジェクトをパラメーターとして使用して呼び出されます。

function pushState(title,url,callback)
{
    var state = {
        Url : url,
        Title : title,
    };
    if(window[callback] && typeof window[callback] === 'function')
    {
        state.callback = callback;
    }
    history.pushState(state,state.Title,state.Url);
}

ニーズに合わせてこれを簡単に拡張できます。

于 2015-07-17T14:12:47.887 に答える
0

そして最後に言う:

ブラウザは、通常のリクエストで作成されたエントリに戻って喜んでくれると思いました。

その奇妙なブラウザの動作の説明をここで見つけました。説明は

サイトが最初に読み込まれたとき、およびその後は状態が変わるたびに、状態を保存する必要があります

私はこれをテストしました-それは動作します。

これは、window.locationに基づいてコンテンツをロードする必要がないことを意味します。

誤解しないでください。

于 2016-01-06T19:50:41.990 に答える