Введение
History API представляет собой одно из интересных новшеств HTML 5. Благодаря ему появляется возможность навигации по истории одного таба браузера без перезагрузки страницы, при этом браузер корректно отрабатывает функции "назад" и "вперед".
Это дает замечательные возможности при использовании History API совместно с Ajax. Теперь нет необходимости использовать традиционную конструкцию #!, можно просто заменить URL целиком. Благодаря этому мы получаем следующие преимущества:
-
- в URL теперь отображается реальный адрес страницы, пользователи могут спокойно копировать ссылку на страницу из адресной строки браузера и распространять ее;
- отказ от конструкции #! при использовании Ajax позволяет не беспокоиться о потерянных для индексации поисковыми системами ссылках;
- ссылки просто становятся чище и красивее.
В этой статье мы кратко рассмотрим HTML5 History API и создадим простой пример с использованием плагина History.js для популярного Javascript фреймворка jQuery. В качестве примера реализуем постраничную Ajaxнавигацию на странице листинга продуктов Magento.
Обзор HTML5 History API
За работу с историей отвечает объект History. Мы можем получить этот объект для текущего таба через readonly ссылку window.history.
Основные методы и свойства объекта History:
window.history.stateВозвращает текущий объект истории. С помощью этого объекта можно получить данные, которые передаются методами pushState и replaceState путем доступа к свойствам этого объекта.
window.history.lengthСвойство lenght показывает количество записей в истории.
window.history.go(n)Переход к определенной позиции в истории, в качестве аргумента передается смещение относительно текущий позиции. Этот метод существовал до появления HTML5.
window.history.back()Переход к предыдущему элементу в истории, идентично вызову go(- 1).
window.history.forward()Переход к следующему элементу в истории, идентичный вызову go(1).
Методы window.history.back() и window.history.forward() также существовали до HTML5.
window.history.pushState(data, title [, url])Добавляет новый элемент в историю. Метод принимает три параметра:
- Данные состояния истории. Эти данные можно получить затем в обработчике события popstate. Если дополнительные данные не требуются можно передавать null;
- Заголовок страницы, который отобразится в окне браузер, так же можно передавать null;
- URL, который должен отображаться в адресной строке.
Пример:
history.pushState({param: 'Value'}, '', 'myurl.html');
Если текущий URL был http://yoursite.com/path/to/page.html, то он будет заменен на http://yoursite.com/path/to/myurl.html, как если бы мы перешли по ссылке обычным способом.
URL, передаваемый третьим параметром, может быть как относительным так и абсолютными, но использование Ajax накладывает ограничение на использование только текущего домена.
window.history.replaceState(data, title [, url])Этот метод очень похож на pushState и имеет те же параметры. Отличие заключается в том, что данный метод не добавляет новую запись, а изменяет текущую запись в истории.
popstateЭто событие срабатывает при переход от одного элемента истории к другому. При этом history.pushState() и history.replaceState() не приводят к вызову этого события. Только нажатие кнопок вперед/назад в браузере, либо вызов history.back() или аналогичной функции в Javascript.
Пример:
window.addEventListener('popstate', function(e) { // код обработчика события });Поддержка браузерами
History Api и jQuery
Существует интересный плагин History.js, который предоставляет единый интерфейс для всех популярных браузеров для работы с историей просмотра. Он базируется на HTML5 History API и содержит практически те же самые методы, что и объект window.history. Используя данный плагин, можно не беспокоиться о работе скрипта в старых браузерах. Если браузер не имеет поддержки History API, то для его эмуляции будет использован # (якорь URL).
Для обращения к функциям плагина используется объект History. Например, вызов функции History.pushState(data,title,url) практически аналогичен вызову history.pushState. Детально ознакомиться с Api плагина можно на его сайте. Однако, есть 2 момента, на которые стоит обратить внимание:
- Плагин использует собственное событие «statechange» вместо стандартного «popstate» для определения момента перехода по истории;
- Методы History.pushState и History.replaceState приводят к вызову события «statechange», тогда как аналогичные методы объекта window.history нет.
Для демонстрации работы плагина добавим Ajax с динамической сменой URL к стандартной постраничной навигации продуктов Magento.
Скачать плагин можно по этой ссылке https://github.com/browserstate/history.js/ . Нам потребуется только один файл jquery.history.js из папки scripts/bundled/html4+html5 (минимизированная версия с поддержкой HTML4). Подключаем файл плагина и библиотеку jQuery, если она еще не подключена. В версии Magento 1.9.0.1, которую я использовал для этого примера, jQuery подключен по умолчанию, поэтому мне достаточно подключить только файл плагина.
Копируем jquery.history.js в папку /skin/frontend/< your_package>/< your_theme>/js/lib/. В моем случае это папка skin/frontend/rwd/default/js/lib/. В файле разметки page.xml текущей темы в в самый конец блока «head» добавляем:
<block type="page/html_head" name="head" as="head"> ... <action method="addItem"><type>skin_js</type><name>js/lib/jquery.history.js</name></action> </block>
Кроме этого, нам потребуется отдельный файл для инициализации плагина и написания собственного кода обработчиков событий. Создадим файл main.js и так же поместим его в папку текущего скина по адресу skin/frontend/< your_package>/< your_theme>/js/.
Подключаем файл main.js:
<block type="page/html_head" name="head" as="head"> ... <action method="addItem"><type>skin_js</type><name>js/lib/jquery.history.js</name></action> <action method="addItem"><type>skin_js</type><name>js/main.js</name></action> </block>
В файл main.js добавляем следующий код:
(function ($) { $(function(){ var $categoryProducts = $('.category-products'); $categoryProducts.on('click', '.pages a', function(e){ e.preventDefault(); History.pushState(null, document.title, $(this).attr('href')); loadPage($(this).attr('href')); }); function loadPage(url) { $categoryProducts.load(url + " .category-products > *"); } }); })(jQuery);
Мы использовали именно такой синтаксис для избежания конфликтов с библиотекой Prototype, которую Magento использует по умолчанию.
(function ($) { // код })(jQuery);
Мы добавили обработчик события «click» на ссылки постраничной навигации и отменили стандартное поведение ссылок с помощью метода e.preventDefault. Затем мы используем History.pushState для добавления нового элемента в историю и в фоновом режиме загружаем новую страницу с помощью функции loadPage.
$categoryProducts.load(url + " .category-products > *");
Этот код загружает страницу по указанному URL с помощью Ajax и помещает в элемент $categoryProducts содержимое блока с классом '.category-products'.
Рассмотрим подробнее вызов метода History.pushState.
History.pushState(null, document.title, $(this).attr('href'));
Первым параметром передаем null, так как нам не требуется передавать дополнительные параметры в объект State. Вторым параметром передаем текущий заголовок документа, поскольку при переходе на следующую страницу нам не нужно менять заголовок. И третьим параметром передаем новый URL — ссылка, на которую нажал пользователь.
Для проверки работы этого примера нужно зайти в любую категорию с товарами, где количество товаров достаточно для появления постраничной навигации, и перейти на любую другую страницу. После этого URL в адресной строке заменяется на новый, как если бы мы перешли по этому URL, и после небольшой паузы подгружаются товары с новой страницы.
Наш пример справляется со своей задачей, однако при нажатии кнопки «назад» в браузере ничего не происходит. Для обработки события перехода истории нам требуется назначить обработчик на событие «statechange».
History.Adapter.bind(window, 'statechange', function(e){ var State = History.getState(); loadPage(State.url); });
Поскольку событие «statechange» срабатывает всякий раз, когда происходит переход по истории, нам больше не требуется вызывать функцию loadPage после History.pushState.
После внесения необходимых правок, наш код выглядит следующим образом:
(function ($) { $(function(){ var $categoryProducts = $('.category-products'); $categoryProducts.on('click', '.pages a', function(e){ e.preventDefault(); History.pushState(null, document.title, $(this).attr('href')); }); function loadPage(url) { $categoryProducts.load(url + " .category-products > *"); } History.Adapter.bind(window, 'statechange',function(e){ var State = History.getState(); loadPage(State.url); }); }); })(jQuery);
Теперь при нажатии в браузере кнопок вперед/назад срабатывает событие «statechange» и содержимое страницы корректно обновляется. Для наглядного отображения процесса загрузки немного изменим функцию loadPage, добавив изменение прозрачности блока с продуктами на время загрузки контента.
function loadPage(url) { $categoryProducts.css({opacity: 0.5}); $categoryProducts.load(url + " .category-products > *", function(){ $categoryProducts.css({opacity: 1}); }); }
На этом наш простой пример завершен. Как видите, использование History API не представляет особых сложностей. Вы легко можете использовать красивые URL в Ваших Ajax-приложениях, не боясь проблем с поисковиками, а использованный в примере плагин позволит избежать проблем в старых браузерах.
Спасибо за внимание!