Большинство веб-разработчиков ограничивают себя в использовании аякса, потому что он, как известно, обладает некоторыми распространенными недостатками:
1. Неиндексируемость динамически загружаемого содержимого поисковиками;
2. Невозможность работать с URL (к примеру, добавлять в закладки динамически загруженный материал);
3. Динамически создаваемые страницы не регистрируются в истории браузера, поэтому невозможно использовать клавиши «назад» и «вперед»;
Но, на другой стороне весов имеются неопровержимые преимущества AJAX:
1. Экономия трафика;
2. Уменьшение нагрузки на сервер;
3. Ускоренная работа веб-приложения (это результат первых двух пунктов).
В этом уроке мы расскажем, как преодолеть минусы и полноценно пользоваться плюсами AJAX.
Идеи
1. Все страницы сайта получаем через AJAX, т. е. обновляем только заголовок и содержимое страницы. Это увеличивает скорость работы сайта и снижает трафик между сервером и браузером.
2. У каждой «аяксовой» страницы свой адрес. Например, вы кидаете другу ссылку в аське на такую страницу и он видит именно то, что нужно. Или человек приходит по ссылке на какую-то страницу (скажем, из поисковика) и тоже видит нужную страницу.
3. Понятная навигация — страницы сайта не ссылаются сами на себя. В нашем случаем мы будем просто выделять ссылки на текущую страницу.
4. Рабочие кнопки «назад» и «вперед». Чтобы пользователь работал с нашим сайтом в привычной ему форме.
5.Работа сайта без Javascript. Содержимое сайта будет индексироваться поисковиками и приверженцы текстовых браузеров тоже не останутся в обиде.
Серверная часть
Для начала нам нужен обычный сайт. Кроме того, у нас должна быть возможность получать одну и ту же страницу в форматах HTML (полный код страницы) и XML (только заголовок и содержимое страницы). При этом будет удобно, если html и xml версии будут различаться только одним параметром в адресе. Например, так:
- HTML версия страницы — /page1/page 1-1.html;
- XML версия — /page1/page 1-1.html?ajax=yes.
Мы сделали это на PHP, вы же можете выбрать любой другой серверный язык программирования.
Клиентская часть
Для простоты работы возьмем библиотеку jQuery (с ее помощью будем реализовывать AJAX). Для работы с историей браузера понадобиться библиотека unFocus History.
Для начала нам нужно переписать адреса у ссылок и на событие click привязать выполнение AJAX-запроса, отметку ссылок с тем же адресом, что и у текущей, и добавление этого клика в историю посещений. Обратите внимание, что мы не будем переписывать следующие типы ссылок:
- внешние ссылки, т. е. ссылки на страницы других сайтов;
- ссылки на картинки, архивы и прочие файлы (расширения таких файлов указываем в переменной forbiddenTypes);
- ссылки с заранее указанным классом (forbiddenClass) — если внутри сайта есть страницы, которые необходимо грузить целиком.
function setupNewLinks(obj)
{
var links = obj.getElementsByTagName("a"); // все ссылки внутри объекта obj
// перебираем ссылки
for (i = 0; i < links.length; i ++)
{
// проверяем, можно ли переписывать адрес текущей ссылки
//(проверка по имени класса, расширению и "внешности" ссылки :-)
// если можно, то переписываем
tmp = links[i].href;
tmp = tmp.replace("http://"+site, "");
indexOfDot = 0;
if(tmp.lastIndexOf(".") > 0) indexOfDot = tmp.lastIndexOf(".");
if(forbiddenTypes.match(tmp.substring(indexOfDot)) == null && tmp.substring(0, 7) != "http://" && links[i].className != forbiddenClass)
{
// прописываем, что происходит при клике на ссылку
$(links[i]).bind("click", function()
{
markLinksWithHref(this.href);
historyVar.getNewHistoryState(this.href.replace("http://"+site+"/#", ""));
this.className = "curPage";
});
// заменяем адрес
links[i].href = "#" + tmp;
}
}
}
Параметром функции указываем объект страницы, внутри которого надо искать ссылки.Вначале указываем весь документ (setupNewLinks(document)), при каждом обновлении контента — только этот контент (setupNewLinks(document.getElementById(’’content’’))).
Сейчас при клике на ссылку ее класс меняется на curPage. Но нам нужно отметить все такие ссылки внутри страницы. Для этого используем вот такую функцию. Ее аргумент — адрес текущей ссылки.
// с помощью этой функции мы отмечаем все ссылки
// с одинаковым значением href
function markLinksWithHref(linkHref)
{
var links = document.getElementsByTagName("a");
if(navigator.appName == 'Microsoft Internet Explorer') linkHref = linkHref.replace("http://"+site+"/", "");
for (i = 0; i < links.length; i ++)
{
if(links[i].href == linkHref || links[i].href == linkHref+"/") links[i].className = "curPage";
else links[i].className = "";
}
}
Мы получили данные в формате XML и теперь нам нужно их распарсить и заменить заголовок и содержание текущей страницы. Аргументом функции будет полученный XML.
function parseGetData(data)
{
//получаем заголовок и содержание страницы из XML
title = data.getElementsByTagName('title');
cnt = data.getElementsByTagName('content');
//вставляем это в нашу страницу
$('#content').html(cnt[0].firstChild.nodeValue);
document.title = title[0].firstChild.nodeValue;
// обновляем ссылки внутри страницы
setupNewLinks(document.getElementById('content'));
markLinksWithHref(window.location.href);
}
Итак,мы реализовали пункты 1 и 3. Сделаем пункт 2. При каждой загрузке страницы (или клике на кнопки «назад» или «вперед») мы будем проверять адрес и показывать пользователю нужную ему страницу. Заметим, что проверка при первой загрузке и переходе из истории немного отличаются.
function checkAnchorOnload()
{
// определяем полный путь и якорь страницы
var urlPathName = location.pathname;
var urlAnchor = location.hash;
// проверяем адрес, если пользователь первый раз
//вошел на сайт. Если в адресе есть якорь, то
// подгружаем страницу
if(start == 'yes')
{
if(urlPathName != "/")
{
location.href = "http://"+site+"/#"+urlPathName;
}
else if(urlAnchor != "" && urlAnchor.indexOf("#/") != -1)
{
getDataAjax(urlAnchor.replace("#", ""));
}
}
// примерно такая же проверка, но только при нажатии
// "назад" или "вперед" в браузере
else if(start == '')
{
if(urlPathName == "/")
{
if(urlAnchor == "" || (urlAnchor == "#" && navigator.appName == 'Microsoft Internet Explorer'))
{
getDataAjax("http://"+site+"/");
}
else if(urlAnchor.indexOf("#/") != -1)
{
getDataAjax(urlAnchor.replace("#/", ""));
}
}
}
}
Рассмотрим функцию, которая будет «слушать» изменение истории и реагировать на нажатия кнопок «назад» и «вперед».
function historyHandler() {
var stateVar = "nothin'", displayDiv = document.getElementById("content");
this.getNewHistoryState = function(currentState) {
var newVal = currentState;
unFocus.History.addHistory(newVal);
};
this.historyListener = function(historyHash) {
stateVar = historyHash;
// при каждом изменении истории мы будем
// выдавать соответствующий контент
checkAnchorOnload();
};
unFocus.History.addEventListener('historyChange', this.historyListener);
this.historyListener(unFocus.History.getCurrent());
};
var historyVar;
Мы написали все необходимые функции, осталось только «привязать» их к странице.
function init()
{
setupNewLinks(document); // переписываем адреса ссылок
historyVar = new historyHandler(); // запоминаем историю
start = '';
}
Лучше всего вызывать эту функцию либо при загрузке DOM, либо в конце самого документа.
В итоге у нас получился вот такой сайт.