Использование <IFRAME>
На наш взгляд, метод с использованием плавающих фреймов довольно неуклюж. Он использовался ранее, когда браузеры не поддерживали XMLHttpRequest.
В сердце технологии лежит функция создания скрытого фрейма:
function createIFrame() {
var ID = 'f' + Math.floor(Math.random() * 99999);
var div = document.createElement('div');
div.innerHTML = '<iframe style="display:none" src="about:blank"'
+' id="'+id+'" name="'+id+'" onload="sendComplete(''
+id+'')"></iframe>';
document.body.appendChild(div);
return document.getElementById(id);
}
Данная функция проверена нами в следующих браузерах: IE 7, IE 6, Firefox 2, Opera 9. С большой вероятностью она будет работать и в других менее распространенных браузерах, хотя и не во всех.
Для загрузки файла мы будем использовать Html-форму, результат вызова которой направим в созданный IFRAME.
function sendForm(form, URL, func, arg) {
if (!document.createElement) return; // not supported
if (typeof(form)=="string") form=document.getElementById(form);
var frame=createIFrame();
frame.onSendComplete = function() { func(arg, getIFrameXML(frame)); };
form.setAttribute('target', frame.id);
form.setAttribute('action', url);
form.submit();
}
function sendComplete(id) {
var iframe=document.getElementById(id);
if (iframe.onSendComplete && typeof(iframe.onSendComplete) == 'function')
iframe.onSendComplete();
}
function getIFrameXML(iframe) {
var doc=iframe.contentDocument;
if (!doc && iframe.contentWindow) doc=iframe.contentWindow.document;
if (!doc) doc=window.frames[iframe.id].document;
if (!doc) return null;
if (doc.location=="about:blank") return null;
if (doc.XMLDocument) doc=doc.XMLDocument;
return doc;
}
Функция sendComplete будет вызвана по окончании загрузки фрейма. Её задача - обработка результата операции, либо просто уведомление пользователя о завершении загрузки. Для этого будет вызвана пользовательская программа func с двумя аргументами: пользовательский arg, плюс DOM-результат, возвращенный сервером. Мы подразумеваем, что сервер возвращает XML. Для его извлечения из фрейма служит довольно громоздкая кроссбраузерная функция getIFrameXML.
Приведем пример:
< Script type="text/ JavaScript">
// ... сюда необходимо скопировать все вышеописанные функции ...var cnt=0;
function uploadComplete(element, doc) {
if (!doc) return;
if (typeof(element)=="string") element=document.getElementById(element);
element.innerHTML='Результат запроса #'+(++cnt)
+': '+doc.documentElement.firstChild.nodeValue;
}</script>
<form id="ajaxUploadForm" method="post" enctype="multipart/form-data"
onsubmit="sendForm(this,'uploadFile. PHP',uploadComplete,'resultDiv');return true;">
<label>Файл: <input type="file" name="uploadFile" /></label>
<input type="submit" value="Загрузить" />
</form>
<input type="button" value="Альтернативный вызов загрузки файла"
onclick="sendForm('ajaxUploadForm','uploadFile.php',uploadComplete,'resultDiv')" />
<div id="resultDiv"></div>
Текст файла uploadFile.php:
<?php header("Content-type: application/xml; charset=UTF-8"); echo '<?xml version="1.0" encoding="UTF-8" ?>' ?> <result>Получен файл [<?php echo($_FILES['uploadFile']['name']); ?>] размером <?php echo($_FILES['uploadFile']['size']); ?> байт</result> ?>
Использование <SCRIPT>
Следует признать, что данный AJAX-метод является самым лаконичным на стороне клиента. Кроме того, по сравнению с методами XMLHttpRequest/IFRAME он имеет одно важное преимущество: AJAX-запросы можно направлять не только к собственному, но и к чужим серверам. Это преимущество, правда, может обернуться существенным недостатком, если вы обеспокоены вопросами безопасности и не хотите, чтобы к вашему серверу обращались пользователи других серверов. Если же вы наоборот хотите разместить этим способом у себя на странице чужой AJAX-виджет, также будьте бдительны: выбранный вами AJAX-провайдер сможет в любой момент внедрить на вашу страницу любой (в том числе вредоносный) код. Например, подсматривающий пароли ваших пользователей: В общем, подходить к использованию данного метода следует с осторожностью, осознавая все плюсы и минусы.
В своей основе лежит следующий метод (проверено в IE>=6, FF>=2, Opera 9):
function callServer() {
var script = document.createElement("script");
script.src = ' HTTP:// Domain.ru/dynamicDataScript.php';
script.type = 'text/javascript';
document.body.appendChild(script);
}
Серверный скрипт dynamicData.php возвращает код JavaScript, который незамедлительно выполняется в браузере клиента. Данный код может, например, как минимум, загрузить в переменные (var) новые значения, как максимум - полностью перерисовать веб-страницу. Единственное, что он не может делать, это использовать функцию document.write() для вставки HTML-кода (данная функция доступна только при первичной загрузке страницы). Ограничение весьма условное, так как все то же самое можно реализовать через DOM и/или innerHTML.
Вся эта гибкость, конечно же, кажется нам чрезмерной - хочется как-то ситематизировать процесс, ввести ряд ограничений, хотя бы на уровне договоренностей. Во-первых, стоит договориться о том, что никаких операций со страницей серверный JavaScript-код не производит, а лишь передает данные (это будет полезным и с точки зрения его переиспользования на других страницах сайта). В связи с этим весьма полезным представляется набор соглашений JSON (JavaScript Object Notation) по представлению данных в формате, удобном для обработки интерпретатором JavaScript.
JSON (JavaScript Object Notation)
По сути, JSON - это JavaScript-код, описывающий некую структуру данных. В нем используются две основные синтаксические конструкции:
// объявление массива: var array = [ v1, v2, ... ]; // объявление ассоциативного массива: var hash = { "key1" : v1, "key2" : v2, ... };
С их помощью можно описать структуру данных произвольной сложности. Например:
{
"firstName": "Иван",
"lastName": "Федоров",
"address": {
"street": "Ордынка",
"city": "Москва",
"postalCode": 127327
},
"phoneNumbers": [
"495 765-1234",
"916 123-4567"
]
}
Если предположить, что вышеприведенный текст находится в переменной JSON_text, то работать в JavaScript с ним становится очень удобно:
var p = eval("(" + JSON_text + ")"); div.innerHTML = p.firstName+" "+p.lastName+ " живет в городе "+p.address.city;
Просто несравнимо по удобству с манипулированием моделью XML/DOM!
Таким образом, для передачи данных нам нужно лишь научить наш серверный скрипт форматировать данные в формате JSON.
Для построения законченного AJAX-приложения нам не хватает лишь одного - уведомления клиента о том, что процесс загрузки данных завершен. Сам по себе JSON такой возможности не предоставляет. Можно конечно грузить JSON-данные через XMLHttpRequest.responseText, используя все возможности последнего по контролю за завершением соединения, однако, это не есть тема нашей статьи.
JSONP: JSON With Padding
Для устранения указанного выше недостатка была предложена концепция JSONP (JSON With Padding). Она состоит в том, что в запрос к серверу добавляется параметр callback, в котором клиент указывает имя функции, которую необходимо вызвать для обработки данных. Для иллюстрации приведем простой серверный PHP-скрипт (sample_ajax_script_json.php), возвращающий данные в формате JSONP:
<?php echo($_REQUEST['callback'] .'({"result":" Данные из файла sample_ajax_script_json.php"})'); ?>
Если обратиться к скрипту с запросом: sample_ajax_script_json.php?callback=onComplete123, в ответ мы получим строку:
onComplete123({"result":"Данные из файла sample_ajax_script_json.php"})
Нам остается лишь обеспечить наличие функции onComplete123(), которая отобразит результат запроса.
Теперь постараемся сделать нашу AJAX-систему универсальной:
// в ассоциативном массиве callbacks мы будем динамически
// создавать и хранить до завершения запроса все
// callback-функции (ведь AJAX-запросы могут поступать
// одновременно от разных компонент на веб-странице)var callbacks=new Object();
function callJSONP(url, func, arg) {
var cbId;
// генерируем уникальный callback-id:
do cbId = ' C++' + Math.floor(Math.random() * 99999);
while (callbacks[cbId]);
// создаем callback-функцию для данного запроса:
callbacks[cbId] = function(obj)
{ func(arg, obj); delete callbacks[cbId]; };
// создаем элемент <script>:
var script = document.createElement('script');
// сообщаем серверу имя нашей функции:
script.src = url+(url.indexOf('?')>=0 ? '&' : '?')+
'callback=callbacks.'+cbId;
script.type = 'text/javascript';
// делаем запрос к серверу:
document.body.appendChild(script);
}
- url - ссылка на серверный AJAX-скрипт, возвращающий данные в формате JSONP; в ссылку необходимо включить все параметры запроса (метод POST в случае с элементом <script> неприменим);
- func - функция для обработки результата (например, отображения данных); функция будет вызвана с двумя параметрами: func(arg, obj):
- arg - пользовательский аргумент (например, идентификатор элемента страницы, который следует обновить), который будет передан без изменения в функцию func();
- obj - объект JSON, подлежащий обработке.
В заключение приведем пример использования:
<script type="text/javascript">
// ... сюда необходимо скопировать вышеописанную функцию callJSONP() ...
function showHTML(element, responseObject) {
if (typeof(element)=="string") element=document.getElementById(element);
element.innerHTML=responseObject.result;
}
</script>
<input type="button" value="Загрузить!"
onclick="callJSONP('sample_ajax_script_json.php',showHTML,'targetDiv')"/>
<div id="targetDiv">Здесь появится
результат вызова sample_ajax_script_json.php</div>
|