Spec-Zone.ru › Web APIs

Работа с API истории

API истории позволяет веб-сайту взаимодействовать с историей сеанса браузера: то есть со списком страниц, которые пользователь посетил в данном окне. Когда пользователь переходит на новые страницы, например, щелкнув по ссылкам, эти новые страницы добавляются в историю сеанса. Пользователь также может перемещаться вперед и назад по истории, используя кнопки браузера «Назад» и «Вперед».

Основной интерфейс, определённый в API истории, это интерфейс History, и он определяет два довольно различных набора методов:

  1. Методы для перехода на страницу в истории сеанса:

    • History.back()
    • History.forward()
    • History.go()
  2. Методы для изменения истории сеанса:

    • History.pushState()
    • History.replaceState()

В этом руководстве нас будут интересовать только методы второго набора, поскольку они имеют более сложное поведение.

Метод pushState() добавляет новую запись в историю сеанса, а метод replaceState() обновляет запись в истории сеанса для текущей страницы. Оба этих метода принимают параметр state, который может содержать любой сериализуемый объект. Когда браузер переходит к этой записи истории, браузер генерирует событие popstate, которое содержит объект состояния, связанный с этой записью.

Основная цель этих API — поддержка веб-сайтов, таких как одностраничные приложения, которые используют JavaScript-API, такие как fetch(), для обновления страницы новым содержимым вместо загрузки новой страницы целиком.

Одностраничные приложения и история сеанса

Традиционно веб-сайты реализуются как набор страниц. Когда пользователи переходят к различным частям сайта, щелкнув по ссылкам, браузер загружает новую страницу каждый раз.

Хотя это отлично подходит для многих сайтов, это может иметь некоторые недостатки:

  • Это может быть неэффективно загружать всю страницу каждый раз, когда нужно обновить только часть страницы.
  • Сложно поддерживать состояние приложения при переходе между страницами.

По этим причинам популярным шаблоном для веб-приложений является одностраничное приложение (SPA), в котором сайт состоит из одной страницы, и когда пользователь щелкает по ссылкам, страница:

  1. Блокирует стандартное поведение загрузки новой страницы
  2. Загружает новое содержимое для отображения
  3. Обновляет страницу новым содержимым

Например:

document.addEventListener("click", async (event) => {
  const creature = event.target.getAttribute("data-creature");
  if (creature) {
    // Prevent a new page from loading
    event.preventDefault();
    try {
      // Fetch new content
      const response = await fetch(`creatures/${creature}.json`);
      const json = await response.json();
      // Update the page with the new content
      displayContent(json);
    } catch (err) {
      console.error(err);
    }
  }
});

В этом обработчике кликов, если ссылка содержит атрибут данных "data-creature", то мы используем значение этого атрибута для загрузки JSON-файла, содержащего новое содержимое страницы.

JSON-файл может выглядеть следующим образом:

{
  "description": "Bald eagles are not actually bald.",
  "image": {
    "src": "images/eagle.jpg",
    "alt": "A bald eagle"
  },
  "name": "Eagle"
}

Наша функция displayContent() обновляет страницу с JSON:

// Update the page with the new content
function displayContent(content) {
  document.title = `Creatures: ${content.name}`;

  const description = document.querySelector("#description");
  description.textContent = content.description;

  const photo = document.querySelector("#photo");
  photo.setAttribute("src", content.image.src);
  photo.setAttribute("alt", content.image.alt);
}

Проблема в том, что это нарушает ожидаемое поведение кнопок «Назад» и «Вперед» браузера.

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

Но, с точки зрения браузера, последняя ссылка не загрузила новую страницу, поэтому «Назад» переведет браузер на ту страницу, которая была загружена до того, как пользователь открыл SPA.

В сущности, эта проблема решается методами pushState(), replaceState(), и событием popstate. Они позволяют нам синтезировать записи истории и получать уведомления, когда текущая запись истории сеанса изменяется на одну из этих записей (например, потому что пользователь нажал кнопки «Назад» или «Вперед»).

Использование pushState()

Мы можем добавить запись в историю в обработчик кликов выше следующим образом:

document.addEventListener("click", async (event) => {
  const creature = event.target.getAttribute("data-creature");
  if (creature) {
    event.preventDefault();
    try {
      const response = await fetch(`creatures/${creature}.json`);
      const json = await response.json();
      displayContent(json);
      // Add a new entry to the history.
      // This simulates loading a new page.
      history.pushState(json, "", creature);
    } catch (err) {
      console.error(err);
    }
  }
});

Здесь мы вызываем pushState() с тремя аргументами:

  • json: это содержимое, которое мы только что загрузили. Оно будет храниться вместе с записью истории и позднее включено как свойство state аргумента, передаваемого обработчику события popstate.
  • "": это необходимо для обратной совместимости со старыми сайтами и должно всегда быть пустой строкой.
  • creature: это будет использоваться в качестве URL для записи. Оно будет отображаться в адресной строке браузера и будет использоваться как значение заголовка Referer в любых HTTP-запросах, которые выполняет страница. Обратите внимание, что это должно быть одного происхождения с страницей.

Использование события popstate

Предположим, пользователь:

  1. Щелкнул по ссылке в нашем SPA, поэтому мы обновили страницу и добавили запись истории A с помощью pushState()
  2. Щелкнул по другой ссылке в нашем SPA, поэтому мы обновили страницу и добавили запись истории B с помощью pushState()
  3. Нажал кнопку «Назад»

Теперь новой текущей записью истории является A, поэтому браузер генерирует событие popstate, и аргумент обработчика события содержит JSON, который мы передали в pushState() при обработке перехода к A. Это означает, что мы можем восстановить правильное содержимое с помощью обработчика событий, такого как этот:

// Handle forward/back buttons
window.addEventListener("popstate", (event) => {
  // If a state has been provided, we have a "simulated" page
  // and we update the current page.
  if (event.state) {
    // Simulate the loading of the previous page
    displayContent(event.state);
  }
});

Использование replaceState()

Нам нужно добавить ещё один момент. Когда пользователь загружает SPA, браузер добавляет запись в историю. Поскольку это была фактическая загрузка страницы, запись не имеет состояния, связанного с ней. Итак, предположим, что пользователь:

  1. Загружает SPA: браузер добавляет запись в историю
  2. Щелкнул по ссылке внутри SPA: обработчик клика обновляет страницу и добавляет запись в историю с pushState()
  3. Нажал кнопку «Назад»

Теперь мы хотим вернуться к начальному состоянию SPA, но поскольку это навигация в одном документе, страница не будет перезагружена, а так как запись в истории для начальной страницы не имеет состояния, мы не можем использовать popstate для её восстановления.

Решение здесь — использовать replaceState() для установки объекта состояния для начальной страницы. Например:

// Create state on page load and replace the current history with it
const image = document.querySelector("#photo");
const initialState = {
  description: document.querySelector("#description").textContent,
  image: {
    src: image.getAttribute("src"),
    alt: image.getAttribute("alt"),
  },
  name: "Home",
};
history.replaceState(initialState, "", document.location.href);

При загрузке страницы мы собираем все части страницы, которые нам нужно восстановить, когда пользователь возвращается к начальной точке SPA. Эта структура аналогична JSON, который мы загружаем при обработке других переходов. Мы передаём этот объект initialState в replaceState(), что фактически добавляет объект состояния в текущую запись истории.

Когда пользователь возвращается к нашей начальной точке, событие popstate будет содержать это начальное состояние, и мы можем использовать нашу функцию displayContent() для обновления страницы.

Полный пример

Вы можете найти этот полный пример по адресу https://github.com/mdn/dom-examples/tree/main/history-api, а демо-версию — по адресу https://mdn.github.io/dom-examples/history-api/.

См. также

  • API истории
  • history глобальный объект

© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API