Асинхронная отправка файлов при помощи YUI

29 февраля 2008

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

В YUI, отправка асинхронных запросов на сервер осуществляет функция asyncRequest() модуля Connection Manager. Функции необходимо передать тип (GET или POST), URL запроса и объект callback, который будет обрабатывать полученные от сервера данные. Если нужно отправить на сервер данные формы, то необходимо указать объекту Connect вашу форму. Делается это при помощи функции setForm():

YAHOO.util.Connect.setForm(formObject, true);

Первый параметр - formObject - это указатель на форму. Однако, первым параметром может быть и ID формы. В этом случае Connection Manager сам получит указатель на форму при помощи getElementById. Второй параметр - это собственно то, что нам и нужно - это значение указывает на то, что нам необходимо отправлять файлы на сервер.

Далее нужно создать объект callback (если вам конечно нужно обрабатывать данные, которые возвращает сервер). Все члены объекта callback являются не обязательными, но желательно указать по крайней мере success и failure - функции, которые будут вызываться при успешной отправке (success) или ошибке (failure). Помимо этого, при отправке файлов, нужно также объявить функцию upload, которая будет вызываться при успешной отправке файла. Еще один очень важный параметр объекта callback это массив argument, в который нужно добавлять значения, необходимые для последующей обработки данных. Итак, пример:

var callback_obj = {
	argument : ['targetPre'],
	success: function(o) { alert('Файл успешно отправлен.'); },
	failure: function(o) { alert('Ошибка!!!'); },

	upload : function(response){
		var target = document.getElementById(response.argument[0]);
		target.innerHTML = response.responseText;
	}
}

В этом примере ответ от сервера (responseText) будет добавлен как содержимое в элемент с ID = targetPre (параметр, указанный в argument). После этого можно выполнять запрос:

var cObj = YAHOO.util.Connect.asyncRequest('POST', 'http://www.example.com', callback_obj);

Теперь пример простого web приложения, которое асинхронно отправляет файл на сервер и полученные от сервера данные помещает в элемент pre:

<html>
<head>
<script src="http://yui.yahooapis.com/2.5.0/build/yahoo/yahoo-min.js"></script>
<script src="http://yui.yahooapis.com/2.5.0/build/event/event-min.js"></script>
<script src="http://yui.yahooapis.com/2.5.0/build/connection/connection-min.js"></script>
<script type="text/javascript">

function upload(){
	var formObject = document.getElementById('aForm'); 

	// аргумент true указывает на то, что нам нужно отправлять файлы
	YAHOO.util.Connect.setForm(formObject, true);
	// отправка формы
	var cObj = YAHOO.util.Connect.asyncRequest('POST', formObject.getAttribute('action'), callback_obj);
}

var callback_obj = {
	argument : ['targetPre'],
	success: function(o) { alert('Файл успешно отправлен.'); },
	failure: function(o) { alert('Ошибка!!!'); },

	upload : function(response){
		var target = document.getElementById(response.argument[0]);
		target.innerHTML = response.responseText;
	}
}
</script>
</head>
<body>
<form id="aForm" name="my_form" action="upload.php" onsubmit="upload(); return false;">
<input type="file" name="my_file" />
<input type="submit" name="submit_btn" value="Send" />
</form>
<pre id="targetPre"></pre>
</body>
</html>

Здесь указатель на форму получается при помощи getElementById(), аттрибут action которого используется как URL запроса. Функция upload() вызывается из хендлера onsubmit, а для того, чтобы форма не выполняла действие по умолчанию (чтобы не перезагружалась страница) возвращаем из onsubmit значение false.

Подробнее о модуле Connection Manager можно узнать из официальной документации библиотеки.

Проблемы с созданием таблиц в Internet Explorer

26 февраля 2008

Сегодня я хочу обратить ваше внимание на один довольно интересный (если с ним не сталкиваться :) ) баг, с которым мне довелось повстречаться. Мне нужно было создать таблицу в которую я добавлял строки при помощи innerHTML. Код для добавления строк был приблизительно следующим.

function insertData( name, value ){
	var table = document.getElementById('dataTable');
	table.innerHTML += '<tr><td>'+name+'</td><td>'+value+'</td></tr>';
}

Все работало отлично везде, кроме IE. После долгих поисков я все-таки обнаружил причину. Дело в том, что innerHTML нельзя использовать для добавления содержимого в тэги table, thead, tfoot, tr, которые в Internet Explorer доступны только для чтения (больше об этом можно узнать здесь). Поэтому эти элементы нужно создавать при помощи стандартных методов DOM. Однако и здесь хитро подложены грабли. Оказывается, что при создании таблицы в IE, элемент tbody является обязательным, т.е. структура таблицы должна иметь следующий вид table > tbody > tr > td. Например, следующий код будет работать везде, кроме Internet Explorer:

function createTable(){
	var body = document.getElementsByTagName('body')[0];
	var table = document.createElement('table');
	table.setAttribute('border','2');
	var tr = document.createElement('tr');
	var td = document.createElement('td');
	td.setAttribute('id','myCell');
	td.appendChild(document.createTextNode('Это таблица'));

	tr.appendChild(td);
	table.appendChild(tr);
	body.appendChild(table);
}

В IE таблица будет создана и добавлена в документ (это видно в IE Developer Toolbar), но отображаться она не будет! Теперь, если добавить в таблицу элемент tbody, то проблемы исчезнут:

function createTable(){
	var body = document.getElementsByTagName('body')[0];
	var table = document.createElement('table');
	table.setAttribute('border','2');
	var tbody = document.createElement('tbody');
	var tr = document.createElement('tr');
	var td = document.createElement('td');
	td.setAttribute('id','myCell');
	td.appendChild(document.createTextNode('Это таблица'));

	tr.appendChild(td);
	tbody.appendChild(tr);
	table.appendChild(tbody);
	body.appendChild(table);
}

Не правда ли, жесткий баг? И уж очень он смахивает на peekaboo баг, при котором элементы в IE точно так же не отображаются на странице.

И напоследок, интересная ссылка на сайт с описанием четырех багов innerHTML - Web bug track.
До встречи!

Асинхронная отправка файлов на сервер

20 февраля 2008

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

var AIM = {
	frame : function(c) {

		var n = 'f' + Math.floor(Math.random() * 99999);
		var d = document.createElement('DIV');
		d.innerHTML = '<iframe style="display:none" src="about:blank" id="'+n+'" name="'+n+'" onload="AIM.loaded(\''+n+'\')"></iframe>';
		document.body.appendChild(d);

		var i = document.getElementById(n);
		if (c && typeof(c.onComplete) == 'function') {
			i.onComplete = c.onComplete;
		}

		return n;
	},

	form : function(f, name) {
		f.setAttribute('target', name);
	},

	submit : function(f, c) {
		AIM.form(f, AIM.frame(c));
		if (c && typeof(c.onStart) == 'function') {
			return c.onStart();
		} else {
			return true;
		}
	},

	loaded : function(id) {
		var i = document.getElementById(id);
		if (i.contentDocument) {
			var d = i.contentDocument;
		} else if (i.contentWindow) {
			var d = i.contentWindow.document;
		} else {
			var d = window.frames[id].document;
		}
		if (d.location.href == "about:blank") {
			return;
		}

		if (typeof(i.onComplete) == 'function') {
			i.onComplete(d.body.innerHTML);
		}
	}
}

Функция submit() является основной в объекте AIM. Она создаёт скрытый iframe при помощи функции frame(), добавляет атрибут target для формы и при необходимости выполняет callback onStart при начале отправки формы. В целом, принцип действия скрипта основывается на применении атрибута target, который указывает, где будет отображаться новый документ. Поскольку здесь target указывает на скрытый iframe, то страница не будет перезагружаться при оправке формы, а ответ от сервера будет помещен как его содержимое.

Пример применения объекта AIM приведен в следующем листинге:

<form action="index.php" method="post" onsubmit="return AIM.submit(this, {'onStart' : null, 'onComplete' : completeCallback})">
....
</form>

В этом примере будет происходить отправка данных формы (в том числе и файлов, если таковые указаны) на сервер. Возвращаемые сервером данные передаются в функцию completeCallback, которую целесообразно использовать для оповещения пользователя о результатах отправки формы и возможных ошибках.

Если вы предпочитаете использовать библиотеку YUI, то отправку файлов можно осуществлять при помощи connection manager. Подробнее об этом я расскажу в следующий раз.

Reflection.js - создание зеркального отражения

16 февраля 2008

Сегодня я представляю вашему вниманию скрипт Reflection.js, который позволяет добавлять эффект зеркального отражения к изображениям на web-странице. Эффект создаётся при помощи объекта Canvas на браузерах, которые его поддерживают, в Internet Explorer вместо Canvas использются фильтры Opacity (эффект прозрачности) и FlipV (зеркальное отображение). Конечный результат выглядит следующим образом:

Читать дальше »

Обнаружение утечек памяти в Internet Explorer

12 февраля 2008

Утечки памяти в браузерах - распространенное явление, и особенно подвержен этому напастью Internet Explorer. Для обнаружения утечек памяти в IE существует замечательная утилита sIEve, которая способна загружать страницу, производить мониторинг за выделение памяти, отображать свойства тэгов и др. Главное окно приложения показано на рисунке:

Главное окно sIEve
Главное окно sIEve

Для начала работы с приложением нужно ввести адрес тестируемой страницы в строку Address и нажать кнопку Go. После загрузки страницы, вы увидите в панели Memory usage изменения в выделении памяти. Если выделение памяти изменяется в большую сторону, то соответствующая строка выделяется красным цветом, в меньшую - зеленым. Самый простой тест - это последовательная перезагрузка страницы с контролем за выделением памяти. Чтобы начать тест, нужно нажать кнопку Auto-refresh - автообновление. sIEve начнет автоматически перезагружать страницу через короткие промежутки времени, при этом в панели Memory usage будет отображаться выделенная процессу память. Если выделение памяти постоянно расти, значит мы имеем дело с утечкой памяти. При нормальной же работе скрипта будет наблюдаться только небольшие флуктуации вокруг определенного уровня.

Читать дальше »

Создание объекта XMLHTTPRequest

8 февраля 2008

Очень часто для создания объекта XMLHTTPRequest используется схема с использованием структуры try … catch из-за того, что в разных браузерах этот объект создаётся по разному. В Internet Explorer объект XMLHTTPRequest создаётся при помощи ActiveX, в то время как другие браузеры используют собственный объект XMLHTTPRequest. Схема, о которой я говорю представена ниже:

function getXMLHTTPRequest() {
	try {
		req = new XMLHttpRequest();
	} catch(err1) {
		try {
			req = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (err2) {
			try {
				req = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (err3) {
				req = false;
			}
		}
	}
	return req;
}

Читать дальше »

Создание новых элементов DOM

4 февраля 2008

Мы уже давно привыкли пользоваться функцией document.createElement() для создания элементов, которые необходимо добавить в DOM. С учетом добавления аттрибутов и переменных, код выглядит приблизительно следующим образом:

var elem = document.createElement("input");
elem.setAttribute("id","uname");
elem.className("my_class");
elem.setAttribute("type", "text");
elem.setAttribute("name", "username");
elem.setAttribute("value", "");
elem.setAttribute("size", "20");
elem.onkeypress = validate;
elem._my_var_ = 12345;

Читать дальше »

Управление иконками favicon из JavaScript

2 февраля 2008

Иногда появляется необходимость динамически изменять иконку сайта - favicon, например, в чатах, при получении нового сообщения, при завершении продолжительной операции и др.

Читать дальше »