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, плюс не грузит сервер дополнительной работой по проксированию запросов от пользователя к сервисам.