Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I store functions within the HTML5 history states

So I'm using the HTML5 history management for adding the ability to navigate back and forward within a website with AJAX loaded subcontent.

Now I would like to store javascript functions within the state object, to callback at the state popping. More or less like the following code:

$(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) {
    ...
}

All right. So what I'm trying to do is storing a reference to a javascript function. But when pushing the state (the actual window.history.pushState call) the browser files a complaint, namely Error: "DATA_CLONE_ERR: DOM Exception 25"

Anybody knows what I'm doing wrong? Is it at all possible to store function calls within the state?

like image 766
stafffan Avatar asked Jan 18 '23 17:01

stafffan


1 Answers

No, it's not possible, not directly anyway. According to MDC the "state object," i.e. the first argument to pushState, "can be anything that can be serialized." Unfortunately, you can't serialize a function. The WHATWG spec says basically the same thing but in many more words, the gist of which is that functions are explicitly disallowed in the state object.

The solution would be to store either a string you can eval or the name of the function in the state object, e.g.:

$(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"
  );
}

Of course that's assuming switchPageToMode1 and -2 are in the global context (i.e. window), which isn't the best practice. If not they'll have to be accessible somehow from the global context, e.g. [window.]MyAppGlobal.switchPageToMode1, in which case you would call MyAppGlobal[ func ]( argument ).

like image 187
Jordan Running Avatar answered Feb 11 '23 11:02

Jordan Running