22 декабря 2015
17 июня 2008
50
Хороший AJAX
Большинство веб-разработчиков ограничивают себя в использовании аякса, потому что он, как известно, обладает некоторыми распространенными недостатками:
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, либо в конце самого документа.
В итоге у нас получился вот такой сайт.
Архив со скриптами урока
ZIP архив, 29КБ
ZIP архив, 29КБ
Спасибо.
Но недоработок пока еще много: Конпка бэк в опере та же. Поисковикам придется обьяснять почему ваш javascript (а они его уже парсят, хоть и не выполняют) так смахивает на "серый" :)
например тут http://fullajax.ru/
Для оперы необходимо прописать страницу, сразу после первого посещения, если этого не происходит по первому срабатыванию события (у меня не совсем так, как в примере, тут должно работать без этого)...
А Как правильно Нужно Обращаться из 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+"/#", ""));
Не могли бы вы написать решение?
Одних только скиптов вы предлагаете загрузить посетителю столько, сколько весь сайт не весит. Не слишком ли вы обленились?
я пошел глянуть... Пока не отключил жабаскрипт, ссылки внитри их сайта не работали. Наверное, это что-то значит. Наверное, это значит то, что их код глючит, по крайней мере, в некоторых ситуациях).
А не проще ли вместо 70-ти килобайтных страниц (весс чистого ШТМЛ с главной страницы данного сайта), делать страницы в 7 килобайт и перестать беспокоиться о том, что посетитель будет ее долго грузить? К примеру, зачем главная страница со всеми прибамбасами весит 270 килобайт? В 270 килобайт можно было вместить ВЕСЬ этот сайт.
Объясните мне, непонятливому, почему людям нужно делать сверхтяжелые страницы, а потом думать, как бы к ним аякс прилепить? Если графики не много, я сайт-визитку могу вместить в размер одного только скрипта jquery. Сайтостроение - ювелирное занятие. Что тут делать с бульдозером?
Вы начали с указаний на то, что наш пример слишком «тяжелый» в плане кода и потом перешли на приложения в общем смысле. Я надеюсь, вы не считаете, что аяксу как таковому вообще не место в вебе?
Не всегда же есть возможность делать компактные, как вы их назвали, сайты. Бывают случаи (и много), когда 700КБ кода позволяют существенно ускорить работу приложения и облегчить жизнь пользователю. И нормальным разработчикам ясно, когда можно и нужно использовать аякс.
Да, кстати, приведите, пожалуйста, примеры таких сайтов, о которых вы говорите в своих комментариях, чтобы все было наглядно.
Вот решение на jquery. Для него используется 130 килобайт скриптов и сама страница (без рисунков и стилей) весит 28 килобайт. То есть очень много кода. Аналогичное решение без jquery: страница весит 4 килобайта, скрипты 12. То есть, разница между двумя вариантами десятикратная.
Это пример компактного решения и скрипты, которые меняли бы содержимое страницы аяксом весили бы больше, чем сама страница (хотя рисунки в галерее подгружаются именно аяксом, так как их может быть целая тысяча и грузить их все сразу не имеет смысла).
Константин, по показаниям моей Оперы №10.01.1844:
galery.html
Размер страницы: 4 165 байт
Количество встроенных элементов: 28 (171 950 байт)
Итого сами посчитайте. И, между прочим, тыкнул в картинку - так и не дождался самой картинки - все окно позиционировалось со странными подергиваниями...
"Я не понимаю, почему все, что делаю не я, сделано в 10-20 раз тяжелее и медленнее." Браво. )))
--
За статью - большое спасибо, бум копать )
Не общайся с ними, оне лузеры. Ленивые лузеры.
Разве ты не понял это?
Им сложно написать пять строк вместо одной, которая подгрузит полинтернета. Кали Юга, век невежества. И они ещё нас учить пытаются. Просто не общайся с ними да и всё, время теряешь.
Лично я вот пожалел время пока читал весь этот бред. Настроение попортилось даже. Хорошо что хоть твои коменты тут, родственная душа. :)
П. С. У меня периодически возникает впечатление, что программистами становятся выпусники интерната для умственно-отсталых: такое понапридумывают, такое понаворачивают... Мозги закипают! А между тем существуют способы сделать все то же просто и эффективно.
Выгода от этой идеи не много, а если учесть, что всю выгоду надо кодировать в дополнение, то это всё оборачивается большим минусом, т.к., уверен, что тот незначительный прирост производительности выйдет дороже (дешевле железо докупить, чем тратить время программистов на создание и поддержку всего этого). Ну что за выгода может быть? Снижение объёма трафика? Да бросьте. Сколько Вы выиграете, если не будете боковые колонки, шапку и подвал грузить? Несколько килобайт и немного ресурсов сервера, затраченных на gzip и генерацию этих колонок боковых? К тому же все эти боковые колонки, шапки, подвал и прочее закешировать на сервере можно.
А вот, что будет если на какой-то странице колонки боковые должны быть разные (что очень часто бывает)? Или какая-то рандомно генерироваться каждый раз?
А что будет, если страница не загрузится или сервер ошибку вернёт? Надо опять же делать проверку кода ответа сервера.
А как это будет работать на мобильных устройствах?
Да и вообще зачем вся это байда с созданием, по сути, своего недо-браузера?
AJAX не для этого предназначен. Для этого уже есть браузер.
Зачем столько лишнего придумывать?
if ($.browser.msie) if (window.location.hash == '') window.location.href = '!#' + window.location.hash.slice(1);
$("a:not(a[target=_blank],a[rel=no-ajax],a[rel=img],a[href*=mailto:])").live("click", function (e) {
url = $(this).attr("href");
e.preventDefault();
$.ajax({
url: url,
success: function (data) {.....}
typeof (window.history.pushState) == 'function' ? window.history.pushState('', url, url) : window.location.href = '!#' + url.replace('http://' + window.location.host, '');
и понеслась душа в рай// только для ишака лишних пара строчек придётся добавить.
несколько усложнённая реализация тут http://reklamagt.ru/ , для своих проектов упростил код.
Действительно есть чему людей учить, так держать!