Опубликован: 28.02.2016 | Уровень: для всех | Доступ: платный
Лекция 8:

AJAX

< Лекция 7 || Лекция 8: 12 || Лекция 9 >

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

< Лекция 7 || Лекция 8: 12 || Лекция 9 >
Наталья Маркова
Наталья Маркова
Ярослав Гаевой
Ярослав Гаевой

10 марта 2016 c 20:13 до 22:39 я сдавал экзамен. Однако, за два месяца статус не изменился: "Задание не проверено"

Когда ожидать проверки?

Руслан Жанбосынов
Руслан Жанбосынов
Россия
Дмитрий Молокоедов
Дмитрий Молокоедов
Россия, Новосибирск, НГПУ, 2009