Пользователь
0,0
рейтинг
30 июня 2011 в 15:37

Разработка → Введение в HTML5 History API перевод

До появления 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) в тех браузерх, которые его не поддерживают
Перевод: Mike Taylor, Chris Mills
Mikhail Davydov @azproduction
карма
448,5
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (22)

  • 0
    Как долго к этому приходили… Мне все это приходилось вручную реализовывать.
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Сейчас с HTML5 History API мы можем отлавливать кнопки вперед, назад, используя метод pushState и событие popstate. В статье все хорошо описано. И в примере все работает (потыкайте по картинкам, а потом нажмите кнопку назад/вперед). HTML5 History API похож на location.hash magic, но только без магии и вместо хеша мы имеем реальный url, который можем кому-нибудь отправить или загрузить реальный документ тем же wget'ом.
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Думаю, это сделано из соображений безопасности. Т.е. мы можем использовать только те стейты, которые создали сами, а не брать чужие.
            • НЛО прилетело и опубликовало эту надпись здесь
              • 0
                Ваша правда
      • 0
        Думаю лучше, стандарт должен быть кроссбраузерным по идее. А когда свою навигацию строишь, приходится отлавливать такие глюки, как например в опере — когда несколько раз нажмешь на ссылку с одним якорем, потом ровно столько же раз нужно отмотать назад, при этом другие браузеры переходы по идентичным хэшам второй раз не фиксирут. Я пока этот ньюанс узнал, уйму времени потерял.
  • 0
    только вот если нажать на «Refresh» в броузере то откроется только картинка. Судя по всему без костылей пока не обойтись.
    • 0
      Это пример так сделан коряво. Нормально History API работает. Сам пробовал для нормальных браузеров его использовать, а для паранормальных по старинке с обновлением страницы. Ничего сложного в этом нет, не считая того факта, что Chrome посылает события об изменении урла при первом открытии страницы, что есть моветон (кстати в статье о подобных нюансах ни единого слова...).
      • 0
        Это не баг примера, а его фича:
        Кроме этого при клике на картинку мы получаем в адресной строке её действительный адрес, который мы можем сохранить или отправить кому-нибудь.

        Если бы мы немного подправили код на сервере, то могли бы получать состояние как при закрытии.
  • 0
    «Опера» 11.50 под Мак, кнопки истории в примере остаются неактивными.
    • 0
      Windows 7 — аналогичною. Баг оперы вестимо. Кнопки проявляются после нажатия Backspace.
  • 0
    Было бы здорово, если бы можно было говорить счетчикам посещаемости, что сделан переход на следующую страницу или страницу назад, в общем чтобы считчики как обычно считали переходы по сайту, может их код тоже целиком перезагружать… но тогда нужно наверное очищать некоторые переменные и объекты, которые они создали… никто не сталкивался?
    • 0
      В этом случае лучше перезагружать код, чтобы счетчик думал, что пользователь на самом деле перешел по ссылке.
    • +1
      Для гуглосчетчика это сделать элементарно. Для него можно на яваскрипте регистрировать любые произвольные события. Подробнее можно почитать вот тут
  • 0
    Кстати, Вконтакте использует это уже Х месяцев, благодаря этому флэш-плеер всегда на виду и играет даже при переходах на др. страницы, молодцы ребята.
    Автор, спасибо за статью, давно ждал!
  • +1
    Для jQuery, кстати, есть библиотеки для работы с историей, которые используют history API в новых браузерах и манипуляции с location.hash в старых. Я в одном проекте использовал jQuery BBQ.
  • 0
    а вместо: if (history.state){
    не должно быть if (e.state){
    • 0
      имеется в виду 25-ая строчка people.opera.com/miket/2011/6/app.js — потому что только с таким исправлением у меня работает обновление path, note из state-а.
  • 0
    Добавили бы в статью ссылку сюда habrahabr.ru/post/144071/ — polyfill для старых браузеров
  • –3
    Очень хорошо реализовано на shotme.ru

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.