Пользователь
0,0
рейтинг
18 мая 2012 в 10:39

Разработка → HTML5 History API уже сегодня и без ограничений

Библиотека для работы HTML5 History API


Изначально этот проект был задуман добавить поддержку HTML5 History API в старые HTML4 браузеры. Первые версии библиотеки были нацелены именно на эти потребности, но с учетом прошедшего времени и пожеланий многоуважаемых разработчиков использующих эту библиотеку, она выросла до уровня того, что выполняет некие промежуточные действия по добавлению/исправлению того функционала что описаны в спецификациях по интерфейсу History.

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

Использование.

Использование библиотеки очень простое, она максимально пытается придерживаться официальной спецификации по интерфейсу History.

Приведу небольшой пример:
<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript" src="history.js"></script>
        <script type="text/javascript">
            window.onload = function() {

                // просто функция добавляет DIV с нужным нам текстом
                function appendText( text ) {
                    var div = document.createElement( "div" );
                    div.innerHTML = text;
                    document.body.appendChild( div );
                }

                // функция для ссылок обрабатывается при клике на ссылку
                function handlerAnchors() {

                    // заполним хранилище чем нибудь
                    var state = {
                        title: this.getAttribute( "title" ),
                        url: this.getAttribute( "href", 2 ) // двоечка нужна для ИЕ6-7
                    }

                    // заносим ссылку в историю
                    history.pushState( state, state.title, state.url );

                    // тут можете вызвать подгруздку данных и т.п.
                    // ...

                    appendText( '<b>Вы перешли по ссылке:</b> ' +
                                    '<span style="color: green;">' + state.url + '</span>' );

                    // не даем выполнить действие по умолчанию
                    return false;
                }

                // ищем все ссылки
                var anchors = document.getElementsByTagName( 'a' );

                // вешаем события на все ссылки в нашем документе
                for( var i = 0; i < anchors.length; i++ ) {
                    anchors[ i ].onclick = handlerAnchors;
                }

                // вешаем событие на popstate которое срабатывает
                // при нажатии back/forward в браузере
                window.onpopstate = function( e ) {

                    // просто сообщение
                    appendText( '<b>Вы вернулись на страницу:</b> ' +
                        '<span style="color: green;">' + history.location + '</span>' +
                        '<br/><b>state:</b> <span style="color: green;">' +
                        JSON.stringify( history.state ) + '</span><br/><br/>' );

                    // тут можете вызвать подгруздку данных и т.п.
                    // ...
                }
            }
        </script>
    </head>
    <body>
        <h1>Переходите по ссылкам, а затем жмите в браузере кнопки back/forward</h1>
        <a href="/mylink.html" title="Заголовок связанный с ссылкой My Link">My Link</a>
        <a href="/otherlink.html" title="Заголовок связанный с ссылкой Other Link">Other Link</a>
    </body>
</html>

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

Как это работает.

В браузерах HTML5 она выполняет лишь роль исправления багов/ошибок при работе с историей. А ссылки имеют нормальный приятный вид, без использования каких либо hash-fallback.

В браузерах HTML4, конечно же, используется hash-fallback, ибо другого варианта для таких браузеров, конечно же, нет. Но для разработчика внутри JavaScript-кода это не будет являться каким-то недугом, потому как ссылки будут иметь тот вид, что изначально задуманы. И вам не нужно где либо, прописывать hash-ссылки и/или соблюдать правила для поисковиков, что бы те в свою очередь нормально индексировали сайт. Ведь ссылки будут нормального вида, а значит поисковый робот, спокойно перейдет по нужной ссылке.

Функционал библиотеки.

Библиотека имеет небольшие тонкости при работе с ней. Как мы уже заметили, у библиотеки есть собственный объект history.location, он ничем не отличается от известного объекта window.location. То есть одним словом это и есть ссылка на объект window.location за исключением того что в браузерах HTML4 он перехватывает getters/setters у оригинального объекта, делает нужные модификации и возвращает результат.

Способ работы с history.location ничем не ограничен, вы так же можете его назначать, получать, обращаться к его свойствам и т.д.

К примеру, это выглядит так:
    history.location = "http://yandex.ru/"; // произойдет переход на страницу Яндекса.
    history.location.hash = "#newhash";  // просто сменим hash на странице.
    // и так далее по всем свойствам, описанным
    // в спецификации по интерфейсу Location

Следующая тонкость библиотеки это его настройка под ваши нужды. Библиотека не имеет каких-то объектов для настройки, а получает их из параметра GET при подключении библиотеки через HTML-элемент script, то есть считывает ссылку при подключении скрипта к сайту.

Выгладит это примерно так:
<script type="text/javascript" src="history.js?type=/&redirect=true&basepath=/pathtosite/"></script>

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

Параметр: type

Особой роли не играет, он нужен для украшения ссылки в браузерах HTML4. В этом параметре вы можете указать совершенно любую строку/символ, который будет добавлен после знака # (hash), тем самым вы просто украсите ссылку нужным знаком/текстом. К примеру, если я задам этому параметру текст: «HelloWorld/», то ссылки будут иметь эту подстроку. Допустим кликнув по ссылке http://somesite.com/folder/page.html мы получим ссылку вида: http://somesite.com/#HelloWorld/folder/page.html. Вы можете поэкспериментировать с этим параметром. По умолчанию в библиотеке этот параметр имеет подстроку "/" (слеш).

Параметр: basepath

Один из важных параметров, именно по этому параметру библиотека формирует ссылку для объекта history.location. Он указывает, где находиться корень сайта, обычно сайты лежат прямо в корне домена, но иногда бывает нужно сайт положить в иную папку от основного домена. Для этого и был добавлен этот параметр, что бы сайты, находящиеся вне корня могли полноценно работать в браузерах HTML4.

Параметр: redirect

Этот параметр отвечает за переадресацию сайта при переходе по ссылке, скопированной в браузере HTML4 в браузер HTML5. Она просто перенаправляет пользователя перешедшего по ссылке вида: http://somesite.com/#/folder/page.html на ссылку вида http://somesite.com/folder/page.html и наоборот, если пользователь перешел по ссылке из браузера HTML5 в браузер HTML4. Важно помнить, что этот параметр тесно связан с параметром basepath, так как именно по этому параметру он делает выводы, куда перенаправить пользователя.

Заключение.

В заключении хочу добавить, что иногда порой нужно узнать в каком браузере мы находимся, в браузере HTML4 или в браузере HTML5. Для этих целей я добавил свойство emulate в объект window.history указывающее на то происходит эмуляция или нет. Если это свойство имеет значение true, значит, мы находимся в браузере HTML4 и работаем с hash-ссылками, в противном случае иное.

Так же добавлю, что в браузерах HTML5 библиотека исправляет баги/недоделки интерфейса History и все что с ним связано. К примеру, в Safari добавляет объект state в интерфейс History, в браузерах Safari и Chrome убирает initial state (срабатывание события popstate при первой загрузке документа).

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

Библиотека работает с совершенно любыми фреймворками, и не использует их для своих целей.

Библиотека была протестирована в браузерах:
IE 6+
FireFox 3+
Opera 11+
Opera Mobile 11+
Chrome 17+
Safari 5+


Если у вас есть возможность проверить в других браузерах или версиях и вам не составит труда мне отписаться, то я буду очень вам признателен.

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

Скачать и посмотреть данную библиотеку вы можете на GitHub: https://github.com/devote/HTML5-History-API

Так же можете посмотреть работоспособность библиотеки на моем, к сожалению не доделанном сайте: http://history.spb-piksel.ru/. Сам сайт не доделан, но работоспособность библиотеки вы можете на нем посмотреть.

UPD(12.09.2012): Важно! Мне часто стали писать/сообщать о неработоспособности библиотеки в ИЕ9. Проблема связана с тем, что ИЕ9 капризный браузер и придирается ко всем мелочам. Как выяснилось, многие разработчики что используют скрипты взятые с GitHub сталкиваются с проблемами их неработоспособности, потому что ИЕ9 при подключении скриптов ожидает получить заголовок application/javascript, но GitHub возвращает заголовок text/plain. Поэтому подключать скрипты напрямую с GitHub не рекомендуется. Скачайте скрипт к себе на хост и используйте.
Dmitrii Pakhtinov @devote
карма
32,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

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

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

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

  • +4
    Аллилуйя! Лучше и придумать почти нельзя, а где можно, там патчи пришлют.
    • 0
      Спасибо, я постарался учесть все по максимому, но с удовольствием выслушаю любые предложения/улучшения.
      • 0
        А чем вам не угодил проект github.com/balupton/history.js? Вроде как задачи те же, захотелось повелосипедить? :)
        • +7
          Нет, дело не в велосипеде, дело тут в очень простом. Суть данной библиотеки максимально снизить написание лишнего кода. А в будущем ее легко извлечь из проекта, при этом не затрагивая основной код сайта. Например если я буду писать с использованием библиотеки от balupton, и захочу отказаться от старых браузеров, то мне нужно будет переписать основной код сайта что бы вырезать эту библиотеку.

          Еще один недостаток библиотеки представленной вами это громоздкость, весит она в разы больше, а с учетом того что сейчас довольно сильно развивается мобильный интернет, это не особо хорошо для них. Конечно это не огромный аргумент, но все же. Ну и конечно же что бы пользоваться библиотекой от balupton, ее нужно изучить, точнее прочитать документацию по ней. Плюс ко всему та библиотека не работает полноценно с браузером ИЕ7, хотя это тоже не аргумент, но все же маленький но аргумент.
          • +2
            Не аргумент… но рагумент :)
            Отличный инструмент.
          • 0
            History.js от balopton'а использую уже давно. Единственные косяки связаны с urlencode/decode (неверно работает со всякими японскими, китайскими и т.п. языками + избыточно работает с некоторыми системными символами). Чтобы выключить поддержку HTML4-браузеров ничего особенного делать не придется, если вы изначально пишите с умом. Достаточно будет просто загрузить History.js только с поддержкой HTML5.
            • 0
              Я и не заставляю отказаться от привычных инструментов, конечно же каждый выбирает то что ему по душе, тем и пользуется. Но зачем загружать «History.js только с поддержкой HTML5»? Вот этого я не могу понять, да конечно она будет поправлять косяки браузеров, а если их не сегодня а завтра поправят в браузерах? Конечно я утрирую и дождаться от разработчиков браузеров тяжело чего либо, но все же.

              Суть в том что в моем случае я просто отключу библиотеку и сайт не упадет а продолжит работать как работал. Единственное что нужно будет сделать это заранее об этом подумать и прописывать где нужно примерно такой код:
              // .........
              if ( history.pushState ) {
                  // .........
                  var locationObject = history.location || window.location;
                  // .........
              }
              // .........
              

              Тем самым отключение моей библиотеки не нарушит работоспособность сайта в HTML5 браузерах, а в браузерах HTML4 просто будет обычный переход по ссылкам. Хотя конечно дело выбора и желаний.
              • 0
                Ну да. В случае с Балуптоноским методом, простое отключение библиотеки не поможет, так как у него используется объект History, а не history. Но, как и в вашем случае, можно будет сделать довольно быстро работоспособным код этот :)
                Да и не понимаю я, зачем нужно использовать history.location. Если есть pushState.
                • 0
                  Да и не понимаю я, зачем нужно использовать history.location. Если есть pushState.
                  Объект history.location содержит ссылку информирующую нас о том куда мы вернулись по истории, не зависимо от присутствия объекта state. То есть в в браузерах HTML5 для этих целей служит обычный window.location и/или document.location. Это я считаю один из важных параметров в отличии от объекта state, который на мой взгляд ввели избыточно.
          • 0
            Извините, опечатался: balupton'а, конечно.
        • +7
          Такое ощущение что есть специальные задроты которые только и ждут чтобы указать на «велосипеды». Если мир состоял только вами то вы никуда дальше от развития одноклеточных амеоб не дошли бы.
          • +1
            Я лично сам прошел долгий путь от велосипедирования и пришел к выводу что писать что-то свое целесообразно лишь когда:

            1) похожего инструментария вообще нету
            2) есть что-то похожее, но оно совсем не нравится

            Задротства тут никакого не вижу, главное чтобы цели оправдывали средства.
            • +1
              Ну да! зато я могу теперь выбрать что использовать.
            • 0
              А если просто не нравится, а не совсем? Где грань?
              • 0
                Нету никаких граней, это же и коню понятно. Сами определяете границы. Некоторые пишут велосипеды «джаст фор фан» или ради практики в программировании.

                Ничего не имею против велосипедов.
              • 0
                Я в таких случаях форкаю, и делаю так, чтобы «нравилось».

                Осязание грани «велосипеда» и «инновации» считаю талантом. Нужно уметь вовремя остановиться.
  • 0
    Кстати, есть ещё более высокоуровневые framework’и, чтобы удобнее писать код с History pushState. Например, Pages.js: github.com/ai/pages.js
    • 0
      Спасибо, я посмотрю на досуге на эту библиотеку, но сомневаюсь что в ней увижу что-то новое. Хотя моя цель была сделать библиотеку без добавления всяких лишних методов, которые нужно изучать.
      • 0
        Да, тут разные цели. Pages.js не имеет поддержи для старых браузеров, просто framework для упрощения разработки :).
        • 0
          Да вы правы, но при использовании моей библиотеки совместно с библиотекой Pages.js боюсь приведет к плачевному результату лишь по одной причине того что у меня реализован history.location который не будет учитывать Pages.js. Хотя конечно же можно попросить разработчиков той библиотеки добавить поддержку.
          • 0
            Тут вообще глобальный вопрос, должны ли мы поддерживать старые браузеры. History pushState разработан так, что сайт будет работать и без поддержки pushState, просто не так круто. А если у пользователей старых браузеров сайт один-в-один с современными браузерами, то какой смысл обновлять браузер ;).
            • 0
              Да, риторический вопрос, но с заказчиками не поспоришь. Если требуют, то либо делаем либо теряем заказчика, уж таковы реалии и с этим не поспоришь.
              • 0
                Ну для заказчика главное бизнес, чтобы деньги приносил, а не какие технологии используются :). Не думаю, что пользователи особо пострадают, если страница будет грузиться старым способом.

                Тем более, обычно новые технологии отсутствуют на старых медленных браузерах и старых компьютерах. Так что при попытке заставить их показывать «как все», сайт будет просто больше тормозить.
                • 0
                  Про тормоза, например, утечка памяти из-за особенностей сборщика мусора у старых IE — при обычной загрузке память будет очищаться при переходе между страницами.
                  • 0
                    Я посмотрел внимательнее на ваш плагин для jQuery, и вы можете добавить поддержку моей библиотеки изменив всего три строки кода в своем плагине.
                    Это строки 151, 152
                            if ( Pages._lastUrl != location.href ) {
                              Pages.open(location.pathname);
                            }
                    

                    заменить на:
                            if ( Pages._lastUrl != ( history.location || location ).href ) {
                              Pages.open( ( history.location || location ).pathname );
                            }
                    

                    И строку 448
                        Pages._lastUrl = location.href;
                    

                    заменить на:
                        Pages._lastUrl = ( history.location || location ).href;
                    

                    И у вас появиться поддержка моей библиотеки, как видите ничего сложного и трудоемкого производить не нужно.
                • 0
                  Верно подметили, я с вами совершенно согласен. Но люди разные, кто-то имеет иной взгляд на это все.
  • +1
    А это нормально что при несколькоих кликах на 1 и ту же ссылку в историю попадают одинаковые сущности?
    • +2
      Да это совершенно нормально, поэтому это нужно отслеживать самостоятельно. Но это не глюк библиотеки, так как все браузеры HTML5 делают именно так и в спецификации противоречий этому я не нашел.
  • 0
    Я правильно понимаю, что нужно просто подключить эту либу и мое приложение, к примеру на backbone c pushState роутером, будет работать во всех браузерах без fallback-а на хеши?
    • 0
      нет, fallback-а на хеши конечно же будет присутствовать в браузерах HTML4 но для этого не нужно делать лишних телодвижений и писать лишний код. Суть в том если backbone использует при работе History объект window.location то скорее всего эта библиотека не совсем будет выполнять требования backbone.

      К сожалению браузеры HTML4 не позволяют перезагрузить объект window.location и/или document.location, поэтому пришлось добавить дополнительный объект history.location, который и нужно добавить в тот код который тесно связан с объектом window.location при работе с HTML5 History API. В ином случае вполне возможно что библиотека будет корректно работать, но я не тестировал ее совместно с библиотеками которые осуществляют работу с историей.
  • 0
    Я бы допилил в соответствии с рекомендациями гугла, тогда получим индексацию ajax контента искаропки.
    • 0
      Этого не нужно, потому как ссылки в тегах имеют обычный вид, то есть никакого хеша в ссылках, обычные всеми привычные ссылки. В примере указанной в статье это хорошо видно, и робот зашедший на страницу, спокойно может перейти по нужно ссылке.
      • 0
        Ну я бы правда не забывал про ссылки из HTML4 браузеров, которые используют якорь (знак # (hash)). Ведь пользователи старых браузеров тоже могут распространять ссылки с якорями по интернету. Для таких ссылок в вашей библиотеке можно использовать параметр «type=!», если не лень специально для старых браузеров делать поддержку hashbang (#!) на стороне бэкэнда. Я же верно размышляю?

        Кстати, меня, если честно, слегка смутило, то что вы не используете git теги для версий библиотеки, а вместо этого создали директорию «old». С тегами было бы проще и вам и пользователям библиотеки.
        • 0
          Я же верно размышляю?

          Да все верно

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

          Ну я затруднений пока не вижу в этом, на сегодняшний день, версии меняются редко, так как библиотека во всем корректно работает. Ну или просто никто не присылает баг-репорты.
  • 0
    Я когда-то делал что-то подобное, но чисто для history-api (без хеш-навигации)
    github.com/studentIvan/BatmanHand.JS/blob/master/index.html
    • 0
      Кстати, кто знает, есть ли разница между window.onpopstate и window.addEventListener('popstate'...?
      • 0
        разницы нет никакой, разница лишь в том что при использовании addEventListener вы можете не ограничивать себя и назначать на тоже событие новые и новые перехватчики. Суть в том что событие popstate нельзя отменить и перехватить на стадии погружения. Поэтому разница тут не велика.
    • 0
      Вы делали просто обертку для удобства работы с HTML5 History API, моя же библиотека это не обертка, а расширение для HTML4 браузеров, что бы ваша обертка могла работать и в старых браузерах тоже.
  • 0
    Подскажите, что-то нигде не нашел информации о лицензии — под какой лицензией опубликована эта библиотека?
    • 0
      Да, уже нашел:

       * Dual licensed under the MIT and GPL licenses:
       *   http://www.opensource.org/licenses/mit-license.php
       *   http://www.gnu.org/licenses/gpl.html
      


      Вы бы куда-нибудь это на видное место добавили — и в README, и сюда, и в какой-нибудь файл типа LICENSE в корень репозитария…
  • 0
    Спасибо большое за отличную библиотеку.
    Я, с тех пор как прочитал webreflection.blogspot.com/2011/02/btw-getters-setters-for-ie-6-7-and-8.html, так и не смог придумать как же это использовать. А Вам это удалось!

    Форкнул и рефакторю. Написал в личку
    • +1
      Да я тоже очень долго решал как применить то что вы привели по ссылке, хотя по приведенной вами ссылке это сырой способ, просто человек писавший метод objectStatic не полностью осведомлен о реалиях VB, от того его способ имеет кучу ошибок и ограничений, поэтому помимо этого мне пришлось перелопатить еще и MSDN что бы получить более полную картину по скрипту VB
    • 0
      Кстати насчет статьи про getters/setters я их еще применил в этом проекте https://github.com/devote/jsClasses#readme я как то о нем писал на хабре. Если вам интересно конечно их применение не только в библиотеке history.
  • 0
    VB — это Visual Basic? Зачем он используется в этом скрипте?
    • 0
      Для возможности использовать setters/getters в ИЕ<9
  • 0
    Кстати как бы вы поступали с адресами страниц, при которых HTML-содержимое страниц на самом деле не меняется, а изменения применяются только для мультимедиа элементов на самой странице, т.е. адреса на контент которым управляют скрипты. Примеры: адреса слайдов в swf-презентации, адреса определённого видео-фрагмента. Я бы для таких адресов использовал именно якоря (hash), т. к. нужно учитывать, что HTML будет один, и дубликаты однотипного ресурса для поисковиков не хотелось бы создавать.

    Ваша библиотека нормально будет работать при совместном использовании адресов без якорей и с ними в HTML5 и HTML4 браузерах?
    • 0
      Ваша библиотека нормально будет работать при совместном использовании адресов без якорей и с ними в HTML5 и HTML4 браузерах?

      Да мои тесты это проходили успешно, но учитывая то что подобное мало кому нужно. Возможно где-то, может и выползет косяк при попытке использовать это.
  • 0
    Извиняюсь за поднятие старого топика, нашел его в поиске.

    url: this.getAttribute( «href», 2 ) // двоечка нужна для ИЕ6-7

    а можете объяснить, почему? нигде в документации не нашел второго аргумента. или это опечатка?
    • 0
      О втором параметре написано тут msdn.microsoft.com/en-us/library/ie/ms536429%28v=vs.85%29.aspx
      Но так же вы можете почитать на форумах о проблеме получения ссылки у элемента HTMLAnchor средствами getAttribute, в ИЕ6-7 он возвращает абсолютную ссылку, вместо того что реально написано в атрибуте.

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