Как я могу хранить функции в состояниях истории HTML5 - PullRequest
4 голосов
/ 20 января 2012

Поэтому я использую управление историей HTML5 для добавления возможности перемещаться назад и вперед по веб-сайту с загруженным AJAX-субконтентом.

Теперь я хотел бы сохранить функции 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 Exception 25»

Кто-нибудь знает, что я делаю не так? Можно ли вообще хранить вызовы функций в состоянии?

Ответы [ 3 ]

7 голосов
/ 20 января 2012

Нет, это невозможно, во всяком случае, напрямую. Согласно MDC «объект состояния», то есть первый аргумент pushState, «может быть любым, что может быть сериализовано». К сожалению, вы не можете сериализовать функцию. Спецификация WHATWG говорит в основном то же самое, но во многих других словах, суть которых в том, что функции явно запрещены в объекте состояния.

Решением было бы сохранить либо строку, которую вы можете 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 ).

2 голосов
/ 02 июля 2013

Я придумал немного другое решение.

Я добавил две переменные в переменную окна

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

Каждый раз, когда я выполняю pushstate, все, что я делаю, это нажимаю уникальный идентификатор для первого параметра

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

В случае события popstate я просто извлекаю идентификатор из объекта состояния и ищу его из объекта данных.

0 голосов
/ 23 июля 2014

Вот что я делаю:

  • Каждая HTML-страница содержит один или несколько компонентов, которые могут создавать новые записи истории.
  • Каждый компонент реализует три метода:
    1. getId(), который возвращает уникальный идентификатор DOM.
    2. getState(), который возвращает состояние компонента:

      {
        id: getId(),
        state: componentSpecificState
      }
      
    3. setState(state), который обновляет состояние компонента, используя вышеупомянутое значение.
  • При загрузке страницы я инициализирую отображение из идентификатора компонента в компонент следующим образом:

    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);
    

Подводя итог: вместо сериализации function() мы сериализуем идентификатор компонента и запускаем его функцию setState(). Этот подход выдерживает загрузку страницы.

...