AJAX
Что такое AJAX я думаю рассказывать не стоит, ибо с приходом веб-два-нуля большинство пользователей уже воротят носом от перезагрузок страниц целиком, а с появлением jQuery реализация упростилась в разы…
Начнем с самого простого – загрузка HTML кода в необходимый нам DOM элемент на странице. Для этой цели нам подойдет метод "load()". Данный метод может принимать следующие параметры:
url – запрашиваемой страницы
data – передаваемые данные (необязательный параметр)
callback – функция которая будет вызвана при завершении запроса к серверу (необязательный параметр)
Теперь на примерах:
// в элемент с id=content будет вставлен весь HTML с указанной страницы
$("#content").load("/get-my-page.html");
// в элемент с id=content будет вставлен HTML с указанной страницы
// выбранный по указанному селектору #wrapper
$("#content").load("/get-my-page.html #wrapper");
// передаем данные на сервер
$("#content").load("/get-my-page.html", {id:18});
// обрабатываем полученные данные
$("#content").load("/get-my-page.html", function(){
alert("Ничего оригинальней не придумал");
});Из моего опыта работы – вам очень часто придётся пользоваться методом "load()" как описано в первом примере, а еще советую запомнить второй пример, он может выручить, когда надо реализовать загрузку AJAX’ом, а доступа к сервер-сайду у вас нет или он ограничен.
Живой пример:
<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Пример загрузки HTML посредством AJAX и метода load()</title>
<link rel="profile" href="http://gmpg.org/xfn/11"/>
<link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
<link rel="stylesheet" href="css/styles.css"/>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/code.js"></script>
<style>
article {
border:1px dotted #ccc
}
</style>
</head>
<body>
<div id="content" class="wrapper box">
<menu>
<a href="index.html" title="go prev" class="button alignleft" rel="prev">? Back</a>
<a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
<a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
<a href="ajax.datatype.html" title="go next" class="button alignright" rel="next">Next ?</a>
<hr/>
<pre><code contenteditable="true"><em>// AJAX is easy</em>
$(<span>'article'</span>).load(<span>'index.html'</span>)</code></pre>
<button type="button" class="code">Run Code</button>
<pre><code contenteditable="true"><em>// easy to load</em>
$(<span>'article'</span>).load(<span>'index.html header'</span>)</code></pre>
<button type="button" class="code">Run Code</button>
<pre><code contenteditable="true"><em>// easy to handle</em>
$(<span>'article'</span>).load(<span>'index.html article'</span>, function(){
$(<span>'article'</span>).show().fadeOut(2000, function(){
$(this).html('<strong>?</strong>').fadeIn();
});
})</code></pre>
<button type="button" class="code">Run Code</button>
</menu>
<header>
<h1>load()</h1>
<h2>Простой пример использования метода load()</h2>
</header>
<article>
<h2>Пробуем загрузить контент ?</h2>
</article>
<footer>
©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
</footer>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1669896-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</div>
</body>
</html>Следующий метод с которым я вас познакомлю будет "ajax()" – собственно, он тут главный, и все остальные AJAX методы являются лишь обёрткой (и "load()" в том же числе). Метод "ajax()" принимает в качестве параметра пачку настроек и URL куда стучаться, приведу пример аналогичный вызову "load()":
$.ajax({
url: "/get-my-page.html", // указываем URL и
dataType: "html", // тип загружаемых данных
success: function (data) {
// вешаем свой обработчик события success
$("#content").html(data)
}
});Тут мы обрабатывали HTML ответ от сервера – это хорошо когда нам полстраницы обновить надо, но данные лучше передавать в "правильном" формате – это XML – понятно, структурировано, и избыточно, и как-то не совсем JavaScript-way, и поэтому наш выбор – это JSON (wikipedia в помощь):
{
"note": {
"time":"2012.09.21 13:11:15",
"text":"Рассказать про JSONP"
}
}Фактически это и есть JavaScript код как есть (JavaScript Object Notation если быть придирчиво точным), при этом формат уже распространён настолько, что работа с данными в другом формате уже не комильфо.
Жизнь не стоит на месте, есть и более удобные форматы, но не в JavaScript’е :)
Для загрузки JSON существует быстрая функция-синоним – "jQuery.getJSON(url [,data] [,success(data, textStatus, jqXHR)])" – в качестве обязательного параметра лишь ссылка, куда стучимся, опционально можно указать данные, для передачи на сервер и функцию обратного вызова
Нельзя просто так взять и описать все возможные параметры для вызова "ajax()", таки стоит держать официальный мануал под рукой – http://api.jquery.com/jQuery.ajax/
Я неспроста оставляю так много места под ваши заметки:..
Обработчики AJAX событий
Для удобства разработки, AJAX запросы бросают несколько событий, и их естественно можно и нужно обрабатывать. jQuery позволяет обрабатывать эти события для каждого AJAX запроса в отдельности, либо глобально. Приведу схемку на которой наглядно видно порядок возникновения событий в jQuery:
Вот и полный список событий с небольшими ремарками:
ajaxStart — данное событие возникает в случае когда побежал первый AJAX запрос, и при этом других активных AJAX запросов в данный момент нет
beforeSend — возникает до отправки запроса, позволяет редактировать XMLHttpRequest, локальное событие
ajaxSend — возникает до отправки запроса, аналогично "beforeSend"
success — возникает по возвращению ответа, когда нет ошибок ни сервера, ни вернувшихся данных, локальное событие
ajaxSuccess — возникает по возвращению ответа, аналогично success
error — возникает в случае ошибки, локальное событие
ajaxError — возникает в случае ошибки
complete — возникает по завершению текущего AJAX запроса (с ошибкой или без — срабатывает всегда), локальное событие
ajaxComplete — глобальное событие, аналогичное complete
ajaxStop — данное событие возникает в случае, когда больше нету активных запросов
Пример для отображения элемента с "id="loading"" во время выполнения любого AJAX запроса (т.е. мы обрабатываем глобальное событие):
$("#loading").bind("ajaxSend", function(){
$(this).show(); // показываем элемент
}).bind("ajaxComplete", function(){
$(this).hide(); // скрываем элемент
});Это задачка по юзабилити – мы всегда должны держать пользователя сайта в курсе дела о происходящем на странице, и отправка AJAX запроса тоже попадает под разряд "must know". Подобное решение у вас будет практически на любом сайте, где ходит AJAX
Для локальных событий – вносим изменения в опции метода "ajax()":
$.ajax({
beforeSend: function(){
// данный обработчик будет вызван
// перед отправкой данного AJAX запроса
},
success: function(){
// а этот при удачном завершении
},
error: function(){
// этот при возникновении ошибки
},
complete: function(){
// и по завершению запроса (удачном или нет)
}
});Можно глобальные обработчики отключить принудительно используя флаг "global", для этого выставляем его в "false", и пишем функционал в обход событий "ajaxStart" и "ajaxStop":
$.ajax({
global: false,
beforeSend: function(){
// ...
},
success: function(){
// ...
},
error: function(){
// ...
},
complete: function(){
// ...
}
});Данная опция частенько помогает избежать конфликтов при работе с AJAX запросами, но не стоит ей злоупотреблять.
JSONP
JSONP – это наш старый знакомый JSON с прослойкой в виде callback функции О_о. Да ладно, давайте на примерах, вот как у нас выглядит ответ сервера в формате JSON:
{
"note": {
"time":"2012.09.21 13:12:42",
"text":"Рассказать зачем нужен JSONP"
}
}Хорошо, когда у нас эти данные приходят с нашего сервера – обработаем, и всё будет чики-пики, но а если нам потребуется заполучить данные с другого сервера, то политика безопасности в браузерах не позволит отправить XMLHTTPRequest на другой сервер, и надо уже будет что-то придумывать. Можно чуть-чуть напрячься и вспомнить, что подключать JavaScript с другого сервера то мы можем, и он будет выполнен. Вот она – зацепка-то, а если подключаемый скрипт будет содержать вызов нашей функции с подготовленными данными – то это уже что-то:
alertMe({
"note": {
"time":"2012.09.21 13:13:13",
"text":"Каков же профит от использования JSONP?"
}
})Таким образом, описав в своём коде функцию "alertMe()" мы сможем обработать данные с удаленного сервера. Зачастую, сервера ловят параметр "callback" или "jsonp", и используют его как имя функции обёртки:
<script type="text/javascript" src="http://domain.com/getUsers/?callback=alertMe"> </script>
Ну это было предыстория, теперь вернёмся к jQuery и методу "ajax()":
$.ajax({
url: "http://domain.com/getUsers/?callback=?", // указываем URL dataType: "jsonp",
success: function (data) {
// обрабатываем данные
}
});
В запрашиваемом URL наблюдательный читатель заметит незаконченную структуру "callback=?", так вот вместо "?" будет подставлено имя ново сгенерированной функции, внутри которой будет осуществляться вызов функции "success()". Вместо этой прокси-функции можно использовать и свою функцию, достаточно указать её имя в качестве параметра "jsonpCallback" при вызове "ajax()". Оба этих подхода есть в примере:
<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Пример использования JSONP</title>
<link rel="profile" href="http://gmpg.org/xfn/11"/>
<link rel="shortcut icon" href="http://anton.shevchuk.name/favicon.ico"/>
<link rel="stylesheet" href="css/styles.css"/>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/code.js"></script>
<script>
function toJsonContainer(data, status, xhr) {
$('#json').text(data);
}
function buildImages(data) {
$.each(data.items, function(i, item){
$("<img/>").attr("src", item.media.m).appendTo("#images");
if ( i == 3 ) return false;
});
}
</script>
<style>
article pre {
font-size: 14px;
padding: 20px;
}
</style>
</head>
<body>
<div id="content" class="wrapper box">
<menu>
<a href="ajax.script.html" title="go prev" class="button alignleft" rel="prev">? Prev</a>
<a href="index.html" title="back to Index" class="button alignleft" rel="index">Index §</a>
<a href="#" title="reload" class="button alignleft" onclick="window.location.reload();return false">Reload ¤</a>
<a href="ajax.google.html" title="go next" class="button alignright" rel="next">Next ?</a>
<hr/>
<pre><code>$.ajax({
url: <span>"http://anton.shevchuk.name/book/code/ajax/example.php?callback=?"</span>,
dataType: <span>"jsonp"</span>,
success: toJsonContainer
});</code></pre>
<button type="button" class="code">Run Code</button>
<pre><code>$.ajax({
url: <span>"http://anton.shevchuk.name/book/code/ajax/example.php?callback=?"</span>,
dataType: <span>"jsonp"</span>,
jsonpCallback: <span>"toJsonContainer"</span>
});</code></pre>
<button type="button" class="code">Run Code</button>
<h3>Flickr API</h3>
<pre><code contenteditable="true">$.getJSON(
<span>"http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?"</span>,
{
tags: <span>"orange"</span>,
tagmode: <span>"any"</span>,
format: <span>"json"</span>
},
buildImages
);</code></pre>
<button type="button" class="code">Run Code</button>
</menu>
<header>
<h1>Пример использования JSONP с простым сервером</h1>
<h2>Код сервера <a href="http://anton.shevchuk.name/book/code/ajax/example.code">code/ajax/example.code</a></h2>
</header>
<article>
<pre id="json">
</pre>
</article>
<article>
<div id="images">
</div>
</article>
<footer>
©copyright 2014 Anton Shevchuk — <a href="http://anton.shevchuk.name/jquery-book/">jQuery Book</a>
</footer>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1669896-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</div>
</body>
</html>А еще стоит упомянуть, что можно указать как обзывается callback- параметр используя параметр "jsonp", таким образом указав "jsonp:"my"" в URL будет добавлена структура "my=?"
На данный момент достаточно много сервисов предоставляют API с поддержкой JSONP:
- Google – поиск и большинство сервисов
- Yahoo – поиск и большинство сервисов
- Flickr – работа с поиском данного сервиса
- MediaWiki – соответственно и производные – Wikipedia, Wiktionary
- CNET – поиск по новостному порталу
Использование подобного подхода позволяет обходить ограничения накладываемые сервисами на количество запросов с одного IP, плюс не грузит сервер дополнительной работой по проксированию запросов от пользователя к сервисам.
