2012-01-20 15 views
4

HTML5履歴管理を使用して、AJAXがロードされたサブコンテンツを含むウェブサイト内を前後にナビゲートする機能を追加しています。HTML5履歴状態内に関数を保存する方法

これで、状態オブジェクト内にjavascript関数を格納し、状態がポップアップしてコールバックするようにしたいと考えています。多かれ少なかれ次のコードのようになります:

$(window).bind("popstate", function(event) { 
    var state = event.originalEvent.state; 
    if (!state) { 
     return; 
    } 
    state.callback(state.argument); 
} 

function beforeLoad() { 
    var resourceId = "xyz"; 
    var func; 

    if (case1) { 
     func = switchPageToMode1; 
    } else { // case 2 
     func = swithPageToMode2; 
    } 

    func(resourceId); // run the function 
    window.history.pushState({ callback: func, resourceId: resourceId }, "newTitle", "newURL"); // and push it to history 
} 

function switchPageToMode1(resourceId) { 
    alterPageLayoutSomeWay(); 
    loadResource(resourceId); 
} 

function swithPageToMode2(resourceId) { 
    alterPageLayoutSomeOtherWay(); 
    loadResource(resourceId); 
} 

function loadResource(resourceId) { 
    ... 
} 

すべてです。だから私がしようとしているのは、javascript関数への参照を格納することです。状態(実際window.history.pushStateコール)を押すとブラウザが申し立てをしかし、すなわちエラー:「DATA_CLONE_ERR:DOM例外25」

誰も私が間違ってやっている知っていますか?状態内に関数呼び出しを格納することは可能ですか?

答えて

6

いいえ、それは不可能ですが、とにかくではありません。 According to MDC「状態オブジェクト」、すなわちpushStateの最初の引数は、「シリアライズ可能なものであれば何でもよい」残念ながら、関数をシリアル化することはできません。 WHATWG specは、基本的には同じことを言いますが、より多くの言葉で言えば、その主な目的は、関数が明示的に状態オブジェクトで禁止されているということです。

ソリューションを使用すると、状態オブジェクトで evalまたは関数の名前をすることができ、文字列、例えばいずれかで保存することです

:もちろん

$(window).bind("popstate", function(event) { 
    var state = event.originalEvent.state; 

    if (!state) { return; } 

    window[ state.callback ](state.argument); // <-- look here 
} 

function beforeLoad() { 
    var resourceId = "xyz", 
     func 
    ; 

    if (case1) { 
    func = "switchPageToMode1"; // <-- string, not function 
    } else { 
    // case 2 
    func = "swithPageToMode2"; 
    } 

    window[ func ](resourceId); // <-- same here 

    window.history.pushState(
    { callback : func, 
     argument : resourceId 
    }, 
    "newTitle", "newURL" 
); 
} 

switchPageToMode1-2は、グローバルな文脈(すなわちであると仮定していますwindow)、これはベストプラクティスではありません。もしそうでなければ、彼らは何とかグローバルコンテキストからアクセス可能でなければならないでしょう。 [window.]MyAppGlobal.switchPageToMode1、その場合はMyAppGlobal[ func ](argument)とします。

+0

ありがとう!それはそれをすべて説明します。シリアライゼーション部分を見逃してしまった。 – stafffan

+0

@Jordan私は '{html:$( '#main')。html()}'のように見えるオブジェクトを保存していますが、IE 11で 'Data Clone Error' /またはhttps://github.com/devote/HTML5-History-API/blob/master/history.jsのような追加の履歴APIを追加する。私はまだ何が間違っていますか? –

1

私は若干異なる解決策を思いついた。

私は、ウィンドウの変数

window.history.uniqueStateId = 0; 
window.history.data = {}. 

私はpushstateを実行するたびに、私はすべてが最初のパラメータpopstateイベントで

var data = { /* non-serializable data */ }; 
window.history.pushState({stateId : uniqueStateId}, '', url); 
window.history.data[uniqueStateId] = data; 

の一意のIDをプッシュしているに二つの変数を追加しました私はちょうど状態オブジェクトからIDをつかみ、それをデータオブジェクトから探します。ここで

+1

これは実際にはすごく、まだシンプルな解決策です。大好きです! – stafffan

+2

-1:これはページの再読み込みには存続しません。戻るボタンを使用してページを移動し、次に進むボタンを押すと、すべての状態がなくなったことがわかります。 – Gili

+0

他の回答はどちらも行いません。必要に応じてローカルストレージソリューションを混在させるだけです – futbolpal

0

は、私が何をすべきかです:

  • 各HTMLページが新しい履歴エントリを作成することができる1つのまたは複数のコンポーネントが含まれています。
  • 各コンポーネントは3つのメソッドを実装しています。
    1. getId()独自のDOM IDを返します。前述の値を使用してコンポーネントの状態を更新

      { 
          id: getId(), 
          state: componentSpecificState 
      } 
      
    2. setState(state):コンポーネントの状態を返す
    3. getState()
    4. ページロードで
  • 、私はそうのようなコンポーネントにコンポーネントIDからのマッピングを初期化します。

    this.idToComponent[this.loginForm.getId()] = this.loginForm; 
    this.idToComponent[this.signupForm.getId()] = this.signupForm; 
    
  • コンポーネントは、新しい履歴エントリを作成する前にその状態を保存します。 history.replaceState(this.getState(), title, href);

  • popstateイベントが発生すると、私は次のように呼び出します。

    var component = this.idToComponent[history.state.id]; 
    component.setState(history.state); 
    

要約する代わりに、我々はコンポーネントIDをシリアル化し、そのsetState()機能を発射function()をシリアル化するの。この方法はページの読み込みにも影響します。

関連する問題