30 June 2011

Введение в HTML5 History API

JavaScript
Translation
Original author: Mike Taylor, Chris Mills
До появления HTML5 единственное, что мы не могли контролировать и управлять (без перезагрузки контента или хаков с location.hash) — это история одного таба. С появлением HTML5 history API все изменилось — теперь мы можем гулять по истории (раньше тоже могли), добавлять элементы в историю, реагировать на переходы по истории и другие полезности. В этой статье мы рассмотрим HTML5 History API и напишем простой пример, иллюстрирующий его возможности.

Основные понятия и синтаксис


History API опирается на один DOM интерфейс — объект History. Каждый таб имеет уникальный объект History, который находится в window.history. History имеет несколько методов, событий и свойств, которыми мы можем управлять из JavaScript. Каждая страница таба(Document object) представляет собой объект коллекции History. Каждый элемент истории состоит из URL и/или объекта состояния (state object), может иметь заголовок (title), Document object, данные форм, позиция скролла и другую информацию, связанную со страницей.

Основные методы объекта History:
  1. window.history.length: Количество записей в текущей сессии истории
  2. window.history.state: Возвращает текущий объект истории
  3. window.history.go(n): Метод, позволяющий гулять по истории. В качестве аргумента передается смещение, относительно текущей позиции. Если передан 0, то будет обновлена текущая страница. Если индекс выходит за пределы истории, то ничего не произойдет.
  4. window.history.back(): Метод, идентичный вызову go(-1)
  5. window.history.forward(): Метод, идентичный вызову go(1)
  6. window.history.pushState(data, title [, url]): Добавляет элемент истории.
  7. window.history.replaceState(data, title [, url]): Обновляет текущий элемент истории

Для перехода на 2 шага назад по истории можно использовать:
history.go(-2)

Для добавления элементов истории мы можем использовать history.pushState:
history.pushState({foo: 'bar'}, 'Title', '/baz.html')

Для изменения записи истории мы можем использовать history.replaceState:
history.replaceState({foo: 'bat'}, 'New Title')

Живой пример


Теперь мы знаем основы, давайте посмотрим на живой пример. Мы будем делать веб файловый менеджер, который позволит вам найти URI выбранного изображения(посмотрите то, что получится в конце). Файловый менеджер использует простую файловую структуру, написанную на JavaScript. Когда вы выбираете файл или папку картинка динамически обновляется.
image
Мы используем data-* атрибуты для хранения заголовка каждой картинки и используем свойство dataset для получения этого свойства:
<li class="photo">
  <a href="crab2.png" data-note="Grey crab!">crab2.png</a>
</li>

Чтобы все работало быстро мы подгружаем все картинки и обновляем атрибут src динамически. Это ускорение создает одну проблему — оно ломает кнопку назад, поэтому вы не можете переходить по картинками вперед или назад.

HTML5 history приходит на помощь! Каждый раз когда мы выбираем файл создается новая запись истории и location документа обновляется (оно содержит уникальный URL картинки). Это означает, что мы можем использовать кнопку назад для обхода наших изображений, в то время как в строке адреса у нас будет прямая ссылка на картинку, которую мы можем сохранить в закладки или отправить кому-либо.

Код


У нас есть 2 дива. Один содержит структуру папок, другой содержит текущую картинку. Все приложение управляется с помощью JavaScript. В будут освещены только самые важные моменты. Исходный код примера очень короткий (порядка 80 строк) посмотрите его после прочтения всей статьи.

Метод bindEvents навешивает обработчики для события popstate, который вызывается, когда пользователь переходит по истории и позволяет приложению обновлять свое состояние.
window.addEventListener('popstate', function(e){
  self.loadImage(e.state.path, e.state.note);
}, false);

Объект event, который передается в обработчик события popstate имеет свойство state — это данные, которые мы передали в качестве первого аргумента pushState или replaceState.

Мы навешиваем обработчик на событие click на див, который представляет нашу файловую структуру. Используя делегацию событий, мы открываем или закрываем папку или загружаем картинку (с добавлением записи в историю). Мы смотрим на className родительского элемента для того, чтобы понять на какой из элементов мы нажали:
— Если это папка мы открываем или закрываем её
— Если это картина, то мы показываем её и добавляем элемент истории

dir.addEventListener('click', function(e){
  e.preventDefault();
  var f = e.target;

  // Это папка
  if (f.parentNode.classList.contains('folder')) {
    // Открываем или закрываем папку
    self.toggleFolders(f);
  } 
  // Это картинка
  else if (f.parentNode.classList.contains('photo')){
    note = f.dataset ? f.dataset.note : f.getAttribute('data-note');
    
    // отрисовываем картинку
    self.loadImage(f.textContent, note);
    // добавляем элемент истории
    history.pushState({note: note, path:f.textContent}, '', f.textContent);
  }
}, false);

Метод, который изменяет содержимое картинки и обновляет её подпись очень прост:
loadImage: function(path, note){
    img.src = path;
    h2.textContent = note;
}

Мы получили простое приложение, демонстрирующее возможности обновленного интерфейса объекта History. Мы используем pushState для добавления элемента истории и событие popstate для обновления содержимого страницы. Кроме этого при клике на картинку мы получаем в адресной строке её действительный адрес, который мы можем сохранить или отправить кому-нибудь.

Когда можно будет использовать?


Firefox 4+
Safari 5+
Chrome 10+
Opera 11.5+
iOS Safari 4.2+
Android 2.2+
IE ???
Список браузеров, поддерживающих history API

Где это используется?


1. Facebook
2. Github — навигация по дереву проекта
3. ???

Почитать


1. Manipulating History for Fun & Profit
2. WHATWG HTML5 history API
3. W3C history API Spec
4. MDC Manipulating the browser history
5. History.js — скрипт эмулирует HTML5 history API(location.hash magic) в тех браузерх, которые его не поддерживают
Tags:javascripthtml5history apiwhatwg
Hubs: JavaScript
+51
212.9k 571
Comments 22
Popular right now
Javascript разработчик
from 130,000 to 180,000 ₽ArtezioНижний Новгород
Javascript разработчик
from 160,000 to 220,000 ₽ArtezioМосква
Javascript разработчик
from 2,800 to 3,300 $ArtezioМинск
Javascript разработчик
from 160,000 to 220,000 ₽ArtezioСанкт-Петербург
Javascript разработчик
from 2,800 to 3,300 $ArtezioМогилев
Top of the last 24 hours