Website development
12 July 2011

Создание одностраничного ajax-приложения с поддержкой History API (и без нее)

Судя по комментариям в этой статье, создание веб-приложений с возможностью аякс-навигации является интересной для сообщества темой и пока еще немногие сталкивались с подобной задачей. Я расскажу о ее решении с помощью небольшой библиотеки под названием jQuery-Pjax (либо моего форка ее).

Моя мотивация: в проекте нужно было реализовать mp3-плеер, играющий независимо от навигации на сайте. Далее потребовалось добавить поддержку браузеров без pushState — и я сделал форк библиотеки.

Основные особенности

  • навигация по сайту и обработка форм без полной перезагрузки страниц
  • чистые url, доступные для прямого доступа
  • поддержка #!/hash для устаревших браузеров (добавлено в моей версии)
  • работа с кнопками «назад» и вперед» для современных браузеров
  • а теперь и для старых — благодаря benalman.com/projects/jquery-hashchange-plugin
  • похоже, есть проблемы с ИЕ7 (спасибо Nc_Soft) (тем более, большое спасибо за участие Nc_Soft) и, возможно, opera 11.5 (пока не могу подтвердить, но нахожу крайне удивительным из-за dev.opera.com/articles/view/introducing-the-html5-history-api) — сообщение artishok — проверено и работает на сборке 1074 (not_ice)
  • imsamurai (https://github.com/imsamurai) предложил улучшения библиотеки (и я радостью слил изменения): встроенная функция для отправки форм, улучшения работы с хешами и более развитая система триггеров. (у imsamurai, к сожалению, нет аккаунта на хабре — будем рады помощи)

Ссылки


Принцип работы Pjax


Основа — раздельный вывод и кеширование обычных запросов и запросов со специфическими заголовками. То есть, в контроллере перед выводом шаблона страницы, необходима проверка наличия заголовка X-PJAX, например, так:

PHP:
if (!isset($_SERVER['HTTP_X_PJAX'])
{ // here is regular-kind load }
else
{    // here you don't print page layout — just the page }


Далее, на ссылки подвешивается событие, которое передает управление навигацией яваскрипту (это самая радужная и ненавязчивая реализация — а особого смысла в других я не вижу). В моем случае есть нечто вроде:

$('#pjaxcontainer a:not(.logout-link,.login-link,.login_link,[id*="login_link"],[href*="#"],[target="_blank"],'
      +'[href$="mp3"],[href$="jpg"],[href$="jpeg"],[href$="gif"],[href$="png"],[href$="doc"],[href$="pdf"])')
   .pjax('#pjaxcontainer', {
      timeout: 0
   });


Для обработки форм мы используем:

$('#pjaxcontainer form').live('submit',function(a){

     // display loading message
      $('#loading-shade').show();

      if( !$(a.target).attr('action'))
         a.target = $(a.target).closest('form');

      data = $(a.target).serialize();

      cont = $('#pjaxcontainer');

      $.ajax({
         type: "POST",
         url: $(a.target).attr('action'),
         data: data,
         beforeSend : function(xhr) {
            return xhr.setRequestHeader('X-PJAX','true'); // IMPORTANT
         },
         success: function(msg){
            cont.html(msg);
            $('#loading-shade').hide();
         },
         error: function(a,b,c) {
            $('#loading-shade').hide();
         }
      });

      a.preventDefault();
      return false;
   });


Но вы, конечно, можете использовать определенный класс или атрибут data-pjax для выборки ссылок.

Также есть два полезные события:
  $('body').bind('start.pjax',function() {
      setTimeout("$('#loading-shade').hide();",2000); // to be sure that loading message hides
      $('#loading-shade').show();
   });
   $('body').bind('end.pjax',function() {
      $('#loading-shade').hide();
   });


Более подробную информацию о Pjax вы найдете в README.md на github.

Допиливание хеш-навигации


К создателю библиотеки несколько раз обращались с просьбой добавить поддержку хешей, но его позиция оказалась принципиальной ( github.com/defunkt/jquery-pjax/issues/3#issuecomment-986233, github.com/defunkt/jquery-pjax/issues/3#issuecomment-1353555, github.com/defunkt/jquery-pjax/issues/3#issuecomment-1354589). Потому я слегка модифицировал ее, добавив автоматический переход на #!/хеши и свободную конвертацию адресов двух типов.

Однако, в моей модификации необходимо указать два параметра:
$.siteurl = 'http://yousite.com';
$.container = '#pjaxcontainer';


Заключение


Таким образом мы в несколько строк кода реализуем одностраничное веб-приложение. Буду благодарен за комментарии и любые багтреки.

Feel free to fork and roll your own © defunkt

Спасибо Terion за наводку.

image => image

Планы


Работа над этим проектом оказалась крайне познавательной — потому есть, что рассказать еще. В следующей статье я напишу о создании многофункционального аудио-видео плеера с плейлистом на основе jPlayer (http://jplayer.org/).



Важные комментарии


Devgru, 12 июля 2011, 20:43 #

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

Devgru, 12 июля 2011, 23:29 #

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

И да, вроде бы пока это только гугл.
Я не использовал этот функционал, только читал.


+79
50.4k 402
Comments 63
Top of the day