Javascript.RU

AJAX и history API

Сегодня я решил написать статью про ajax и history API вместе, так как history API без него просто не имеет смысла. Мы напишем универсальную функцию ajax() и модифицированную из прошлой моей статьи функцию go().
Итак, начнем!

Чтобы использовать AJAX нужно создать на странице обьект XmlHttpRequest.
Кроссбраузерно это делается так:

function XmlHttp(){
var xmlhttp;
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");} 
catch (E) {xmlhttp = false;}
}
if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
xmlhttp = new XMLHttpRequest();
}
return xmlhttp;
}

И еще одна вспомогательная функция для передачи POST данных

function ajaxDataMaker(data){
	if(typeof data!="object")return;
	var result="",a=0,c=0;
	for (var i in data)a++;
	for (var i in data){c++;result+= i+"="+data[i]+(c!=a?"&":'')}
	return result;
}

Отлично, это мы сделали!
Теперь перейдем к написанию самой функции ajax() :

function ajax(param){//method,url,data,success
	var req = new XmlHttp();
	var method=(!param.method ? "POST" : param.method.toUpperCase());
	var send=(method=="GET"?null:(param.data?ajaxDataMaker(param.data):null));
	req.open(method, param.url, true);
	req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
	req.send(send);
	req.onreadystatechange = function(){
		if (req.readyState == 4 && req.status == 200) {//если ответ положительный
			if(param.success) param.success(req.responseText);
		}
	}
}

Теперь разберемся, что же тут происходит?

В первой строке функции мы создаем тот самый XMLHttpRequest объект, о котором описано выше:

var req = new XmlHttp();

Далее у нас идет определения типа запроса - GET или POST :

method=(!param.method ? "POST" : param.method.toUpperCase());

Как видите, по умолчанию у меня стоит POST (мне он почему-то больше нравится), вы можете поставить GET, если хотите.
В следующих строках идет подготовка данных для передачи в зависимости от типа запроса:

var send=(method=="GET"?null:(param.data?ajaxDataMaker(param.data):null));

Если мы используем GET запрос, то мы передаем данные прямо через адресную строку и запрос посылаем пустым.
Если у нас тип запроса - POST, мы используем вспомогательную функцию, данную выше.
С этим вроде все.
В следующем шаге мы открываем соединение:

req.open(method, param.url, true);

Отправляем заголовки:

req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.setRequestHeader("X-Requested-With", "XMLHttpRequest");

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

$ajax=$_SERVER['HTTP_X_REQUESTED_WITH']=="XMLHttpRequest"?true:false;

$ajax здесь будет равна true если запрос AJAX, иначе false.
Дальше посылаем запрос:

req.send(send);

Следующим шагом будет долгожданное завершение запроса:

req.onreadystatechange = function(){
if (req.readyState == 4 && req.status == 200)
if(param.success) param.success(req.responseText);
}
ajax({
url:"site.ru/ya.php?act=msg", //думаю, в комментариях не нуждается
method:"POST",//указываем метод. Он и так по умолчанию POST, но это для наглядности
data:{name:"ВАСЯ",id:453,msg:document.getElementById("msg).value},//передаются данные.
success:function(data){document.getElementById('success').innerHTML=data;}
})

В параметре url у нас пишется адрес. Даже если метод POST, все равно можно писать GET запрос.
В data передаются данные. На сервер они придут с такими же именами, как и здесь, то-есть:

$_POST['name']=="ВАСЯ";
$_POST['id']==453;
$_POST['msg']=="значение текстового поля с id='msg'";

И, наконец, success. Сюда передается функция с параметром, в котором содержится ответ от сервера, вы можете назвать его как угодно. То-есть если вы написали:

success:function(server_callback){.....}

То в server_callback будет ответ от сервера.

Теперь перейдем к history API.
Такая штука поддерживается в Google Chrome и начиная с версии 3.6 Mozilla FireFox.
С помощью API можно отслеживать щелчки на кнопки <вперед> <назад> в браузере (от кнопки <обновить> не спасает) и изменять адресную строку не используя хэш,что есть хорошо.
Начнем с изменения адресной строки.За это отвечает функция history.pushState().
Так как поддерживается не везде, нам надо знать, можно ее использовать или нет. Для этого введем переменную:

var histAPI=!!(window.history && history.pushState);

Она принимает значение true если поддерживается History API, иначе - false.
Теперь давайте напишем саму функцию.

function go(param,event,success) {
	var url,success=success||param.success||false,event=event||param.event||false;
	if(typeof param.href!="undefined")url=param.href;
	else {url=(typeof param=="string"?param:(typeof param.url=="object" ? param.url.href : param.url));}
	if(!histAPI){window.location=url;return true;}
	if(event && event.button==1)return true;
		ajax({
			url:(typeof param.data!="undefined" ? url+"&"+param.data : url),
			method:"GET",
			data:param.data,
			success:function(data){
				history.pushState(null,null, url);
			}
		})
		return false;
}
<a href="index.php" onclick="return go(this,event,function(data){alert(data)})">Главная</a>
function popstate(url) {
	ajax({
		url:url,
		method:"GET",
		success:function(data){
			//Ну, здесь пишете что надо делать с полученными данными
// например  так
document.getElementById("content").innerHTML=data;
			}
	})
}

Здесь, все немного по-другому. Надо вешать обработчик на событие popstate объекта window. Делается это так:

window.onload=function(){
if (histAPI) { 
window.setTimeout(function() {
window.addEventListener("popstate",
 function() {
popstate(location.pathname);
}, 
false);
	}, 1);
}
}

При загрузке страницы, если поддерживается history API, к навигационным кнопкам привязывается функция popstate.
Если событие popstate не ловить, то адрес страницы будет просто меняться, а контент будет оставаться неизменным. Поэтому мы ловим это событие, и вызываем функцию, которая отправляет запрос по текущему адресу и вставляет данные в нужное место страницы.

Я объяснил все, что знал, спасибо за прочтение. Если вы заметили ошибку или у вас есть вопрос - напишите это пожалуйста.
И если уж не сложно, напишите комментарий, как понравилось, все-таки первая большая статья, интересно ваше мнение.
Сливаев Дмитрий.

+12

Автор: walik, дата: 6 июня, 2011 - 21:46
#permalink

Ошибочка в строке:

for(var i in data)

Нужно было указать:

for(var i in param.data)

Статья очень хорошая, как раз хотел почитать про History API.


Автор: Jude, дата: 13 июня, 2011 - 02:06
#permalink

Да, статья супер, всё расписано, но вот последние два скрипта я не понял, зачем это надо вообще? Если для управления кнопками, то ничего не происходит:(
Может есть у кого рабочий пример целиком?


Автор: KiTE, дата: 15 июня, 2011 - 15:35
#permalink

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


Автор: arbitsv, дата: 1 июля, 2011 - 11:38
#permalink

Здравствуйте!

По поводу AJAX-части статьи...
Я новичок. Подскажите пожалуйста, можно ли сделать следующее:

Мне нужно чтобы функция ajax(param) не только принимала мои параметры, но и сама возвращала request.responseText в мою пользовательскую функцию, из которой была вызвана, а не вызывать для этой цели еще одну пользовательскую функцию

То есть:
function my_func()
{
var result = ajax(param);

}


И в этой же функции делать с результатом все что необходимо.

Что-то типа:
сначала return request.responseText
в функции req.onreadystatechange = function()
и далее return полученного результата в функции ajax(param)

У меня никак это не получается... А хочется именно так

Заранее благодарен всем откликнувшимся.


Автор: pharrell, дата: 27 июля, 2012 - 01:23
#permalink

JS - событийно ориентированный язык. На нём нельзя сделать так, как вы хотите)


Автор: Yes, дата: 26 августа, 2011 - 09:47
#permalink

функция Ajax ошибка в строке 11. Вот как я её исправил

for (var i in param.data){send+= i+"="+param.data[i]+"&";}

Автор: ЭмМэмДэмс, дата: 2 сентября, 2011 - 02:24
#permalink

ошибка нубы

functionv go(param)

исправте днища


Автор: Гость (не зарегистрирован), дата: 11 сентября, 2011 - 15:16
#permalink

Пример History API совершенно не понятный.
Вот живой пример без Ajax:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>HTML5 History API test</title>
</head>
<script type="text/javascript">
function set(text,e)
{
var e = e || window.event;
e.preventDefault();
element=e.target || e.srcElement; 

document.getElementById('h1').innerHTML=text;

history.pushState({text:text},text,element.href); // добавляем новый елемент истории
document.title=text;
}

window.onload=function()
{

history.replaceState({text:document.getElementById('h1').innerHTML},document.title,location); // при загрузке обновляем текущий элемент истории

// Ключевая часть всего History API без которой он абсолютно бесполезен.
// Навешивает обработчик события на кнопки браузера Вперед и Назад.
// Узнать какая именно была нажата кнопка Вперед или Назад нельзя , но это и не нужно. Это за нас делает сам браузер.
window.addEventListener('popstate', function(e){
document.getElementById('h1').innerHTML=e.state.text; // e.state == обьект переданный в функции history.pushState , а именно {text:text}
document.title=e.state.text;
}, false);


}
</script>
<body>
     <div id="content">
         <h1 id="h1">Текст</h1>
         <div>
		     <a href="test1" onclick="set('клик по тесту 1',event);">тест 1</a> 
             <a href="test2" onclick="set('клик по тесту 2',event);">тест 2</a> 
			 <a href="test3" onclick="set('клик по тесту 3',event);">тест 3</a>
		 </div>
     </div>
</body>
</html>

Автор: pharrell, дата: 13 октября, 2011 - 19:24
#permalink

Не хочу вас обижать, но по-моему это немного бред.
Во первых

var e = e || window.event;
e.preventDefault();

А вы уверены что у пользователя поддерживается history API ? Если он не поддерживается, все попытки пользователя щелкнуть на ссылку будут изначально обречены на неудачу.
Во вторых,

history.replaceState({text:document.getElementById('h1').innerHTML},
document.title,location);

Кто-нибудь объяснит мне, зачем мы это делаем???
И это вопрос как от чайника

element=e.target || e.srcElement;

Что это вообще такое и для чего?


Автор: Гость (не зарегистрирован), дата: 20 октября, 2011 - 17:03
#permalink
$ajax=$_SERVER['HTTP_X_REQUESTED_WITH']=="XMLHttpRequest"?true:false;
// --
$ajax=$_SERVER['HTTP_X_REQUESTED_WITH']=="XMLHttpRequest";

Автор: pharrell, дата: 3 января, 2012 - 01:29
#permalink

Спасибо!) Че то даже забыл про этот способ!)


Автор: Гость (не зарегистрирован), дата: 7 января, 2012 - 10:55
#permalink

На самом деле можно передать абсолютно любой заголовок.
"Синтаксис
void setRequestHeader(String имя, String значение)
Аргументы
имя
Имя устанавливаемого заголовка. Этот аргумент не должен содержать пробелы,
двоеточия, символы перевода строки или листа.
значение
Значение заголовка. Данный аргумент не должен содержать символы перевода
строки или листа."


Автор: pharrell, дата: 25 февраля, 2012 - 23:42
#permalink

Я это знал, но все равно спасибо за информацию. Просто такой заголовок шлет jQuery, и я решил не изобретать новый.


Автор: Гость (не зарегистрирован), дата: 26 января, 2013 - 13:11
#permalink

На мой взгляд не очень удобочитаемое форматирование кода, местами встречаются переменные с именами в духе a, b, c, что не способствует лучшему пониманию алгоритма. Пробелов явно не хватает между выражениями - всё сливаетсяводнустрокуитруднопонятьчтотам происходит. Думаю, что если бы комментарии/объяснения были прямо в коде, то это также повысило шанс разобраться в вопросе. В инете (да ине этом сайте) примеров навалом как лучше оформлять.


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
9 + 8 =
Введите результат. Например, для 1+3, введите 4.
 
Поиск по сайту
Другие записи этого автора
pharrell
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Популярные таги
Последние комментарии
Последние темы на форуме
Forum