Pull to refresh

Опыт создания системы навигации на Ajax

Reading time 5 min
Views 59K
Меня всегда интересовало, почему при разработке сайтов, так редко в системе навигации используется Ajax? Ведь преимущества по-моему очевидны! Сайт на аякс работает в разы быстрее любого обыкновенного сайта, и даже если учитывать кэш браузера, это заметно.

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

С чего начинаем? Теория


Что мы хотим получить в результате? Получить мы хотим, полноценный сайт, который смогут проиндексировать поисковики, с нормальной системой навигации, и главное — быстрый.

Решение я выбрал следующее: сайт был обыкновенным (без аякс), с обычными ссылками (/news/ или /contacts/), все что я сделал — добавил перехватчик для навигации. При нажатии на ссылку, программа просто посылала POST запрос на ту же самую страницу, только с параметром ajaxLoad, который говорил серверу о том, что отдать нужно лишь часть страницы (без шапки, стилей, скриптов и т. д.), и этот кусок просто подставлялся в нужное место.

Как пример, можно взглянуть на сайт, для которого я реализовал эту систему: wicegoal.com. (сайт пока в разработке, поэтому рекомендую просто погулять по ссылкам навигации, и по странам в самом верху).

В качестве JavaScript библиотеки я выбрал jQuery. Первый вопрос, который необходимо было решить — это ссылки, и навигация браузера. Нужно перехватывать нажатия кнопок браузера назад-вперед, и конечно же, сделать так, чтобы пользователь смог добавить сайт в закладки. Тут есть два варианта, и у каждого свои недостатки:

  • Хэш-ссылки. Хороши тем, что совместимы со всеми браузерами, но обладают большим минусом — с ними нельзя работать в PHP, а из этого вытекает ряд проблем, таких как невозможность определить страницу, после её перезагрузки. На самом деле можно придумать несколько извращенных методов чтобы решить эту проблему, например такой: когда пользователь заходит по ссылке /#page=/news/, просто редиректим его на страницу /news/, но вариант явно не самый лучший, но тем не менее, имеет место быть.
  • HTML5 History API. Этот вариант куда лучше. Представлять его не имеет смысла, это уже сделали за меня, вот только этот вариант обладает одним большим НО: его не поддерживает ни одна версия IE.


Выход конечно же можно найти, например сделать так: для современных браузеров, выбрать History API, а для IE — хэш-ссылки. Плюс, обязательно должна быть версия сайта, без аякс, с которой смогут работать поисковики.

Я решил ограничиться только History API и версией без аякса, чтобы не создавать для себя лишних проблем.

Разработка движка навигации


Первое, что я сделал — создал файл navigator.js, который должен заниматься обработкой ссылок, и поместил туда событие:

if (history.pushState) {
  $('.navigation-menu').live("click", function(){	
      setPage($(this).attr('href'));
      return false;
  })	
}


Здесь, для начала, проверяем, поддерживается ли History API браузером, если да, то вешаем обработчик на все ссылки, с классом navigation-menu, обработчик вызовет функцию setPage:

function setPage(page) {
  $.post(page, { ajaxLoad: true }, function(data){
    $('#content-inner').html(data); 
    NavigationCache[page] = data;
    history.pushState({page: page, type: "page"}, document.title, page);            
  })  
} 


Эта функция отправляет post-запрос, к необходимой нам странице. Стоит обратить внимание на параметр ajaxLoad. Этот параметр необходим серверному скрипту, который будет отдавать нам страницу. Если есть параметр ajaxLoad, сервер отдаст нам только нужную часть страницы, если его нет, сервер отдаст всю страницу целиком, вместе с шапкой, подвалом, навигацией и т. д.
Это необходимо, для того, что при заходе например на страницу site.com/news/ напрямую, мы получили всю страницу целиком, а если мы перейдем на эту страницу например с главной по ajax-ссылке, то получить нужно лишь её часть, которую мы и вставим в блок #content-inner, нам ведь не нужна вся страница, тащить целую кучу лишнего кода бессмысленно.

Есть так же ещё один момент, которым вы наверняка заинтересовались, я говорю об этой строке:

NavigationCache[page] = data;


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

var NavigationCache = new Array();
$(document).ready(function(){
  NavigationCache[window.location.pathname] = $('#content-inner').html();
  history.pushState({page: window.location.pathname, type: "page"}, document.title, window.location.pathname);
});


При загрузке сайта, будет создан массив просмотренных страниц. Как вы возможно заметили, в него изначально добавляется html-код текущей страницы. При каждом переходе по ajax-ссылке, в этот массив будет записываться код страницы.

Теперь нужно повесить обработчик на навигацию браузера. За это отвечает событие History API onpopstate, здесь мы и будем использовать кэш:

window.onpopstate = function(event) {
  if (event.state.type.length > 0) {
    if (NavigationCache[event.state.page].length>0) {
      $('#content-inner').html(NavigationCache[event.state.page]);
    }
  }
} 


Это событие, будет срабатывать, по нажатию кнопок вперед-назад в браузере, все что от него требуется — вставлять в блок #content-inner содержимое из кэша, когда пользователь возвращается на предыдующую страницу.

Что из этого всего получилось?



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

Весь файл navigator.js выглядит так:

var NavigationCache = new Array();
$(document).ready(function(){
  NavigationCache[window.location.pathname] = $('#content-inner').html();
  history.pushState({page: window.location.pathname, type: "page"}, document.title, window.location.pathname);
});

function setPage(page) {
  $.post(page, { ajaxLoad: true }, function(data){
    $('#content-inner').html(data); 
    NavigationCache[page] = data;
    history.pushState({page: page, type: "page"}, document.title, page);            
  })  
} 

$(document).ready(function(){
  if (history.pushState) {
    window.onpopstate = function(event) {
      if (event.state.type.length > 0) {
        if (NavigationCache[event.state.page].length>0) {
          $('#content-inner').html(NavigationCache[event.state.page]);
        }
      }
    } 

    $('.navigation-menu').live("click", function(){ 
        setPage($(this).attr('href'));
        return false;
    })  
  }
})


Перевести сайт, на такой подход довольно просто. Нужно лишь поработать с серверной частью, и научить её отдавать часть или страницу целиком, когда параметр ajaxLoad есть и когда его нет. Стоит ли использовать ajax или нет, это уже дело взглядов, но я не увидел пока что ни единого минуса в таком подходе.
Tags:
Hubs:
+40
Comments 136
Comments Comments 136

Articles