Информация к новости
  • Просмотров: 723
  • Автор: sulicompany
  • Дата: 16-12-2012, 02:56
 (голосов: 0)
16-12-2012, 02:56

Отправка форм с помощью Ajax’a

Категория: Программирование » JavaScript


Зачем это нужно?

Каждый раз при отправке формы на сервер, страничка полностью перезагружается, на что уходит трафик и время. Иногда эти потери совсем не оправданы. Представьте, что на страничке с кучей картинок и флэша есть мини-опрос «каким браузером вы пользуетесь?». После того, как пользователь проголосовал, страничка со всем тяжеловесным контентом полностью обновляется. Избежать подобной ситуации можно, если для отправки форм использовать ajax. Эта технология позволяет javascriptотправлять абсолютно любые http-запросы, в том числе и точно такие же, какие летят на сервер при отправке формы. Прелесть состоит в том, что при выполнении подобного запроса страничка не перезагружается. 

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

Для выполнения запросов на сервер при помощи javascript нам понадобится объект XMLHttpRequest, который, к сожалению, не стандартизирован и создаётся в разных браузерах по-разному. Для кроссбраузерного создания этого объекта напишем функцию:

function xmlHttp() {
  var tran;
  //Для оперы, firefox'a, хрома
  if (window.XMLHttpRequest) {
    tran = new XMLHttpRequest();
  //Для ie
  } else if (window.ActiveXObject) {
    try {
      //Для новых версий
      tran = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      //Для cтарых
      tran = new ActiveXObject("Microsoft.XMLHTTP");
    }
  }
  return tran;
}

Отправка запроса

Для отправки GET-запроса будем использовать функцию sendGetRequest:

function sendGetRequest(tran, url, params, callback) {
  if(tran.readyState == 4 || tran.readyState == 0) {    
    tran.open('GET', url + '?' + urlEncodeData(params), true);
    tran.send('');
    if(callback) {
      tran.onreadystatechange = function () {
        if(tran.readyState == 4 && tran.status >= 200 && tran.status < 300)
          callback(tran.responseText);
      }
    }
  }
}

Эта функция, как вы видите, принимает три параметра urlparams и callback. Параметр url — это адрес скрипта на сервере, который будет обрабатывать запрос, params — это ассоциативный массив, содержащий параметры запроса. Он может выглядеть, например, вот так:

{login: 'true-coder', mail: '[email protected]'}

Последний необязательный параметр callback — это функция, которая будет вызвана, когда сервер ответит на запрос. В качестве параметра она принимает текст ответа. В теле функции sendGetRequestмы первым делом проверяем не занят ли объект tran передачей другого запроса. Свойство readyStateсодержит число, показывающее на какой стадии находится выполнение запроса. Когда оно равно 0, то запрос ещё не инициализирован, а когда 4, то он уже завершён. В строке

tran.open('GET', url +?+ urlEncodeData(params), true);

мы обращаемся к скрипту с адресом url и определяем тип запроса. Функция urlEncodeDataпреобразовывает параметры запроса из ассоциативного массива в строку. Её мы рассмотрим позже. Последний параметр функции open, в данном случае равный true, определяет будет выполнен асинхронный или синхронный запрос. В нашем случае запрос асинхронный. При синхронном запросе выполнение сценария приостанавливается до того момента, пока не будет получен ответ. При асинхронном запросе сценарий продолжает исполняться, в то время как выполняется запрос. В строчке

tran.send('');

отправляется сам запрос. Далее проверяется был ли передан при вызове функции параметр callback. Если, был, то присваиваем свойству onreadystatechange функцию, которая будет вызываться каждый раз, когда будет меняться свойство readyState. В теле этой функции будем вызывать функцию callback, но только если запрос завершён (readyState == 4) и при передаче не произошло никаких ошибок (tran.status >= 200 && tran.status < 300). Свойство status содержит статус запроса. Иногда это код ошибки, например, 404. В качестве параметра функции callback передаётся текст ответа сервера.

Функция для отправки POST-запроса аналогична:

function sendPostRequest(tran, url, params, callback) {
  if(tran.readyState == 4 || tran.readyState == 0) {
    tran.open('POST', url, true);
    tran.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    tran.send(urlEncodeData(params));
    if(callback) {
      tran.onreadystatechange = function () {
        if(tran.readyState == 4 && tran.status >= 200 && tran.status < 300)
          callback(tran.responseText);
      }
    }
  }        
}

Только здесь при отправке запроса параметры передаются не вместе с url, а в строчке

tran.send(urlEncodeData(params));

и заголовок запроса Content-Type (тип содержимого запроса), устанавливается равным application/x-www-form-urlencoded.

Предотвращение некорректных запросов и решение проблем с кодировкой

Код функции urlEncodeData, которая позволяет избежать некорректных запросов и помогает устранить проблемы с кодировкой, выглядит вот так:

function urlEncodeData(data) {  
  var arr = [];
  for(var p in data)
    arr.push(encodeURIComponent(p) + '=' + encodeURIComponent(data[p]));
  return arr.join('&');  
}

Эта функция лепит строку вида ‘par1=value1&par2=value2&..&parn=valuen’ из ассоциативного массива {par1: value1, par2: value2, …, parn: valuen}. При этом параметры и их значения обрабатываются функцией encodeURIComponent. Эта функция преобразовывает компоненту запроса к виду, безопасному для передачи на сервер. Она кодируют все символы escape-последовательностями в кодировке UTF-8, кроме букв латинского алфавита, десятичных цифр и !*()’. Например мы хотим передать на сервер следующее данные:

{par1: 'value1&value2', par2: 'value3'}

Тогда тело запроса будет выглядеть вот так: ‘par1=value1&value2&par2=value3’ и на сервере будет принято:

Array ( [par1] => value1, [value2] => '', [par2] => value3 );

Так же эта функция encodeURIComponent позволяет избежать проблем с кодировкой. Дело в том, ajax всегда передаёт текст в UTF-8 и, если этот текст не перекодировать в эту кодировку, то на сервере получим кучу «крокозябр».

Генерация тела запроса

Теперь мы переходим к самому главному — к написанию функции, которую назовём postForm. Она будет формировать и отправлять запрос, основываясь на данных введённых в форму.

function postForm(tran, form, callback) {
  if(form.method == 'POST') {
    sendPostRequest(tran, form.action, getRequestBody(form), callback);
  } else {
    sendGetRequest(tran, form.action, getRequestBody(form), callback);
	}
  return false;
}

В качестве параметров функция принимает объект для отправки запросов, форму которую нужно отправить и функцию, которая будет вызвана как только сервер ответит на запрос.

Функция getRequsetBody принимает в качестве параметра форму. Из данных которые введены в поля формы создаётся ассоциативный массив из пар имяПоля: значенияПоля

function getRequestBody(form) {
  var par = {}, el;
  for(var i = 0; i < form.elements.length; ++i) {
    el = form.elements[i];      
    par[el.name] = el.value;
  }
  return par;
}

Все вышерассмотренные функции сохраним в файл ajax.js.

Пример использования

Теперь проверим, как это всё работает. На запросы будет отвечать скрипт, записанный в файлеaction.php. Выглядит он вот так:

<?php
header("Content-Type: text/html; charset=utf-8");
  if(count($_POST) > 0)
  {
    foreach($_POST as &$v)
      $v = iconv('UTF-8', 'WINDOWS-1251', $v);    
    print_r($_POST);
  }
 
?>

В этом скрипте мы, если в POST-заросе хоть что-то передано, преобразуем значения параметров из кодировки UTF-8, в которой они прилетели в WINDOWS-1251 и отправляем назад на клиент их в форматированном виде. Никакой обёртки данных в html делать не будем, поскольку наша цель всего лишь продемонстрировать работу скрипта.

Саму форму и ещё кое-что разместим в файле index.php. Вот его содержимое:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="ru-RU">
<head>
<title>ajax post form</title>
<script type="text/javascript" src="ajax.js"></script>
<script type="text/javascript">
  var tran = xmlHttp();
  function callback(data) {
    document.getElementById('answer').innerHTML = data;    
  }
</script>
</head>
<body>  
  <form action="action.php" method="POST" onsubmit="return postForm(tran, this, callback)">
    <span>Ник: </span><input type="text" name="nick" /><br />
    <span>E-mail: </span><input type="text" name="mail" /><br />
    <span>Cообщение: </span><br /><textarea name="text"></textarea><br />
    <input type="submit" value="Отправить" name="submit" />
  </form>
  <div id="answer"></div>
</body>
</html>

В див с id=»answer» будет присылатся ответ сервера. В нашем случае это просто массив $_POST в форматированном виде. Менять содержимое этого дива будет функция

function callback(data) {
  document.getElementById('answer').innerHTML = data;    
}

В обработчике события onsubmit формы вызывается функция postForm. Её код мы рассмотрели ранее. Обратим внимание на то, что эта функция всегда возвращает false, а значит и сам обработчикonsubmit, в нашем случае, возвращает false. Это означает, что форма не отправится стандартным «не аяксовким» способом.

 |