Серия статей "Ajax на практике"
- Основы передачи данных
- Получение данных из формы
- Загрузка файлов
- Progress Bar - индикатор процесса загрузки
- Запрос на чистом JavaScript.
Во всех четырёх статьях серии "Ajax на практике", мы разбирались, как использовать эту технологию с помощью библиотеки jQuery и, в частности, её метод $.ajax(), но всегда ли нужно прибегать к помощи фреймворков? К примеру, если в своём проекте, вы больше нигде не используете библиотеку, то абсолютно не резонно нагружать страницы лишними килобайтами и особенно, если этих килобайт около девяноста. Вывод - пишем ajax-запрос на чистом JavaScript, тем более, что та же библиотека jQuery, ни что иное, как подготовленный набор методов (функций), написанный на нативном JS. Если взять для сравнения JS-код из статьи "Получение данных из формы", то переписывая код, нам нужно будет сделать следующее:
- Дождаться загрузки всего документа
- Получить в распоряжение элемент <form>
- Повесить на него обработчик события submit
- Подготовить данные формы на отправку
- Создать объект XMLHttpRequest
- С помощью XHR отправить данные
- Получить, обработать и вывести на экран ответ сервера
Поехали пошагово...
1) Вместо привычных $(function(){}) или $(document).ready(function(){}), устанавливаем свой обработчик события onload. Но прежде, хочу пояснить, почему мы не будем использовать конструкцию element.onevent = func;. Это способ хоть и кроссбраузерный, но главным его недостатком является то, что установив на один элемент два и более обработчика, последний "затрёт" предыдущие и они "не откатают свою обязательную программу". Поэтому, хоть кода на пару строк получится больше, но запишем так:
// функция кроссбраузерной установки обработчиков событий
function addEvent(elem, type, handler){
if(elem.addEventListener){
elem.addEventListener(type, handler, false);
} else {
elem.attachEvent('on'+type, handler);
}
return false;
}
// Вешаем обработчик события загрузки документа - DOM-Ready
addEvent(window, 'load', init);
После загрузки документа, будет вызвана функция init, где мы сможем получить доступ к нашей форме. Этот шаг можно и пропустить, если весь js-код, будет расположен в самом конце документа, перед закрывающим тего </body>, но случаи бывают разные...
2) В функции init, мы обращаемся к нашей форме и, в свою очередь, устанавливаем на неё обработчик события submit.
// Инициализация после загрузки документа
function init(){
output = d.getElementById('output'); // элемент, куда мы выведем полученный в ответе результат
myform = d.getElementById('my_form'); // форма
addEvent(myform, 'submit', sendAjaxRequest); // устанавливаем на форму обработчик события submit
return false;
}
По событию submit, будет вызвана функция sendAjaxRequest. И сразу же переходим к этой функции, где сформируем данные формы для запроса и отправим ajax-запросом. Сформировать данные можно или с помощью объекта FormData, с которым вы могли познакомится (если до этого не были знакомы) в статье "Получение данных из формы", или же обращаться к каждому элементу формы, получая значения, по какому-нибудь его селектору, или пробежаться по элементам формы в цикле. Но прежде всего, нам нужно отменить обычное поведение формы на событие submit:
// Функция Ajax-запроса
function sendAjaxRequest(e){
var evt = e || window.event;
if(evt.preventDefault){
evt.preventDefault(); // для нормальных браузров
} else {
evt.returnValue = false; // для IE старых версий
}
// .....
}
В этой же функции, собираем все данные формы:
// формируем данные формы
var elems = myform.elements, // все элементы формы
url = myform.action, // путь к обработчику (берём из атрибута action нашей формы)
params = [],
elName,
elType;
// проходимся в цикле по всем элементам формы
for(var i = 0; i < elems.length; i++){
elType = elems[i].type; // тип текущего элемента (атрибут type)
elName = elems[i].name; // имя текущего элемента (атрибут name)
if(elName){ // если атрибут name присутствует
// если это переключатель или чекбокс, но он не отмечен, то пропускаем
if((elType == 'checkbox' || elType == 'radio') && !elems[i].checked) continue;
// в остальных случаях - добавляем параметр "ключ(name)=значение(value)"
params.push(elems[i].name + '=' + elems[i].value);
}
}
url += '?' + params.join('&');
На выходе, в переменной url, мы получим строку вида "http://site.com/handler.php?name1=val1&name2=val2&nameN=valN". Если на предыдущих этапах сложностей не возникло, то можно переходить непосредственно к ajax-запросу и это всё продолжаем писать в функции sendAjaxRequest
. Но и тут нужно внести одну ремарку. Так как еще остаются пользователи, которые пользуются старыми версиями IE, то приходится учитывать этот момент при создании объекта XMLHttpRequest. Internet Explorer до десятой версии его не поддерживает, а имеет собственную реализацию - объект ActiveXObject. Поэтому напишем ещё одну функцию для создания своего объекта под определенные браузеры:
function getXhrObject(){
if(typeof XMLHttpRequest === 'undefined'){
XMLHttpRequest = function() {
try { return new window.ActiveXObject( "Microsoft.XMLHTTP" ); }
catch(e) {}
};
}
return new XMLHttpRequest();
}
Вы могли видеть и более "навороченные" реализации, но уверяю вас, что на сегодняшний день этого вполнене достаточно, т.к. говоря об устаревших версиях IE, я не беру в расчет седьмой и ниже. Почему восьмой "ослик" еще требует внимания? Для себя я это определил просто: сегодня еще достаточно популярна ОС Windows 7, а с ней по умолчанию идёт в комплекте IE8. Не все имеют возможность (напр., на работе) или не умеют, или не хотят обновлять до более гуманной версии и с этим приходится мириться... надеюсь. что не долго ;)
А мы продолжаем. Создаём новый объект XHR (XMLHttpRequest), открываем соединение с помощью его метода open(), используем собятие onreadystatechange для проверки состояния выполнения запроса (readyState) и статуса ответа сервера (status) и отправляем запрос на сервер, используя метод send(). Когда readyState примет значение равное 4 (запрос завершен) и status значение 200 (OK), мы можем выводить или использовать для других целей ответ сервера, который будет нам доступен в свойстве responseText или responseXML, если мы в ответе получаем полноценный XML-документ.
var xhr = getXhrObject();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200){
output.innerHTML = JSON.parse(xhr.responseText);
}
}
xhr.send(null);
Третий параметр в методе open(), указывает как будет производиться запрос: true - асинхронно, false - синхронно. В большинстве случаев, используют асинхронный запрос, то есть такой, когда страница не блокирует свою работу, в ожидании ответа сервера. Если вы заметили, то в коде выше, я использовал http-метод передачи данных "GET". Для метода "POST", надо еще добавлять заголовки, которые будут передаваться вместе с запросом на сервер, а так же делаем ещё некоторые изменения: вторым параметром (url) в методе open(), у нас будет только путь к файлу-обработчику, а сформированную строку с данными формы, мы передаём в метод send():
var xhr = getXhrObject();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200){
output.innerHTML = JSON.parse(xhr.responseText);
}
}
xhr.send(params.join('&'));
Вот, как бы и всё. Остаётся только собрать весь этот конструктор и привести полный листинг.
var d = document,
myform,
output;
// кроссбраузерная установка обработчика событий
function addEvent(elem, type, handler){
if(elem.addEventListener){
elem.addEventListener(type, handler, false);
} else {
elem.attachEvent('on'+type, handler);
}
return false;
}
// Универсальная функция для создания нового объекта XMLHttpRequest
function getXhrObject(){
if(typeof XMLHttpRequest === 'undefined'){
XMLHttpRequest = function() {
try {
return new window.ActiveXObject( "Microsoft.XMLHTTP" );
} catch(e) {}
};
}
return new XMLHttpRequest();
}
// Функция Ajax-запроса
function sendAjaxRequest(e){
var evt = e || window.event;
// Отменяем стандартное действие формы по событию submit
if(evt.preventDefault){
evt.preventDefault(); // для нормальных браузров
} else {
evt.returnValue = false; // для IE старых версий
}
// получаем новый XMLHttpRequest-объект
var xhr = getXhrObject();
if(xhr){
// формируем данные формы
var elems = myform.elements, // все элементы формы
url = myform.action, // путь к обработчику
params = [],
elName,
elType;
// проходимся в цикле по всем элементам формы
for(var i = 0; i < elems.length; i++){
elType = elems[i].type; // тип текущего элемента (атрибут type)
elName = elems[i].name; // имя текущего элемента (атрибут name)
if(elName){ // если атрибут name присутствует
// если это переключатель или чекбокс, но он не отмечен, то пропускаем
if((elType == 'checkbox' || elType == 'radio') && !elems[i].checked) continue;
// в остальных случаях - добавляем параметр "ключ(name)=значение(value)"
params.push(elems[i].name + '=' + elems[i].value);
}
}
// Для GET-запроса
//url += '?' + params.join('&');
xhr.open('POST', url, true); // открываем соединение
// заголовки - для POST-запроса
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) { // проверяем стадию обработки и статус ответа сервера
output.innerHTML = JSON.parse(xhr.responseText); // если все хорошо, то выводим полученный ответ
}
}
// стартуем ajax-запрос
xhr.send(params.join('&')); // для GET запроса - xhr.send(null);
}
return false;
}
// Инициализация после загрузки документа
function init(){
output = d.getElementById('output');
myform = d.getElementById('my_form');
addEvent(myform, 'submit', sendAjaxRequest);
return false;
}
// Обработчик события загрузки документа
addEvent(window, 'load', init);
Кончено, такой количество строк кода, по сравнению с записью в $.ajax(), может отпугнуть особо ленивых, но знать, что можно спокойно обойтись без всяких библиотек - будет вам плюсом. И применять его всё же нужно, если в вашем проекте, вы больше нигде не используете js-фреймворки, т.к. подключив библиотеку, вы утяжелите страницу на килобайт 90, а написав на нативном JS - всего на 4, а если еще и минифицировать код, то всего лишь на 2Кб! Разницу ощущаете? ;)
На этом я заканчиваю серию статей посвещенную Ajax. Будут вопросы - спрашивайте, по мере возможностей буду отвечать или дополнять новыми статьями. Успехов!