Хороший AJAX

17 июня 2008, 22:06 Павел Марковнин JavaScript рейтинг +39-

Большинство веб-разработчиков ограничивают себя в использовании аякса, потому что он, как известно, обладает некоторыми распространенными недостатками:

1. Неиндексируемость динамически загружаемого содержимого поисковиками;
2. Невозможность работать с URL (к примеру, добавлять в закладки динамически загруженный материал);
3. Динамически создаваемые страницы не регистрируются в истории браузера, поэтому невозможно использовать клавиши «назад» и «вперед»;

Но, на другой стороне весов имеются неопровержимые преимущества AJAX:

1. Экономия трафика;
2. Уменьшение нагрузки на сервер;
3. Ускоренная работа веб-приложения (это результат первых двух пунктов).

В этом уроке мы расскажем, как преодолеть минусы и полноценно пользоваться плюсами AJAX.

Идеи

1. Все страницы сайта получаем через AJAX, т. е. обновляем только заголовок и содержимое страницы. Это увеличивает скорость работы сайта и снижает трафик между сервером и браузером.

2. У каждой «аяксовой» страницы свой адрес. Например, вы кидаете другу ссылку в аське на такую страницу и он видит именно то, что нужно. Или человек приходит по ссылке на какую-то страницу (скажем, из поисковика) и тоже видит нужную страницу.

3. Понятная навигация — страницы сайта не ссылаются сами на себя. В нашем случаем мы будем просто выделять ссылки на текущую страницу.

4. Рабочие кнопки «назад» и «вперед». Чтобы пользователь работал с нашим сайтом в привычной ему форме.

5.Работа сайта без Javascript. Содержимое сайта будет индексироваться поисковиками и приверженцы текстовых браузеров тоже не останутся в обиде.

Серверная часть

Для начала нам нужен обычный сайт. Кроме того, у нас должна быть возможность получать одну и ту же страницу в форматах HTML (полный код страницы) и XML (только заголовок и содержимое страницы). При этом будет удобно, если html и xml версии будут различаться только одним параметром в адресе. Например, так:

Мы сделали это на PHP, вы же можете выбрать любой другой серверный язык программирования.

Клиентская часть

Для простоты работы возьмем библиотеку jQuery (с ее помощью будем реализовывать AJAX). Для работы с историей браузера понадобиться библиотека unFocus History.

Для начала нам нужно переписать адреса у ссылок и на событие click привязать выполнение AJAX-запроса, отметку ссылок с тем же адресом, что и у текущей, и добавление этого клика в историю посещений. Обратите внимание, что мы не будем переписывать следующие типы ссылок:

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, либо в конце самого документа.

В итоге у нас получился вот такой сайт.

Memori.rumister-wong.combobrdobr.rumoemesto.rudel.icio.uszakladki.yandex.rugoogle.com/bookmarks/
Рекламное место, которое может стать вашим

Понравилась статья?

Тогда подпишись на обновления через RSS или воспользуйся
другими способами подписки.

Читать в Яндекс.Ленте Добавить в Google Добавить в Netvibes
  •  

Комментарии — 29

RSS
  • Аватарка
    18 июня 2008, 9:07 SerjS
    Приятная статья, вполне полезная.
  • Аватарка
    22 июня 2008, 15:59 AztEK
    Все-таки, в 95% случаях XML в AJAX ничем неоправдан. Т.ч. самая первая оптимизация, которую стоить проводить нереходить на JSON.
  • Аватарка
    3 августа 2008, 16:39 Жека
    Ура. Теперь гмэйл стал отображать "хорошие" адреса и в опере.
  • Аватарка
    11 сентября 2008, 21:46 Хер
    Странно как то. С одной стороны подключение Jquery, с другой - использование классического джаваскрипта.Хз чо и думать)
  • Аватарка
    21 сентября 2008, 20:47 sheff88
    jQuery - отличная библиотек. Соглашусь с предыдущим комментом - используете jQuery - пользуйтесь им на протяжении всего кода - это правила хорошего тона коддинга.
  • Аватарка
    1 октября 2008, 8:58 аноним
    в Опере не работает такой способ
  • Аватарка
    8 октября 2008, 15:25 taran2L
    Здравствуйте автор, очень полезная статья, пытаюсь воспользоваться Вашим примером для написания своего сайта, но есть одна проблема. В примере Вы используете метод мод.реврайт для каждой отдельной станици, у меня страниц очень много и это будет не столько объективно приписывать в .htaccess код для каждой страници. Пэтому я пользуюсь одной записью RewriteRule ^(.*)$ index.php [L] и внутри index.php определяю URL браузера и соответвенно реагирую на разные записи, но вот проблема в том, что такой url /#/page1... я не могу определить, перменная $_SERVER[REQUEST_URI] не хочет работать, может подскажите, как решить эту проблему? моя ящик pavel.taran/gmail.com . Если вы уже сталкивались с этим и знаете ответ, помогите пожалуйста.
  • Аватарка
    18 октября 2008, 23:54 Grey
    Пример почему-то отказался работать.
  • Аватарка
    3 ноября 2008, 18:31 diktator
    Интересная идея, но, думаю, что вряд ли ajax приживётся на сайтах в таком качестве.
  • Аватарка
    12 ноября 2008, 11:38 Кос
    А можно ли эту вещь реализовать динамически? то есть, построить cms на основе этого материала? или все равно нужно прописывать все вручную?
  • Аватарка
    27 ноября 2008, 18:33 App
    Видел сайт сделанный по подобной (а может и именно по этой) схеме. Работает нормально, но не индексируется поисковиками... Только главная страница. Актуальна ли эта проблема или просто у автора того сайта кривые руки ?
  • Аватарка
    27 ноября 2008, 18:41 Grin
    App, по нашей схеме все будет индексироваться поисковиками
  • Аватарка
    27 ноября 2008, 19:43 zzz
    Пример на локальном сервере пробовал, но не запускается. Запустил кто нибудь?
  • Аватарка
    27 ноября 2008, 20:24 Grin
    zzz, вы в корень файлы положили?
  • Аватарка
    28 ноября 2008, 14:30 App
    Grin, спасибо
  • Аватарка
    28 ноября 2008, 20:31 zzz
    На локальном сервере я положил в папку www. Вот путь localhost/www/site. В корневой вы имеете в виду непосредственно в localhost/ ?
  • Аватарка
    28 ноября 2008, 20:44 zzz
    Все, заработало! Теперь понял, спасибо
  • Аватарка
    10 декабря 2008, 10:53 Zoltan
    В Opera нужно два раза нажимать на Back чтоб вернутся. Кто сталкивался? Как исправили?
    Спасибо.
  • Аватарка
    15 декабря 2008, 14:27 astons
    в опере 9.62 не работает почему то
  • Аватарка
    12 февраля 2009, 12:12 Виктор
    Отлично.
    Но недоработок пока еще много: Конпка бэк в опере та же. Поисковикам придется обьяснять почему ваш javascript (а они его уже парсят, хоть и не выполняют) так смахивает на "серый" :)
  • Аватарка
    12 февраля 2009, 14:16 Grin
    Виктор, а чем он смахивает на серый?
  • Аватарка
    10 апреля 2009, 17:14 Андрей
    Зачем изобретать велосипед если уже есть готовые решения
    например тут http://fullajax.ru/
  • Аватарка
    10 апреля 2009, 17:18 Grin
    Андрей, мы же написали, что хотели просто показать принципы хорошего аякса. Естественно, наш пример не является полноценным решением, а всего лишь показывает, как правильно реализовывать технологию
  • Аватарка
    13 апреля 2009, 19:26 wavebvg
    На счёт истории, очень понравилось, только есть одно но... В опере заработало только после некоторых волшебных махинаций, если кому будет интересно - отметьте письмом на почту - напишу, что да как тама...
  • Аватарка
    13 апреля 2009, 19:30 wavebvg
    Оо, это у меня надо было изобретать велосипед, тут попроще - надо запретить "переход" на страницу через ссылку и позволить это делать только через js, т.е. посылать return false
    Для оперы необходимо прописать страницу, сразу после первого посещения, если этого не происходит по первому срабатыванию события (у меня не совсем так, как в примере, тут должно работать без этого)...
  • Аватарка
    13 апреля 2009, 19:32 Grin
    wavebvg, напиши нам на pisem@vremenno.net
  • Аватарка
    23 апреля 2009, 20:37 Vladimir
    Здравствуйте!
    А Как правильно Нужно Обращаться из flash!

    У Меня Два Меню Flash, которое находится вверху страницы и html которое находится в низу страницы! Все Вроде Работает Только в Explorer почему то нет!

    Обращаюсь я так:
    Flash: getURL("javascript:GetFlashAjax('overview/story.html')");
    HTML:
    var SetUrl = 'http://'+site+'/' + '#/'+item;
    markLinksWithHref(SetUrl);
    historyVar.getNewHistoryState(SetUrl.replace("http://"+site+"/#", ""));

  • Аватарка
    24 апреля 2009, 15:07 Vladimir
    Можете ответить на мой вопрос Очень срочно нужно!
  • Аватарка
    24 апреля 2009, 15:11 Grin
    Vladimir, я ни разу не делал подобного на флеше, поэтому не могу ничего посоветовать
  • Эл. почта (используется для Граватарки)
  • Домашняя страница
  • Имя в Твиттере
  • Разрешенные теги Текст сообщения (надо бы заполнить это поле)
  • как выглядит какой тег
    жирный текст <b>жирный текст</b>
    курсивный тект <i>курсивный тект</i>
    зачеркнутый текст <s>зачеркнутый текст</s>
    подчеркнутый текст <u>подчеркнутый текст</u>
    ссылка <a href="адрес">ссылка</a>
    function foo() { ... }
    <pre><code>function foo() { ... } </code></pre>