DMakeev ([info]dmakeev) wrote in [info]webdistortion,
  • Location: 8/24
  • Music: Nouvelle Vague — Just Can't Get Enough

Ajax-переходы и кнопка Back

Задача в двух словах: нужно фиксировать события, выполняемые JavaScript`ом, в истории браузера.

Исходные данные: есть сайт, переходы между страницами реализованы ajax`ом.

Задача:
а. Заставить кнопки Back и Forward (и все остальное что связано с history бразузера) работать как подобает.
б. Пользователи должны иметь ссылку на ТЕКУЩУЮ страницу, в не зависимости от того, сколько переходов было совершено. Если пользватель зашел на страницу А и перешел на странцу Б, он должен иметь ссылку на страницу Б, причем с минимальным включением мозга.



Решение:

Для начала решим вторую задачу:
Просто в лоб менять url в браузере не получится - произойдет переход. Обойти этот пунктик врядли получится - такое поведение жестко зашито в BOM (Browser Object Model) и любое отклонение от него есть баг. Единственная часть URL, которая может быть безболезненно изменна - это якорь. Им и займемся:
  1. При каждом переходе меняем якорь страницы.
    Т.е. если человек находится на странице /about/ и переходит на /shop/, то урл должен стать примерно таким: /about/#url=/shop/. Теперь пользователь может копировать ссылку из адресной строки и посылать ее друзьям.
  2. В заголовок страницы (страниц) добавляем JS, который проверяет URL загружаемой страницы и, если в ней есть наш якорь, то осуществляет редирект на нее. Выглядит это примерно так:
    <script type="text/javascript">
    <!-- 
        if(document.location.hash && document.location.hash.indexOf('url=') > -1) {
            document.location = new String(document.location.hash).replace('#url=','');
        }
    -->
    </script>

    При этом код должен быть в заголовке страницы (между <HEAD> и </HEAD>) и не должен выноситься в .js-файл - тогда переход будет осуществляться ДО того как начнется прориосвка страницы.
Теперь пользователь, открывая в браузере страницу /about/#url=/shop/ будет перебрасываться на /shop/, т.е. задача б решена.
Теперь займемся основной задачей
  1. Основная сложность заключается в том, что браузеры по разному отрабатывают движение по history. Если FireFox и Opera воспринимают изменение якоря как отдельное событие в history, то IE их в упор не замечает. Также прибавляет хлопот практически не управляемый объект history.
  2. FireFox и Opera:
    При нажатии на Back / Forvard в адресной сторке либо меняется якорь либо осуществляется преход на прошлую страницу. Если переход, то никаких действий не требуется, если же меняется якорь, нужно это изменение отловить и переправить посетителя на нужную страницу.
    1. В JS создаем глобальную переменную, в которую при открытии страницы и каждом ajax-переходе пишем текущий url (с якорем).
    2. Добавляем таймер (setInterval(...)), который через каждую секунду проверяет соответствие этой переменной фактическому URL в браузере. Если не соответствует, осуществляем переход на тот, что указан в браузере.
  3. Internet Explorer:
    При нажатии Back/Forward происходит переход на предыдующую страницу, все изменения якоря игнорируются. Нам нужно заставить IE отработать все изменения якоря как отдельные переходы. Для этого:
    1. В JS (или в PHP - не суть) проверяем браузер пользователя и, если это IE, добавляем скрытый IFRAME.
    2. В папку /myajax/ кладем .htaccess, который перенаправляет все запросы несуществующих страниц на файл-обработчик. .htaccess должен содержать примерно следующее:
          RewriteEngine On
          RewriteCond %{REQUEST_FILENAME} !-f
          RewriteCond %{REQUEST_FILENAME} !-d
          RewriteCond %{REQUEST_FILENAME} !-l
          RewriteRule (.*) index.php?qs=$1 [QSA]

      Если mod_rewrite или .htaccess не доступны, можно реализовать аналогичную логику при помощи страницы 404.
    3. При загрузке страницы, а также при каждом ajax-переходе в iframe загружаем URL вида: /myajax/наш_урл (т. е при переходе пользователя на страницу /about/ будет /myajax/about/). Это позволяет нам фиксировать переходы для IE.  Кроме того, при нажатии Back переход осуществится не в основном документе, а в  iframe.
    4. В файле /myajax/index.php нам нужно создать JS-код, который при загрузке страницы будет проверять, соответствует ли его URL (за исключением /myajax/) тому что загружено в основном окне браузера. Если нет - осуществить переход. Реализация может выглядеть примерно так:
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
      <html><head>
      <script language="JavaScript" type="text/javascript">
      <!--
          function historyGo() {
              if(window.parent) {
                  var parentUrl = '';
                  var newUrl = '<?=str_replace("/myajax/", "", $_SERVER["REQUEST_URI"]);?>';
                  var parentLoc = window.parent.location;
                  if(parentLoc.hash && parentLoc.hash.indexOf('url=')>-1) {
                      var sss = new String(parentLoc);
                      parentUrl = new String(sss.substr(sss.indexOf('url=')+4));                   
                  }
                  else {
                      parentUrl = new String(parentLoc.href.replace('http://'+parentLoc.hostname, ''));
                  }
                  parentUrl = parentUrl.replace('/myajax/', '');
                  if(parentUrl != newUrl) {
                      if(window.parent.document.changePage) {
                          window.parent.document.changePage(newUrl);
                      }
                      else {
                          window.parent.location.href = newUrl;
                      }
                  }
              }
          }
      //-->
      </script>
      </head><body onload="historyGo()">&nbsp;</body></html>
    5. Пробуем
Вот в общем виде и все. Вероятно некоторые куски кода можно было бы и упростить, но в данном случае мне более важен сам принцип работы, а не оптимальность исполнения.
Несколько подводных камней кроется в самой организации якорей (к примеру, при обработке экшнов форм), если интересно, опишу.

Комментарии и бросание помидоров в автора приветствуется.


Где внедрено: evraz.com
Tags: ajax, web 2.0, фичи

  • Post a new comment

    Error

    Your IP address will be recorded 

  • 0 comments
Create an Account
Forgot your login or password?
Facebook Twitter More login options
English • Español • Deutsch • Русский…