Урок - jQuery.ajax()

Группа вКонтакте
Лицензия: Лицензия Creative Commons.

Метод jQuery.ajax() дает команду выполнить асинхронных HTTP (Ajax) запрос.

Синтаксис метода jQuery.ajax()

jQuery.ajax( url [, settings ] )

 где

  • url - string, путь на который следует отправить данные.
  • settings - PlainObject, набор пар ключ-значение, для указания параметров AJAX- запроса. Любой параметр не обязательный. По умолчанию параметры могут быть выставлены в  методе jQuery.ajaxSetup().

Метод jQuery.ajax() возвращает jqXHR-объект.

 Описание метода jQuery.ajax().

Метод jQuery.ajax() лежит в основе всех AJAX-запросов отправляемых бибдиотекой jQuery. В большинстве случаев нет необходимости использовать на прямую метод jQuery.ajax(), вместо него можно использовать альтернативные методы jQuery.get() и jQuery.post(), которые проще в использовании (меньше параметров). Однако метод jQuery.ajax() гибче в использовании.

В простейшем случае метод jQuery.ajax() может быть вызван без параметров:

jQuery.ajax();

В этом примере все параметры берутся из jQuery.ajaxSetup().

Результатом работы будет получение содержимого текущей страницы с сервера, однако ничего с полученными данными происходить не будет.

jqXHR-объект.

Метод jQuery.ajax() возвращает jqXHR-объект. jqXHR-объект является расширением XMLHttpRequest-объекта браузера. Например, он содержит свойство responseText, свойство responseXML, метод getResponseHeader(), метод overrideMimeType().

В случае, если транспортный механизм не является XMLHttpRequest-объектом (например, JSONP-запрос), библиотек jQuery попытается использовать XMLHttpRequest-объект там, где это возможно.

jqXHR-объекты возвращаемые методом jQuery.ajax() реализуют интерфейс Promise, предоставляя им все свойства, методы и поведение Promise. Эти методы принимают одну или несколько функций в качестве аргументов, вызываемые методом jQuery.ajax(), когда запрос завершается. Такой подход позволяет назначить несколько обработчиков на один AJAX-запрос и даже после отправки запроса. (Если запрос завершен, обработчик будет исключен.)

Доступны следующие методы интерфейса Promise в jqXHR-объекте:

  • jqXHR.done()
  • jqXHR.fail()
  • jqXHR.always()
  • jqXHR.then()

Метод jqXHR.done().

jqXHR.done(function(data, textStatus, jqXHR) {});

Метод jqXHR.done() являтся альтернативой обработчика успешного заврешения AJAX-запроса. Заменяет устаревший метод jqXHR.success().

Метод jqXHR.fail().

jqXHR.fail(function(jqXHR, textStatus, errorThrown) {});

Метод jqXHR.fail() являтся альтернативой обработчика ошибки при AJAX-запросе. Заменяет устаревший метод jqXHR.error().

Метод jqXHR.always().

jqXHR.always(function(data|jqXHR, textStatus, jqXHR|errorThrown) { });

Метод jqXHR.always() являтся альтернативой обработчика заврешения AJAX-запроса. Заменяет устаревший метод jqXHR.complete().

Обратите внимание, что при успешном запросе, аргументы метода jqXHR.always() такие же, как у метода jqXHR.done(), а при AJAX-запросе с ошибкой такие же как у метода jqXHR.fail().

Метод jqXHR.then().

Метод jqXHR.then() включает в себя функциональность методов jqXHR.done() и jqXHR.fail().

Примечание:

Методы jqXHR.success(), jqXHR.error(), jqXHR.complete() являются устаревшими с версии jQuery 1.8 и в будущем будут удалены. Используйте, соответственно, методы jqXHR.done(), jqXHR.fail(), jqXHR.always().

Пример 1:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script type="text/javascript">
        /**
         *    Выводим сообщение в элемент log.
         */
        function logAdd(msg) {
            jQuery('#log').append(msg+'<br>');
        }
        
        
        /**
         *    Инициализация работы примера
         */
        jQuery(function() {
            jQuery('button').each(function(EL) {
                //    назначаем события для <button>'s
                jQuery(this).on('click', eval(this.id + 'OnClick'));
            });
        });
        
        
        /**
         *    jqXHR-объект для примера
         */
        var jqxhr;
        
        
        /**
         *    Событие onclick для кнопки button1
         */
        function button1OnClick() {
            //    выполняем AJAX-запрос
            jqxhr = $.ajax("example1-response.php",{
                    'beforeSend': function() {
                        logAdd('beforeSend');
                        jQuery('#button1').prop('disabled', 'disabled');
                    }
                })
                .done(function() {
                    logAdd('done')
                })
                .fail(function() {
                    logAdd('fail')
                })
                .always(function() {
                    logAdd('always')
                })
            ;
            //    через 2 секунды добавляем к jqXHR-объекту обработчики jqXHR.always(), jqXHR.done(), jqXHR.fail()
            setTimeout(function() {
                logAdd('setTimeOut')
                jqxhr.always(function() {
                    logAdd("always 2");
                    jQuery('#button1').prop('disabled', '');
                });
                jqxhr
                    .done(function() {
                        logAdd("done 2");
                    })
                    .fail(function() {
                        logAdd("fail 2");
                    })
                ;
                jQuery('#button2').show();
                jQuery('#button3').show();
            }, 2000);
        }
        
        
        /**
         *    Событие onclick для кнопки button2
         */
        function button2OnClick() {
            //    добавляем новый обработчик jqXHR.done()
            jqxhr.done(function() {
                logAdd("done 3");
            });
        }
        
        
        /**
         *    Событие onclick для кнопки button3
         */
        function button3OnClick() {
            //    добавляем новый обработчик jqXHR.always()
            jqxhr.always(function() {
                logAdd("always 3");
            });
        }
        
        
    </script>
</head>
<body>
    <p>
        Пример работы метода jQuery.ajax().
    </p>
    <fieldset>
        <legend>Кнопки</legend>
        <button id="button1">1. Выполнить AJAX-запрос</button> - выполняем AJAX-запрос.
После завершения запроса выполняется задержка на 2 секунды, для добавления обработчиков<br>
        <button id="button2" style="display:none">2. Добавить обработчик jqXHR.done()</button><br>
        <button id="button3" style="display:none">3. Добавить обработчик jqXHR.always()</button><br>
    </fieldset>
    <fieldset id="result">
        <legend>result</legend>
    </fieldset>
    <fieldset id="log">
        <legend>log</legend>
    </fieldset>
</body>
</html>

Ссылка this, внутри обработчика указывает, по умолчанию указывает на настройки AJAX-вызова, но может быть изменена с помощью свойства jqXHR.contexts:

jQuery.ajax({
    url: "example.php",
    context: document.body
}).done(function() {
    $(this).addClass("done");
});

Для обратной совместимости jqXHR-объекта и XMLHttpRequest-объекта, jqXHR-объект будет выставлять следующие свойства и методы:

  • readyState
  • status
  • statusText
  • responseXML и/или responseText, когда AJAX-ответ вернул xml и/или text, соответственно.
  • setRequestHeader(name, value), который отличается от стандартного заменой старого значения на новое, а не объединеним старого и нового значения.
  • getAllResponseHeaders()
  • getResponseHeader()
  • statusCode()
  • abort()

Нет обработчика onreadystatechange, однако методы jqXHR.done(), jqXHR.fail(), jqXHR.always() с параметром statusCode охватывают все возможные варинаты.

Очередь вызова обработчиков.

Методы jqXHR позволяют добавлять несколько обработчиков для каждого события, по принципу очереди "Первый Вошел Первый Вышел" (FIFO - First In First Out).

Выполнение обработчиков jQuery.ajax() заключаются в следующем:

  1. Вызываются обработчки назначенные beforeSend. В качестве аргументов передаются jqXHR-объект и Settings-объект (настроеки).
  2. Вызываются обработчики назначенные с помощью jqXHR.error(). В качестве аргументов пердается jqXHR-объект, строка указывающая тип ошибки, Exception-объект (исключение). Некоторые встроенные ошибки будут передаваться строкой в Exception-объекте.
  3. После успешного получения данных вызываются обработчики dataFilter(). Обработчики получают возвращаемые данные и значения типов данных, должен вернуть (возможно внести изменения) данные, чтобы перейти к обработчику отвечающему за успешность AJAX-запроса.
  4. В случае успешного AJAX-запроса данные передаются в метод success(). Передаются возвращаемые данные, строка (с кодом успешного завершения), jqXHR-объект.
  5. Обработчики Promise: .done(), .fail(), .always(), .then() - вызываются в порядке их регистрации.
  6. В завершении запроса вызываются обработчик .complete(), как в случае ошибки, так и в случае успешного завершения. Обработчик получает jqXHR-объект и  строку с кодом (с кодом ошибки или с кодом успешного завершения).

Типы данных.

Метод jQuery.ajax() опирается на информацию полученную от сервера о типе передаваемых данных. Если сервер сообщает о передаче данных в формате XML, то результат может быть обработан с помощью обычных методов XML или селекторов jQuery. Если передан другой тип, например HTML, данные рассматриваются как текст.

Различные методы обработки могут быть достигнуты с помощью параметра dataType. Параметр dataType может иметь следующие значения:

  • html
  • json
  • jsonp
  • script
  • text

Данные типов text и xml возвращаются без обработки. Данные просто передаются в обработчик success, либо через jqXHR.responseText, либо через jqXHR.responseXML.

Примечение: Мы должны убедиться, что MIME-тип сообщаемый сервером соответсвует параметру dataType. В частности XML должен быть объявлен на сервере как text/xml или application/xml, для корректных результатов.

Если указан тип html, любой JavaScript-код внутри полученных данных будет выполнен до возврата HTML в виде строки. Кроме того, сценарий будет выполнять JavaSctipt-код, который вытащил обратно с сервера, и затем ничего не вернет.

Если указан тип JSON, то происходит анализ данных и создается JavaScript-объект. Для этого используется jQuery.parseJSON(), если браузер поддерживает этот метод, в противном случае используется конструктор. Если данные JSON-строки не корректны, то будет выдана ошибка (смотрите json.org, для дполнительной информации). Формат JSON очень удобен для передачи структурированных данных, также легко разбирается в структуры JavaScript. Тип JSONP следует указывать в случае кроссдоменного запроса.

Если указан тип JSONP, то к строку запроса добавляется параметр "callback". Сервер должен обернуть JSON-данные в значение параметра callback, чтобы сформировать правильный ответ. Также мы можем указать название параметра отличного от callback с типом JSON еще до вызова jQuery.ajax().

При получении данных с удаленных серверов (которые возможно только при помощи тега <script> или данных типа JSONP), ошибки обратного вызова и глобальные события никогда не будут вызваны.

Отправка данных на сервер.

По умолчанию, AJAX-запрос отправляет даннные GET-методом. Если необходимо использовать POST-метод отправки данных, необходимо изменить параметр type. Этот параметр влияет как будут отправляться содержимое параметра data на сервер. Данные передаваемые POST-методом будут передаваться в кодировке UTF-8, в соответсвии со стандартом W3C XMLHTTPRequest.

Параметр data может быть задан строкой в виде: <ключ1>=<значение1>[&<ключ2>=<значение2>[&<ключ3>=<значение3>]] или в виде JavaScript-объекта: {key1: 'value1', key2: 'value2' ...}. Если вы используете второй вариант, данные преобразуются в строку запроса с использованием метода jQuery().param() перед отправкой. Вызов jQuery.param() может быть отменен путем уставновки параметра precessData в false. Обработка может быть не желательной, если вы хотите отправить XML-объект на сервер; в этом случае измените параметр contentType c application/x-www-form-urlencoded на более подходящий MIME-тип.

Дополнительные параметры.

Параметр global позволяет регистрировать обработчики для использования .ajaxSend(), .ajaxError() и подобные им методы, что бы вызывать их в случае запроса. Это может быть полезно, чтобы отключать индикаторы загрузки, который был реализован в .ajaxSend(), если запросы являются частыми и кратковременными. Для кросс-доменных скриптов и JSONP-запросов, параметр global автоматически устанавливается в false. Смотрите описание этих методов ниже, для более подробной информации.

Если сервер выполняет HTTP-авторизацию, то логин и пароль могут быть отправлены через параметры username и password.

AJAX-запросы ограничены во времени, по этому ошибки могут быть перехвачены и обработаны, чтобы обеспечить лучшее пользовательское взаимодействие. Значения таймаутов, как правило или оставляют по умолчанию, или устанавливают в качестве глобадльного значения используя метод jQuery.ajaxSetup(), вместо того, что бы переопределять для каждого запроса.

По умолчанию, запросы всегда отправляются, но браузер может возвращать результаты из своего кэша. Чтобы запретить  использование кэшированных результатов, установите параметр cache в false. Чтобы вызываемый запрос сообщил отказ, если данные не были изменены после последнего запроса установите параметр ifModified в true.

Параметр scriptCharset позволяет явно указать набор символов, которые используются в теге <script> (т.е. параметр запроса type равен JSONP). Это полезно, если сценарий и страница имеют различные наборы символов.

Первая буква в аббревиатуре AJAX расшифровывается как "асинхронные", что означает, что операция происходит параллельно и порядок выполнения не гарантируется. Параметр async для метода jQuery.ajax(), по умолчанию  true, что означает, что выполнение кода может продолжаться и после запроса. Установка параметра async в false (т.е. сделать запрос не аснихронным) не рекомендуется, так как это может привести к зависанию браузера.

Метод jQuery.ajax() возвращает XMLHttpRequest-объект, который создает. Обычно jQuery управляет созданием этого объекта внутри, но  можно указать пользовательские функции для создания используя параметр xhr. Возвращаемый объект вообще может быть отброшен, что не предусматривает наблудение и манипулирование запросом на низком уровне. В частности, вызывов метода .abort() в объекте остановит запрос до его завершения.

В настоящее время, в связи с ошибкой в Firefox, где .getAllResponseHeaders() возвращает пустую строку, хотя .getResponseHeader('Content-Type') возвращает не пустую строку, автоматическое декодирование JSON CORS ответов в Firefox с jQuery не поддерживается.

Обойти данную ситуацию можно путем переопределения jQuery.ajaxSettings.xhr, следующим образом:

(function () {
    var _super = jQuery.ajaxSettings.xhr,
    xhrCorsHeaders = [ "Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma" ];
    jQuery.ajaxSettings.xhr = function () {
        var xhr = _super(), getAllResponseHeaders = xhr.getAllResponseHeaders;
        xhr.getAllResponseHeaders = function () {
            var allHeaders = "";
            try {
                allHeaders = getAllResponseHeaders.apply( xhr );
                if (allHeaders) {
                    return allHeaders;
                }
            } catch ( e ) {
            }
            $.each( xhrCorsHeaders, function ( i, headerName ) {
                if ( xhr.getResponseHeader( headerName ) ) {
                    allHeaders += headerName + ": " + xhr.getResponseHeader( headerName ) + "\n";
                }
            });
            return allHeaders;
        };
        return xhr;
    };
})();

Расширени AJAX.

Реализация AJAX-запросов в jQuery включает в себя предварительные фильтры (filters), транспорты (transports) и преобразователи(convertors), которые дают AJAX дополниетельную гибкость.

Использование преобразователей (convertors).

Преобразователи в jQuery.ajax() поддерживают карты преобразования одних типов данных в другие. Но, если вы хотите сопоставить пользовательский тип данных с известным типом (например, JSON), вы должны добавить соответсвие между ответом Content-Type и фактическим типом данных, используя параметр contents:

$.ajaxSetup({
    contents: {
        mycustomtype: /mycustomtype/
    },
    converters: {
        "mycustomtype json": function ( result ) {
            // тут шота робими
            return newresult;
        }
    }
});

Этот дополнительный объект необходим, потому что Content-Type ответа и тип данных в реальности не имеют строго соответсвия один-в-один (отсюда и регулярные выражения).

Для преобразования из поддерживаемых типов (например, текст JSON) для пользовательских типов данных и обратно, используйте другой преобразователь:

$.ajaxSetup({
    contents: {
        mycustomtype: /mycustomtype/
    },
    converters: {
        "text mycustomtype": true,
        "mycustomtype json": function ( result ) {
            // зноуку шота робімі
            return newresult;
        }
    }
});

Пример выше указывает перейти от текста к типу mycustomtype, а затем от mycustomtype к JSON.

Дополнительные примечания

* Из-за ограничений безопастности браузеров, к большинству AJAX-запросов предъявляются та же политика; запрос не может успешно извлекать данные из другого домена, поддомена или по другому протоколу.

* На Script и JSONP-запросы не распространяется таже политика ограничения.

Примеры

Пример: Отправить данные на сервер и сообщить пользователю об окончании операции.

$.ajax({
    type: "POST",
    url: "some.php",
    data: { name: "John", location: "Boston" }
}).done(function( msg ) {
    alert( "Data Saved: " + msg );
});

Пример: Получить последнюю версию HTML-страницы.

$.ajax({
    url: "test.html",
    cache: false
}).done(function( html ) {
    $("#results").append(html);
});

Пример: Отправить XML-документ на сервер. При установке опции processData к ложным, автоматическое преобразование данных в строку не происходит.

var xmlDocument = [create xml document];
var xmlRequest = $.ajax({
    url: "page.php",
    processData: false,
    data: xmlDocument
});
xmlRequest.done(handleResponse);

Пример: Отправить id, и оповещаем пользователя по окончанию операции. Если запрос не удается, предупредить пользователя.

var menuId = $("ul.nav").first().attr("id");
var request = $.ajax({
    url: "script.php",
    type: "POST",
    data: {id : menuId},
    dataType: "html"
});
request.done(function(msg) {
    $("#log").html( msg );
});
request.fail(function(jqXHR, textStatus) {
    alert( "Request failed: " + textStatus );
});

Пример: Загружаем JavaScript-файл для выполнения.

$.ajax({
    type: "GET",
    url: "test.js",
    dataType: "script"
});