В последнее
время термин AJAX
получил широкое распространение. По сути, это
необычное название технологии, которая уже давно
существует. Однако за последний год
JavaScript в стиле
AJAX стал очень
популярным у многих разработчиков, и мы начинаем
видеть, как с его помощью стали создавать
различные интересные вещи.
Google Maps и
GMail являются
двумя наиболее широко известными приложениями
AJAX, но в
последнее время и другие компании по всему миру
начали использовать ее на своих сайтах.
AJAX означает "Asynchronous
JavaScript and XML", т.е. Асинхронный
JavaScript и
XML. В
действительности AJAX
состоит из JavaScript,
как и любое другое приложение, и соединения
XMLHTTP с
Web-сервером. Вот
и все! Общая идея заключается в том, что можно
извлекать с сервера данные просто по мере
необходимости, не обновляя всю страницу.
Прежде всего
AJAX почти всегда
опирается на серверный язык, такой, как
PHP или
ASP. Когда
пользователю необходимо получить новые данные,
JavaScript
запрашивает их, а сервер, вероятно, запросит
базу данных и затем вернет данные. Эти данные
можно вернуть в различной форме. Если они
структурированы, то это будут обычно данные
XML или
JSON. Если это
очень простые данные (такие, как получение
описания какого-то объекта), то можно часто
увидеть людей, которые просто записывают эти
данные непосредственно в ответ
AJAX.
Создание объекта
XMLHttp
При создании
запроса AJAX
прежде всего необходимо создать объект
XMLHTTP.
Netscape/Firefox,
Opera и другие
браузеры имеют этот объект встроенным.
Internet Explorer
использует ActiveXObject.
Поэтому мы создадим одну функцию для работы со
всеми этими браузерами:
if(typeof(XMLHttpRequest)!='undefined'){
var getXMLHttpObj = function(){ return new XMLHttpRequest(); }
} else {
var getXMLHttpObj = function(){
var activeXObjects = ['Msxml2.XMLHTTP.6.0',
'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0',
'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];
for(var i=0; i<activeXObjects.length; i++){
try{
return new
ActiveXObject(activeXObjects[i]);
}catch(err){}
}
}
}
Любой
браузер, который поддерживает объект
XMLHttpRequest,
будет использовать этот встроенный объект.
Internet Explorer 6
и более ранние версии будут использовать объект
ActiveX XMLHttp
компании Microsoft.
Существует много различных версий этого объекта,
поэтому мы попробуем их все, начиная с самого
нового.
Теперь,
когда мы имеем объект
XMLHttp, можно разместить запрос на
сервере:
var oXml =
getXMLHttpObj();
oXml.open('GET', 'getData.php', true);
oXml.onreadystatechange = processingFunction;
oXml.send();
function processingFunction(){
if(oXml.readyState!=4) return; // запрос не выполнен
// Результаты обрабатываются здесь. Подробнее дальше!
}
После
создания объекта XMLHttp
остается еще 4 дополнительных шага. Сначала
определяются параметры соединения с помощью
функции .open.
Функция .open
получает 3
аргумента: тип запроса,
URL и флаг, который сообщает объекту,
выполняться или нет асинхронно.
Первый
аргумент, тип запроса, почти всегда будет
GET или
POST. Если
извлекаются данные, то это будет обычно
GET, а если
отправляется форма с AJAX,
то это часто будет POST.
Флаг
асинхронности немного отличается почти от всего
остального в JavaScript.
Если этот флаг задан как
false, то код продолжает выполняться, как
и любой другой фрагмент кода, то есть он
ожидает, пока объект
XMLHttp завершит свою работу, прежде чем
двигаться дальше. Однако, если этот флаг задан
как true, то код
продолжает делать то, что следует в коде после
запроса. Когда состояние запроса изменяется,
вызывается функция, которая определена в
onreadystatechange.
В чем
различие? Если флаг асинхронности задан как
false, то браузер
будет полностью заблокирован, пока
обрабатывается запрос. Если запрошенные данные
нужны в обязательном порядке для продолжения
работы, то используйте это значение флага. Если
флаг задан как true,
то пользователь может продолжать использовать
Web-страницу, но ничего не известно о том, когда
вернется ответ на запрос. Может потребоваться
1/2 секунды или
1 минута. Все
зависит от нагрузки на сервер и соединения
конечного пользователя.
Когда все
готово, посылается запрос. Затем мы ожидаем.
Если запрос выполняется синхронно с функцией
обработки, определенной с помощью
onreadystatechange,
то она будет вызываться несколько раз. На самом
деле она вызывается всякий раз при изменении
состояния запроса. Существуют различные
состояния, такие, как отправка и загрузка
данных, но нас интересует только завершение
загрузки данных. Если
readyState == 4, то запрос выполнен,
поэтому в любом другом случае мы просто выходим
из функции.
Теперь мы
получили данные, но что это за данные?
XML, JSON или текст
Существует
три возможных варианта получения данных:
XML,
JSON или обычный
текст. При извлечении данных из базы данных,
скорее всего, будет использоваться
XML или
JSON. Ни один из
этих вариантов не имеет явных преимуществ.
XML - широко
распространенный стандарт, и поэтому существует
много приложений, которые работают с файлами
XML.
JSON является
более новой идеей, но быстро становится
популярным. Его обычно легче прочитать (для
человека), и он требует немного меньшую полосу
пропускания для пересылки.
Предположим,
что создается приложение для управления
контактами. Cервер может возвращать информацию о
людях. Одни и те же данные можно выразить в
форме XML или
JSON:
XML:
<xml>
<contacts>
<person firstname="Joe" lastname="Smith" phone="555-1212" />
<person firstname="Sam" lastname="Stevens" phone="123-4567"
/>
</contacts>
</xml>
JSON:
{contacts:[
{"firstname":"Joe", "lastname":"Smith", "phone":"555-1212"},
{"firstname":"Sam", "lastname":"Stevens", "phone":"123-4567"}
]}
Можно видеть,
что нотация XML
выглядит очень похоже на
HTML. По большей части это так и есть.
HTML и
XML оба являются
основанными на тегах языками и могут даже
анализироваться одинаковым образом (см. Лекция
6).
Нотация
JSON выглядит
очень похоже на простой
JavaScript. JSON
означает JavaScript
Object Notation и поэтому действительно
является обычным
JavaScript.
Данные в
любой нотации можно посылать с Web-сервера
просто как обычный текст. Никакие пробелы,
имеющиеся в этих примерах, не нужны, за
исключением одиночных пробелов между именами
атрибутов в тегах person
(в версии XML).
Формат
XML является
совокупностью тегов, очень похожих на
HTML. Можно иметь
любое количество тегов, вложенных друг в друга,
и каждый тег может иметь любое количество
атрибутов: например,
firstname,
lastname и phone
в примере выше. Однако имеется несколько вещей,
за которыми необходимо следить.
- Каждый тег должен
иметь закрывающий тег. Например,
<contacts>
закрывается с помощью расположенного ниже
</contacts>.
Теги person
являются замкнутыми.
/> в конце действует по сути как
дополнительный закрывающий тег. Можно было
так же легко написать один из них как
<person ...
></person>.
-
XML имеет
ограниченный набор символов, которые можно
использовать. В частности следующие символы
являются недопустимыми в узлах и атрибутах и
должны заменяться специальными комбинациями:
& |
--> |
& |
< |
--> |
< |
> |
--> |
> |
" |
--> |
" |
' |
--> |
' |
Например, узел <group
name="Bill & Paul" /> является
недопустимым и должен быть заменен на
<group name="Bill
& Paul" />.
- Приходящие с
сервера данные должны посылаться с
content-type,
заданным как text/xml.
Если извлекается файл с расширением
.xml, то это
должно происходить автоматически. Если
данные извлекаются из сценария, необходимо
задавать это вручную.
Для
PHP добавьте
следующее:
<?php
header('Content-type: text/xml'); ?>
Для
ASP добавьте:
<%
response.contentType = "text/xml" %>
- Для всех других
языков добавьте эквивалентный заголовок
content-type.
Если
этот заголовок не задан, свойство
responseXML
объекта XMLHttp
будет пустым (это свойство будет описано
далее).
JSON имеет
аналогичный набор правил, и всю документацию по
способам записи можно увидеть на json.org.
Однако упрощенно можно сказать, что:
- объекты начинаются
и заканчиваются с помощью символов
{ и
}
соответственно;
- массивы начинаются
и заканчиваются с помощью символов
[ и
]
соответственно;
- все строки
заключаются в двойные кавычки
";
- символы
" в строке
должны экранироваться:
\".
Проще
говоря, строка JSON
должна представлять допустимый объект
JavaScript.
Теперь
посмотрим на то, как можно выполнить
синтаксический разбор этих данных. В данный
момент мы создадим просто сценарий, который
сообщит, сколько имеется контактов, и выведет о
них информацию. Начнем с версии
XML, возвращаясь к
предыдущему незаконченному фрагменту кода:
function
processingFunction(){
if(oXml.readyState!=4) return; // запрос не выполнен
// Результаты обрабатываются здесь. Подробнее дальше!
}
Когда
скрипт попадает в тело функции, запрос
XMLHttp будет
выполнен. Объект XMLHttp
имеет два метода для возврата данных:
responseXML и
responseText. Так
как в данный момент мы работаем с файлом
XML, то будем
использовать responseXML:
function
processingFunction(){
if(oXml.readyState!=4) return;
var xmlDoc = oXml.responseXML;
var contacts = xmlDoc.selectNodes('/xml/contacts/person');
alert('There are '+contacts.length+' contacts!');
for(var i=0; i<contacts.length; i++){
alert('Contact #'+(i+1)+':\n\n'+
'First Name:
'+contacts[i].getAttribute('firstname')+'\n'+
'Last Name:
'+contacts[i].getAttribute('lastname')
+'\n'+
'Phone #:
'+contacts[i].getAttribute('phone')
+'\n');
}
}
Здесь
имеется 3 функции
вывода (alert).
Одна сообщает, что имеется два контакта, а еще
две выводят контактную информацию для каждого
человека.
Посмотрим
на тот же сценарий, использующий текст
JSON:
function
processingFunction(){
if(oXml.readyState!=4) return;
var json = eval('('+oXml.responseText+')');
alert('There are '+json.contacts.length+' contacts!');
for(var i=0; i<json.contacts.length; i++){
alert('Contact #'+(i+1)+':\n\n'+
'First Name: '+json.contacts[i].firstname+'\n'+
'Last Name: '+json.contacts[i].lastname
+'\n'+
'Phone #:
'+json.contacts[i].phone
+'\n');
}
}
Как можно
видеть, строки JSON
можно преобразовать в
JavaScript, используя просто встроенную в
JavaScript команду
eval(). Однако это
можно делать, только если вы полностью доверяете
источнику данных. Если это не так (если данные
поступают из не вполне известного источника
данных), то необходимо пропустить их в целях
безопасности через JSON
Parser (Анализатор
JSON).
Наконец
можно выбрать получение данных в любом другом
простом текстовом формате вместо
XML или
JSON. Однако в
этом случае необходимо решить, как выполнить
синтаксический анализ данных. Если имеется
только один фрагмент данных, такой, как
комментарий, то это может быть практичным
решением.
Что
использовать: XML
или JSON? Здесь
нет больших различий. XML
имеет более широко распространенный формат и
переносим почти на любую систему. Если
создаваемый проект будет работать с внешними
источниками, то, вероятно, лучше использовать
XML. Однако
JSON немного легче
для понимания и в общем он быстрее для
разработки кода, чем XML.
Если эта технология применяется для
персонального проекта или в начале нового
проекта, которому не нужно взаимодействовать с
другими приложениям, то
JSON определенно заслуживает
рассмотрения.
Пример со списком
контактов
В
действительности уже есть все, что нужно для
создания приложений AJAX,
но мы рассмотрим достаточно простой пример. Мы
собираемся написать небольшую таблицу данных (data
grid), которая извлекает данные из трех
различных файлов JSON.
Для простоты эти файлы уже были сгенерированы.
На практике эти файлы будут скорее всего
генерироваться оперативно с помощью серверного
сценария.
Файл 1
{contacts:[
{"firstname":"Steve" ,"lastname":"Smith",
"phone":"555-1212"},
{"firstname":"Joe" ,"lastname":"Stevens",
"phone":"555-0193"},
{"firstname":"Sam" ,"lastname":"Smith",
"phone":"555-5120"},
{"firstname":"Dave" ,"lastname":"Stevens",
"phone":"555-0521"},
{"firstname":"Suzy" ,"lastname":"Smith",
"phone":"555-9410"},
{"firstname":"Jessica" ,"lastname":"Stevens", "phone":"555-8521"},
{"firstname":"James" ,"lastname":"Smith",
"phone":"555-4781"},
{"firstname":"Jacob" ,"lastname":"Stevens",
"phone":"555-9281"},
{"firstname":"Alex" ,"lastname":"Smith",
"phone":"555-7261"},
{"firstname":"Tam" ,"lastname":"Stevens",
"phone":"555-1820"}
]}
Файл 2
{contacts:[
{"firstname":"Nancy" ,"lastname":"Smith",
"phone":"555-9583"},
{"firstname":"Elane" ,"lastname":"Stevens",
"phone":"555-7281"},
{"firstname":"Shawn" ,"lastname":"Smith",
"phone":"555-5782"},
{"firstname":"Jessie" ,"lastname":"Stevens", "phone":"555-7312"},
{"firstname":"Matt" ,"lastname":"Smith",
"phone":"555-4928"},
{"firstname":"Jason" ,"lastname":"Stevens",
"phone":"555-3917"},
{"firstname":"Daniel" ,"lastname":"Smith",
"phone":"555-8711"},
{"firstname":"Shannon" ,"lastname":"Stevens", "phone":"555-0912"},
{"firstname":"Diana" ,"lastname":"Smith",
"phone":"555-6172"},
{"firstname":"Mark" ,"lastname":"Stevens",
"phone":"555-8831"}
]}
Файл 3
{contacts:[
{"firstname":"Laura" ,"lastname":"Stevens",
"phone":"555-3915"},
{"firstname":"Jeff" ,"lastname":"Smith",
"phone":"555-8614"},
{"firstname":"Frank" ,"lastname":"Stevens",
"phone":"555-0213"},
{"firstname":"Elizabeth" ,"lastname":"Smith",
"phone":"555-7531"},
{"firstname":"Jim"
,"lastname":"Stevens", "phone":"555-3951"}
]}
Эти файлы
будут обеспечивать все данные для нашего списка
контактов на AJAX.
Построение списка контактов является в
действительности вполне простым: создается
таблица TABLE для
хранения всех контактов и функция для очищения и
повторного заполнения этой таблицы. Вот и все.
<table cellspacing="1"
cellpadding="3" bgcolor="#000000"
style="font-family:tahoma;font-size:10px;">
<tbody id="contactListTable">
<tr style="background-color:#CCF;">
<th>First Name</th>
<th>Last Name</th>
<th>Phone #</th>
</tr>
</tbody>
</table>
function loadContactListPage(n){
var oXML = getXMLHttpObj();
oXML.open('GET', '/img/10_json_file'+n+'.txt', true);
oXML.onreadystatechange = function(){ doneLoading(oXML); }
oXML.send('');
}
function doneLoading(oXML){
if(oXML.readyState!=4) return;
var json = eval('('+oXML.responseText+')');
var table = document.getElementById('contactListTable');
for(var i=table.childNodes.length-1; i>0; i--){
table.removeChild(table.childNodes[i]);
}
for(var i=0; i<json.contacts.length; i++){
var tr = document.createElement('TR');
var td1 = document.createElement('TD');
var td2 = document.createElement('TD');
var td3 = document.createElement('TD');
tr.style.backgroundColor = i%2?'#FFF':'#E6E6E6';
table.appendChild(tr);
tr.appendChild(td1);
tr.appendChild(td2);
tr.appendChild(td3);
td1.appendChild(document.createTextNode(json.contacts[i].firstname));
td2.appendChild(document.createTextNode(json.contacts[i].lastname));
td3.appendChild(document.createTextNode(json.contacts[i].phone));
}
}
Демонстрационный пример
First Name Last
Name Phone #
Steve Smith
555-1212
Joe Stevens 555-0193
Sam Smith
555-5120
Dave Stevens
555-0521
Suzy Smith
555-9410
Jessica Stevens
555-8521
James Smith
555-4781
Jacob Stevens
555-9281
Alex Smith
555-7261
Tam Stevens 555-1820
Page 1 | Page 2 | Page 3
Как можно
видеть из примера выше, это все достаточно
просто. Большая часть кода нужна в
действительности просто для создания новых строк
в таблице.
AJAX может быть
удивительно полезным инструментом. Его можно
использовать для проверки форм перед их
отправкой, для извлечения данных, как в этом
примере, или для чего-то еще, что можно будет
придумать. Однако в нормальной ситуации он не
должен быть основным элементом Web-сайта. Обычно
надо быть уверенным, что сайт будет доступен,
даже если JavaScript
будет отключен, но всегда существуют некоторые
исключения для этого правила.
Следующая
лекция будет посвящена обработке ошибок в
JavaScript.
|