AJAX: низкий уровень

22 Июл 2007

Если верить некоторым комментариям, то этот блог читают хардкорные программисты, которые изучили фреймворки и сочли, что лучшим решением в случае технологии Ajax является прямое программирование через XMLHttpRequest Есть в этом доля правды: фреймворки тоже писали простые смертные, а найти их ошибку может быть очень сложно. Давайте рассмотрим прямые методы асинхронного доступа в JavaScript пошагово и тщательно на основе спецификаций примерно в таком прядке:

  • Что такое AJAX вообще и в чём суть.
  • “Скажите “Пожалуйста!” или “Как послать HTTP-запрос”.
  • “Сделано!” или “Обрабатываем ответ сервера”.
  • “А теперь вместе!” или “Простой пример”.
  • “X-Files” или “Работа с XML-ответом”.

Что такое AJAX вообще и в чём суть?

Ajax — это “Asynchronous JavaScript and XML” (Асинхронный JavaScript и XML). В основе технологии лежит использование нестандартного объекта XMLHttpRequest(), необходимого для взаимодействия со скриптами на стороне сервера. Объект может как отправлять, так и получать информацию в различных форматах включая XML, HTML и даже текст. Самое привлекательное в Ajax — это его асинхронный принцип работы. С помощью этой технологии можно осуществлять взаимодействие с сервером без необходимости перезагрузки страницы, что позволяет обновлять содержимое страницы частично, в зависимости от действий пользователя. Мы рассмотрим две особенности, с помощью которых можно:

  • Отправлять запросы серверу без перезагрузки страницы.
  • Работать с XML документами.

Скажите “Пожалуйста!” или “Как послать HTTP-запрос”.

Для того, чтобы послать HTTP запрос из JavaScript, необходим экземпляр класса, который поддерживает такую функцию. Такой класс впервые был введен в Internet Explorer как объект ActiveX, называемый XMLHTTP. Позже в Mozilla, Safari и другие броузеры был введен класс XMLHttpRequest, который поддерживал методы и свойства изначального объекта ActiveX от Microsoft. В результате, чтобы создать кросс-браузерный объект, можно сделать следующее:

if (window.XMLHttpRequest){// Mozilla, Safari, ...
http_request = new XMLHttpRequest(; }
else if (window.ActiveXObject){// IE
http_request = new ActiveXObject("Microsoft.XMLHTTP"); }

(Ради наглядности этот код немного упрощен. Более правильный пример будет рассмотрен далее). Некоторые версии браузеров Mozilla не будут корректно работать, если ответ сервера не содержит заголовка XML mime-type. Чтобы решить эту проблему можно использовать переопределение заголовка полученного от сервера.

http_request = new XMLHttpRequest();
http_request.overrideMimeType('text/xml');

Далее необходимо указать объекту, какая функция будет обрабатывать ответ. Это делается путем присваивания свойству onreadystatechange имени JavaScript функции, которую вы собираетесь использовать:

http_request.onreadystatechange = nameOfTheFunction;

После названия функции нет скобок, ни параметров, потому что вы просто присваиваете ссылку на функцию, а не вызываете ее. К тому же, вместо указания имени функции можно использовать возможность JavaScript объявлять функции на лету («анонимные функции») и указывать действия, которые сразу же будут обрабатывать ответ:

http_request.onreadystatechange = function(){
// код функции
};

После этого необходимо сделать запрос. Нужно вызвать методы класса open() и send():

http_request.open('GET' , 'http://www.example.org/some.file' , true);
http_request.send(null);

  • Первый параметр вызова функции open() — метод запроса HTTP (GET, POST, HEAD или любой другой поддерживаемый). Используйте методы в соответствии с HTTP-стандартами. Информация о допустимых HTTP запросах доступна по адресу спецификации W3C.
  • Второй параметр — URL запрашиваемой страницы. Из соображений безопасности нельзя запрашивать страницы сторонних доменов. Типичной ошибкой при доступе к сайту site.ru является подача запросов на www.site.ru. Особым ограничением это не является – ведь сам скрипт (на PHP, например) вполне может обращаться куда угодно.
  • Третий параметр указывает, является ли запрос асинхронным. Если он TRUE, то выполнение JavaScript продолжится во время ожидания ответа сервера.

Параметром метода send() могут быть любые данные, которые вы хотите послать на сервер. Данные должны быть сформированы в строку запроса:

name=value&anothername=othervalue&so=on

Заметьте, что если вы хотите отправить данные методом POST, вы должны изменить MIME-тип запроса с помощью следующей строки:

http_request.setRequestHeader('Content-Type' , 'application/x-www-form-urlencoded');

Иначе сервер проигнорирует данные отправленные методом POST.

“Сделано!” или “Обрабатываем ответ сервера”.

Вернёмся к функции, имя которой мы указали в качестве обрабатывающей ответ.

http_request.onreadystatechange = nameOfTheFunction;

Эта функция должна проверять статус запроса. Если значение переменной статуса равно 4, то ответ от сервера получен и его можно обрабатывать.

if (http_request.readyState == 4){
// ответ получен
}else{
// еще не готово или что-то не так
}

Полный список значений кодов readyState такой:

  • 0 – uninitialized.
  • 1 – loading.
  • 2 – loaded.
  • 3 – interactive.
  • 4 – complete.

(Источник)


Следующее, что нужно проверить — это статус HTTP-ответа. Все возможные коды можно посмотреть на сайте W3C. Нам интересен только код ответа 200 OK.

if (http_request.status == 200){
// то что нужно
}else{
// проблемы, например, 404 (Не найдено) или 500 (Внутренняя ошибка сервера)
}

Теперь, после проверки состояния запроса и статуса HTTP-ответа, вы можете делать с полученными данными все что угодно. Есть два способа получить к ним доступ:

  • http_request.responseText – возвращает ответ сервера в виде строки текста.
  • http_request.responseXML – возвращает ответ сервера в виде объекта XMLDocument, который вы можете обходить используя функции JavaScript DOM.

“А теперь вместе!” или “Простой пример”.

Теперь соберем все вместе и сделаем простой пример HTTP-запроса. Наш JavaScript запросит HTML документ test.html, который содержит текст “I’m a test.” и выведет содержимое файла в диалоговом окне.

<script type="text/javascript" language="javascript">
function makeRequest(url>){
var httpRequest;
if (window.XMLHttpRequest){// Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
if (httpRequest.overrideMimeType){
httpRequest.overrideMimeType('text/xml');
}
}
else if (window.ActiveXObject){// IE
try{
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e){
try{
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e){}
}
}
if (!httpRequest){
alert('Не могу создать экземпляр XMLHTTP');
return false;
}
httpRequest.onreadystatechange = function(){alertContents(httpRequest; };
httpRequest.open('GET', url, true);
httpRequest.send('');
}
function alertContents(httpRequest){
if (httpRequest.readyState == 4){
if (httpRequest.status == 200){
alert(httpRequest.responseText);
}else{
alert('Проблема с запросом.');
}
}
}
</script>
<span style="cursor: pointer; text-decoration: underline" onclick="makeRequest('test.html')">
Make a request
</span>

В этом примере:

  • Пользователь нажимает на ссылку “Сделать запрос” в броузере;
  • Это вызывает функцию makeRequest() с параметром test.html — именем HTML файла;
  • Посылается запрос, после чего (onreadystatechange) выполнение передается alertContents();
  • alertContents() проверяет получен ли ответ и все ли с ним в порядке, после чего содержимое файла test.html выводится в диалоговом окне.

Вы можете посмотреть пример в действии здесь.

Тонкости:

1. Строка http_request.overrideMimeType(‘text/xml’); вызовет ошибки в консоли JavaScript в Firefox 1.5 или более позднем (https://bugzilla.mozilla.org/show_bug.cgi?id=311724), если страница вызванная с помощью XMLHttpRequest, не является правильным XML (например, если это обычный текст). В принципе, это корректное поведение.

2. Если вы посылаете запрос не на статический XML-файл, а на серверный скрипт, возвращающий XML, то нужно установить некоторые заголовки ответа, если вы планируете сделать вашу страницу работоспособной в Internet Explorer. Если вы не установите заголовок Content-Type: application/xml, IE будет сообщать об ошибке JavaScript, ‘Object Expected’, после строки, где вы пытаетесь получить доступ к XML элементу. Если вы не установите заголовок Cache-Control: no-cache, то броузер будет кэшировать ответ и никогда не будет повторно отправлять запрос ссделав отладку весьма увлекательной.

3. Если переменная http_request используется глобально, то конкурирующие функции, вызывающие makeRequest() могут конкурировать друг с другом. Объявление переменной http_request локально в функции и передача ее в alertContent() предотвращает это.

4. При привязывании функции обратного вызова к onreadystatechange нельзя указывать аргументов. По этой причине не работает следующий код:

http_request.onreadystatechange = alertContents(http_request; // Не работает.

Таким образом, для успешной регистрации функции, вы должны передать ей аргументы косвенно через анонимную функцию или используя http_request как глобальную переменную. Например:

http_request.onreadystatechange = function(){alertContents(http_request;)}; // 1 (одновременный запрос)
http_request.onreadystatechange = alertContents; // 2 (глобальная переменная)

Первый способ позволяет делать несколько запросов одновременно, а второй используется, когда переменная http_request является глобальной.

5. В случае ошибки взаимодействия (например, проблемы с сервером), при попытке доступа к переменной .status метода onreadystatechange будет сгенерировано исключение. Убедитесь, что if…then заключено в попытку – try…catch. (https://bugzilla.mozilla.org/show_bug.cgi?id=238559).

function alertContents(httpRequest){
try{
if (httpRequest.readyState == 4){
if (httpRequest.status == 200){
alert(httpRequest.responseText);
}else{
alert('Проблема с запросом.');
}
}
}
catch( e ){
alert('Исключение: '+ e.description);
}
}

“X-Files” или “Работа с XML-ответом”.

В предыдущем примере, после того как был получен ответ на HTTP-запрос мы использовали responseText запрашиваемого объекта, который содержал данные файла test.html. Теперь попробуем использовать свойство responseXML. Прежде всего, давайте создадим правильный XML документ, который мы будем запрашивать. Документ (test.xml) содержит следующее:

<?xml version="1.0"?><root> I'm a test.</root>

В скрипте необходимо всего лишь заменить строку запроса на:

onclick="makeRequest('test.xml')">

Далее в alertContents() нам нужно заменить строку alert(http_request.responseText); на:

var xmldoc = http_request.responseXML; var root_node = xmldoc.getElementsByTagName('root').item(0); alert(root_node.firstChild.data);

Этот код берет объект XMLDocument, возвращаемый responseXML и использует методы DOM для доступа к данным, содержащимся в документе XML. Посмотреть test.xml можно здесь, а обновленный скрипт здесь.

Чтобы узнать больше о методах DOM, изучите реализацию DOM в Mozilla.

Англоязычный источник: http://developer.mozilla.org / en/docs/AJAX:Getting_Started.






 

Павел     .

Написано 22 июля 2007 года в 05:26


По моему можно обойтись без велосипедов JavaScript библиотекой tw-sack.
На ней все те-же самое будет выглядеть так
function test(URL) {
var ajax = new sack();
ajax.AjaxFailedAlert = “Don’t work JavaScript”; // На случай не работающего JavaScript
ajax.requestFile = URL; // К какому файлу обращаемся
ajax.method = “POST”; // Или GET или HEAD
ajax.setVar(“test”, “1”); // Посылается $_POST[‘test’]
ajax.element = ‘data’; // ID элемента в который будет выводится результат
onLoading = loading(‘data’); // ID элемента, в который на время загрузки данных будет выводится результат работы функции loading()
ajax.runAJAX();
}

 

[email protected]     .

Написано 22 июля 2007 года в 08:16


может всё таки:
http_request.onreadystatechange = function(){alertContents(http_request; }; // 1 (одновременный запрос)
тут пропущена “)”
и должно быть так:
http_request.onreadystatechange = function(){alertContents(http_request); }; // 1 (одновременный запрос)

 

Жилинcкий Владимир     .

Написано 22 июля 2007 года в 09:10


Спасибо, поправил.

 

Жилинcкий Владимир     .

Написано 22 июля 2007 года в 09:12


Похоже, умники в комментариях неистребимы…

 

[email protected]     .

Написано 22 июля 2007 года в 11:13


Внимательно прочитайте первый абзац и будет вам счастье :)

 

Артём Курапов     .

Написано 22 июля 2007 года в 12:09


XML это громоздко – попробуйте JSON.

 

Жилинcкий Владимир     .

Написано 22 июля 2007 года в 12:16


В тему, спасибо !

© 2007-2010 Блог интернет-разработчика, автор — Zhilinsky.ru.
При использовании информации ссылка на источник обязательна.