Работа с History API

В HTML5 стали доступны методы pushState() и replaceState() для добавления и изменения записей в истории. Эти методы работают в сочетании с событием onpopstate (en-US)

Добавление и изменение записей в истории

pushState() позволяет изменить referrer, который используется в HTTP-заголовке для XMLHttpRequest объектов, созданных после изменения состояния. Значением referrer будет URL документа, окно которого является this на момент создания XMLHttpRequest объекта.

Пример метода pushState()

Предположим, на странице http://mozilla.org/foo.html выполняется следующий JavaScript-код:

js
let stateObj = {
  foo: "bar",
};

history.pushState(stateObj, "page 2", "bar.html");

В результате этого в URL-строке отобразится адрес http://mozilla.org/bar.html, но браузер не будет загружать страницу bar.html и даже не будет проверять, существует ли она..

Теперь предположим, что пользователь перешёл по адресу http://google.com и затем нажал на кнопку "Назад". В результате этого в URL будет отображаться http://mozilla.org/bar.html, а history.state будет содержать stateObj. Событие popstate не будет вызвано, поскольку страница была перезагружена. Сама страница будет выглядеть как bar.html.

Если пользователь ещё раз нажимает кнопку "Назад", URL изменится на http://mozilla.org/foo.html, а в документе снова произойдёт событие popstate, на этот раз с объектом состояния, имеющим значение null. В этом случае возврат назад также не меняет содержимое документа, как и на предыдущем шаге, хотя документ может сам обновить своё содержимое после получения события popstate.

Примечание: Вызов history.back() обычно ведёт себя так же, как нажатие на кнопку "Назад". Но есть одно важное исключение:

После использования history.pushState(), вызов history.back() не вызывает событие popstate. Нажатие в браузере на кнопку "Назад" (всё ещё) делает это.

Метод pushState()

Метод pushState() принимает три параметра: объект состояния, заголовок (в данный момент игнорируется) и (необязательно) параметр "URL".

Давайте более подробно рассмотрим каждый и этих трёх параметров.

state object

Объект состояния – это JavaScript-объект, связанный с новой записью в истории, созданной pushState(). Всякий раз, когда пользователь переходит к новому состоянию, происходит событие popstate, а свойство state этого события содержит копию объекта состояния с записями истории.

Объект состояния может быть чем-угодно, что может быть сериализовано. Поскольку Firefox сохраняет объекты состояния на диске пользователя, чтобы их можно было восстановить после перезапуска браузера, мы накладываем ограничение в 640 тысяч символов на сериализованное представление объекта состояния. Если вы передаёте объект состояния, чьё сериализованное представление больше этого значения, метод pushState() выдаст исключение. Если вам нужно хранилище большего размера, следует рассмотреть использование sessionStorage и/или localStorage.

title

Заголовок - все браузеры, кроме Safari, на данный момент игнорируют этот параметр, но могут начать использовать в будущем. Ввиду будущих изменений метода, безопасным решением является передача пустой строки. В качестве альтернативы вы можете передать короткий заголовок для состоянии, в которое переходите.

URL

Через этот параметр передаётся URL-адрес новой новый записи в истории. Обратите внимание, что браузер не будет пытаться загрузить данный URL сразу после вызова pushState(), но может попытаться сделать это позже, например, после того, как пользователь перезапустит браузер. Новый URL-адрес не обязан быть абсолютным; если он относительный, то определяется относительно текущего URL. Новый URL должен вести на тот же домен, протокол и порт, иначе pushState() выдаст исключение. Данный параметр не является обязательным; если он не указан, будет использоваться URL текущего документа.

Примечание: Начиная с Gecko 2.0 и до Gecko 5.0, переданный объект сериализуется с помощью JSON. Начиная с Gecko 6.0, объект сериализуется с помощью алгоритма структурированного клонирования. Это позволяет безопасно передавать более широкий спектр объектов.

Вызов pushState() в некоторой степени похож на установку window.location = "#foo", поскольку они оба также создают и активируют ещё одну запись в истории, связанную с текущим документом.

Но у pushState() есть несколько преимуществ:

  • Новый URL может быть любым в пределах того же домена, порта и протокола, что и текущий адрес. Тогда как настройка window.location оставляет вас на том же document лишь в том случае, если вы меняете только хэш
  • Менять URL не обязательно. Тогда как настройка window.location = "#foo"; создаёт новую запись в истории, только если текущий хеш не #foo
  • С новой записью в истории можно связать любые данные. В подходе, основанном на хеше, все соответствующие данные нужно кодировать в короткую строку
  • Если заголовок title впоследствии используется браузерами, эти данные могут быть использованы (независимо от, скажем, хеша).

Обратите внимание, что pushState() никогда не вызывает событие hashchange, даже если новый URL отличается от старого только хешем.

В XUL-документах он создаёт указанный XUL-элемент

В других документах он создаёт элемент с null namespace URI.

Метод replaceState()

history.replaceState() работает точно так же, как history.pushState(), за исключением того, что replaceState() изменяет текущую запись истории вместо создания новой записи. Обратите внимание, что он не предотвращает создание новой записи в глобальной истории браузера.

replaceState() особенно полезен, когда вы хотите обновить объект состояния или URL текущей записи в истории в ответ на какое-то действие пользователя.

Примечание: Начиная с Gecko 2.0 и до Gecko 5.0, переданный объект сериализуется с помощью JSON. Начиная с Gecko 6.0, объект сериализуется с помощью алгоритма структурированного клонирования. Это позволяет безопасно передавать более широкий спектр объектов.

Пример метода replaceState()

Предположим, на странице http://mozilla.org/foo.html выполняется следующий JavaScript-код:

js
let stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");

Объяснение этих двух строк можно найти в приведённом выше разделе пример метода pushState().

Далее, предположим, на странице http://mozilla.org/bar.html выполняется JavaScript-код:

js
history.replaceState(stateObj, "page 3", "bar2.html");

Это приведёт к тому, что в URL-строке отобразится адрес http://mozilla.org/bar2.html, но браузер не станет сразу загружать bar2.html и даже не станет проверять наличие этой страницы bar2.html.

Теперь предположим, что пользователь переходит по адресу http://www.microsoft.com, а затем нажимает на кнопку "Назад". В этом случае в URL-строке отобразится http://mozilla.org/bar2.html. Если же пользователь снова нажмёт на кнопку "Назад", в URL-строке отобразится http://mozilla.org/foo.html и полностью обойдёт bar.html.

Событие popstate

Событие popstate вызывается в окне каждый раз, когда активная запись в истории меняется. Если запись в истории, которая активируется, была создана с помощью вызова pushState или активирована вызовом replaceState, свойство state события popstate содержит копию записи в истории объекта события.

Примеры использования можно посмотреть в Window.onpopstate (en-US).

Чтение текущего состояния

Когда страница загружается, она может иметь объект события со значением, отличным от "null". Это может произойти, например, если страница устанавливает объект состояния (с помощью pushState() или replaceState()) и затем пользователь перезапускает браузер. Когда страница перезагружается, она получит событие onload, но не получит событие popstate. Тем не менее, если вы прочитаете свойство history.state, получите объект состояния, который получили, если бы произошло событие popstate.

С помощью свойства history.state можно прочитать состояние текущей записи в истории, не дожидаясь события popstate, например:

js
let currentState = history.state;

Смотрите также