JavaScript. Подробное руководство, 6-е издание, стр. 376

Предусмотрев изменение значения свойства

<b>location.hash</b>
, вы даете пользователю возможность использовать кнопки Back и Forward для перемещения между состояниями приложения. Чтобы такие перемещения были возможны, приложение должно иметь способ определять момент изменения состояния, прочитать строку, хранящуюся в виде идентификатора фрагмента, и обновить себя в соответствии с требуемым состоянием. Согласно спецификации HTML5, при изменении идентификатора фрагмента броузер должен возбуждать событие «hashchange» в объекте
<b>Window</b>
. В броузерах, поддерживающих событие «hashchange», можно присвоить свойству
<b>window.onhashchange</b>
функцию обработчика, которая будет вызываться при каждом изменении идентификатора фрагмента документа, вызванном перемещением по истории посещений. При вызове эта функция-обработчик должна проанализировать значение
<b>location.hash</b>
и отобразить содержимое страницы, соответствующее выбранному состоянию.

Спецификация HTML5 также определяет другой, более сложный и более надежный способ управления историей посещений, основанный на использовании метода

<b>history.pushState()</b>
и события «popstate». При переходе в новое состояние вебприложение может вызвать метод
<b>history.pushState()</b>
, чтобы добавить это состояние в историю посещений. В первом аргументе методу передается объект, содержащий всю информацию, необходимую для восстановления текущего состояния приложения. Для этой цели подойдет любой объект, который можно преобразовать в строку вызовом метода
<b>JSON.stringify(),</b>
а также некоторые встроенные типы, такие как
<b>Date</b>
и
<b>RegExp</b>
(смотрите врезку ниже). Во втором аргументе передается необязательное заглавие (простая текстовая строка), которую броузер сможет использовать для идентификации сохраненного состояния в истории посещений (например, в меню кнопки Back). В третьем необязательном аргументе передается строка URL, которая будет отображаться как адрес текущего состояния. Относительные URL-адреса интерпретируются относительно текущего адреса документа и нередко определяют лишь часть URL, соответствующую идентификатору фрагмента, такую как #state. Связывание различных состояний приложения с собственными URL-адресами дает пользователю возможность делать закладки на внутренние состояния приложения, и если в строке URL будет указан достаточное количество информации, приложение сможет восстановить это состояние при запуске с помощью закладки.

Структурированные копии

Как отмечалось выше, метод pushState() принимает объект с информацией о состоянии и создает его частную копию. Это полная, глубокая копия объекта: при ее создании рекурсивно копируется содержимое всех вложенных объектов и массивов. В стандарте HTML5 такие копии называются структурированными копиями. Процедура создания структурированной копии напоминает передачу объекта функции JSON. stringif у() и обработку результата функцией JSON.parse() (раздел 6.9). Но в формат JSON можно преобразовать только простые значения JavaScript, а также объекты и массивы. Стандарт HTML5 требует, чтобы алгоритм создания структурированных копий поддерживал возможность создания копий объектов Date и RegExp, ImageData (полученных из элементов <canvas> - раздел 21.4.14) и FileList, File и Blob (описывается в разделе 22.6). Функции JavaScript и объекты ошибок явно исключены из списка объектов, поддерживаемых алгоритмом создания структурированных копий, также как и большинство объектов среды выполнения, таких как окна, документы, элемент и т. д.

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

***********************************************

В дополнение к методу

<b>pushState()</b>
объект
<b>History</b>
определяет метод
<b>replaceState(),</b>
который принимает те же аргументы, но не просто добавляет новую запись в историю посещений, а замещает текущую запись.

Когда пользователь перемещается по истории посещений с помощью кнопок Back и Forward, броузер возбуждает событие «popstate» в объекте

<b>Window</b>
. Объект, связанный с этим событием, имеет свойство с именем
<b>state</b>
, содержащее копию (еще одну структурированную копию) объекта с информацией о состоянии, переданного методу
<b>pushState().</b>

В примере 22.3 демонстрируется простое веб-приложение - игра «Угадай число», изображенная на рис. 22.1, - в которой используются описанные приемы сохранения истории посещений, определяемые стандартом HTML5, с целью дать пользователю возможность «вернуться назад», чтобы пересмотреть или повторить попытку.

Когда эта книга готовилась к печати, в броузере Firefox 4 было внесено два изменения в прикладной интерфейс объекта

<b>History</b>
, которые могут быть заимствованы и другими броузерами. Во-первых, в Firefox 4 информация о текущем состоянии теперь доступна через свойство
<b>state</b>
самого объекта
<b>History</b>
, а это означает, что вновь загружаемые страницы не должны ждать события «popstate». Во-вторых, Firefox 4 более не возбуждает событие «popstate» для вновь загруженных страниц, для которых отсутствует сохраненное состояние. Это второе изменение означает, например, что пример, приведенный ниже, будет работать неправильно в Firefox 4.

JavaScript. Подробное руководство, 6-е издание - i_071.jpg

Примерх 22.3. Управление историей посещений с помощью pushStatef()

<b>&lt;!DOCTYPE html&gt;</b>

<b>&lt;html&gt;&lt;head&gt;&lt;title&gt;I'm thinking of a number...&lt;/title&gt;</b>

<b>&lt;script&gt;</b>

<b>window.onload = newgame;       // Начать новую игру при загрузке</b>

<b>window.onpopstate = popstate;  // Обработчик событий истории посещений</b>

<b>var state, ui;           // Глобальные переменные, инициализируемые в функции newgame()</b>

<b>function newgame(playagain) {  // Начинает новую игру &quot;Угадай число&quot;</b>

<b>  // Настроить объект для хранения необходимых элементов документа</b>

<b>  ui = {</b>

<b>    heading: null, // Заголовок &lt;h1&gt; в начале документа.</b>

<b>    prompt: null,  // Текст предложения ввести число.</b>