Опрос с помощью AJAX

Многие сайты проводят на своих страницах всяческие опросы пользователей. Обычно вопрос и варианты ответа на него помещается на заглавную страницу сайта, там, где его может увидеть как можно большее количество людей. Если делать механизм опроса с помощью традиционных средств HTML, то участие в нем пользователя потребует перезагрузки всей страницы. В данной статье я расскажу как можно осуществить опрос пользователей, используя модную нынче технологию AJAX.

Содержание

Ты — мне, я — тебе

Сейчас подробно будет рассмотрен алгоритм проведения опроса и протокол взаимодействия клиента и сервера. Итак, после загрузки основной страницы, средствами JavaScript выполняется GET-запрос без параметров к серверу для получения исходных данных для проведения опроса. Сервер возвращает их в виде XML документа следующей структуры:

<poll>
 <question>Вам сколько лет?</question>
 <choices>
 <choice>
 <text>10</text>
 </choice>
 <choice>
 <text>20</text>
 </choice>
 <choice>
 <text>Не помню</text>
 </choice>
 <choice>
 <text>А вам зачем?</text>
 </choice>
 </choices>
</poll>

Легко заметить, что сервер передает на клиентскую часть приложения вопрос и варианты ответа на него.

На клиенте происходит обработка полученной информации и вывод ее на страницу. После того, как пользователь выберет один из вариантов ответа, выполняется еще один запрос GET к серверу. Но на этот раз, в качестве параметра передается индекс выбранного пользователем варианта ответа. Сервер сохраняет выбор пользователя и возвращает новый XML-документ, который содержит статистику ответов:

<poll>
 <question>Вам сколько лет?</question>
 <choices>
 <choice>
 <text>10</text>
 <percent>38</percent>
 </choice>
 <choice>
 <text>20</text>
 <percent>20</percent>
 </choice>
 <choice>
 <text>Не помню</text>
 <percent>15</percent>
 </choice>
 <choice>
 <text>А вам зачем?</text>
 <percent>27</percent>
 </choice>
 </choices>
 <totalVoted>21760</totalVoted>
</poll>

В этом документе появилась информация о том, сколько процентов опрошенных выбрало тот или иной вариант ответа, и сколько всего человек приняло участие в опросе. Клиентской части приложения остается только вывести полученные данные на страницу.

Реализация

Сразу приведу ссылку на конечный результат: опрос. Хочу подчеркнуть, что у меня не было задачи сделать универсальный скрипт для проведения опросов. Если вы захотите использовать приведенный ниже код, то вам придется его модифицировать для того, чтобы он соответствовал вашим потребностям. Данный пример имеет минимум функциональности, но этого достаточно для иллюстрации применения AJAX для опроса пользователей сайта.

Код серверной части рассматриваемого примера я приводить не буду. Да он и не важен. Скажу только, что на самом деле он не сохраняет результаты опроса, а генерирует их случайным образом. Уверен, что вам не составит труда реализовать серверный скрипт, возвращающий простые XML-документы, и сохраняющий результаты опроса в базе данных или в файле на сервере.

Теперь переходим к клиентской части. Вот исходный код страницы в текстовом формате: poll.txt. Если вас смущают незнакомые теги (которые значимой роли не играют), то можете просто просмотреть HTML код работающего примера.

Область страницы, на которой будет осуществляться вывод данных в ходе опроса, определяется тегом div, который имеет id="poll". Я не буду останавливаться на служебных функциях, которые возвращают экземпляр класса XMLHttpRequest и осуществляют с помощью него асинхронный запрос GET к серверу. Эти функции были описаны в предыдущей статье. Рассмотрим новый код:

//CallBack-функция.
 function processResponse() {
 if (httpRequester.readyState == READY_STATE_COMPLETE) {
 if (httpRequester.status==200) {
 processXmlResponse(httpRequester.responseXML)
 } else {
 var message = "Problem retrieving data. requestStatus="+httpRequester.status+". Message="+httpRequester.statusText;
 alert (message);
 }
 }
 }

 var isInit = true;

 //выполняет обработку xml-документа, полученного с сервера.
 function processXmlResponse(xmlResponse){
 if (isInit){
 initPoll(xmlResponse);
 isInit = false;
 } else {
 showResults(xmlResponse)
 }
 }

Если при получении данных с сервера не ошибок не возникло (т.е. httpRequester.status=200), то получить их можно с помощью свойства responseXML объекта класса XMLHttpRequest. При этом они уже трактуются не как текст, а как объект XML DOM. После того, как данные получены, они передаются процедуре processXmlResponse, которая вызывает процедуру подготовки опроса или вывода результата в зависимости от того, первый это был вызов или второй. Рассмотрим процедуру initPoll:

//создание на странице всего необходимого для проведения опроса
 function initPoll(xml){
 var divElement = document.getElementById("poll");
 divElement.appendChild(document.createTextNode(getQuestion(xml))); //добавление вопроса на страницу
 divElement.appendChild(document.createElement("br"));
 var choices = xml.documentElement.getElementsByTagName("choice"); // получение всех вариантов ответа из XML
 for (var i=0; i<choices.length;i++){
 var radioElement = createNamedElement("input", "rdChoice"); //создание radio button
 radioElement.setAttribute("type", "radio");
 divElement.appendChild(radioElement); //добавление radio button на страницу
 var choice = choices[i].firstChild.firstChild.data; //полуение варианта ответа из XML
 divElement.appendChild(document.createTextNode(choice)); //добавление варианта ответа
 divElement.appendChild(document.createElement("br"));
 }
 divElement.appendChild(document.createElement("br"));
 var buttonElement = document.createElement("input"); //создание кнопки
 buttonElement.setAttribute("type", "button");
 buttonElement.setAttribute("value", "Голосовать!");
 buttonElement.onclick = vote; //добавление обработчика onClick кнопки
 divElement.appendChild(buttonElement); //добавление кнопки на страницу
 }

 //функция возвращает вопрос извлеченный, из xml-документа с помощью DOM
 function getQuestion(xml){
 return xml.documentElement.getElementsByTagName("question")[0].firstChild.data;
 }

Здесь все достаточно прозрачно. Сначала на странице ищется элемент div, имеющий идентификатор "poll". Затем в него помещается вопрос, варианты ответа, и кнопка, при нажатии на которую будет выполнена процедура голосования. Новые элементы создаются и добавляются на страницу с помощью HTML DOM. Данные из XML-документа, полученного с сервера, тоже извлекаются средствами DOM. Единственный момент, который требует отдельного рассмотрения, это создание элементов input типа "radio" с помощью функции createNamedElement

Преодоление особенностей

Как известно, реализация JavaScript отличается в разных браузерах и не всегда соответствует стандартам W3C. Такое несоответствие принято называть "особенностями" браузера. Одну из таких особенностей и придется преодолеть в ходе реализации поставленной задачи.

Для выбора одного варианта ответа на вопрос я использовал элемент управления "radio button". Для того чтобы объединить несколько таких элементов в группу, им нужно назначить одинаковое значение атрибута name. Я выбрал значение "rdChoice". Итак, я создавал элемент input и после этого устанавливал у него name, равное "rdChoice". Проблема в том, что Internet Explorer не позволяет устанавливать значение атрибута name после того, как элемент уже был создан. Что же делать? Для того, чтобы обойти этот баг, программисты Microsoft сделали возможным создание элементов, уже имеющих некоторые аттрибуты. Как вы понимаете, таке создание элементов не описано в стандарте и поддерживается только в IE. Функция, которая создает элементы с заданным атрибутом name в любом браузере выглядит так:

//функция возвращает новый элемент с установленным аттрибутом name
 function createNamedElement(type, name) {
 var element = null;
 // Попытка создать элемент в стиле IE. Она будет неудачной в большинстве браузеров
 try {
 element = document.createElement('<'+type+' name="'+name+'">');
 } catch (e) {
 }
 if (!element || element.nodeName != type.toUpperCase()) {
 // Для не IE; использование стандартного метода создания элемента
 element = document.createElement(type);
 element.setAttribute("name", name);
 }
 return element;
 }

Возвращение к нашим баранам

Теперь самое время рассмотреть процедуру vote, которая выполняется, когда пользователь выбрал один из вариантов ответа и нажал на кнопку "Голосовать!".

function vote(){
 divElement = document.getElementById("poll");
 var index = 0;
 var selIndex = -1;
 //определение индекса выбранного ответа
 var childs = divElement.childNodes;
 for (var i=0; i<childs.length; i++){
 if (childs[i].nodeType==1){
 if (childs[i].getAttribute("name")=="rdChoice"){
 if (childs[i].checked){
 selIndex=index;
 }
 index++;
 }
 }
 }
 if (selIndex==-1) {
 alert ("Ни один из вариантов не выбран!");
 return;
 }
 makeAJAXCall(sUrl+"?choice="+selIndex);
 }

Здесь тоже все довольно просто. Процедура определяет индекс выбранного пользователем варианта ответа, и выполняет асинхронный запрос к серверу, передавая параметр "choice". В ответ сервер вернет XML-документ с результатами опроса. Этот документ будет обработан процедурой showResults, которая по своему содержанию очень похожа на процедуру initPoll:

//вывод на страницу результатов опроса
 function showResults(xml){
 var divElement = document.getElementById("poll");
 while (divElement.hasChildNodes()) divElement.removeChild(divElement.lastChild); //очищаем содержимое div'а
 divElement.appendChild(document.createTextNode(getQuestion(xml))); //добавление вопроса на страницу
 divElement.appendChild(document.createElement("br"));
 var choices = xml.documentElement.getElementsByTagName("choice"); // получение всех вариантов ответа из XML
 for (var i=0; i<choices.length;i++){
 var choice = choices[i].firstChild.firstChild.data; //получение варианта ответа из XML
 var percent = choices[i].childNodes[1].firstChild.data; //получение процентов проголосовавших из XML
 divElement.appendChild(document.createTextNode(percent+"% - "+choice)); //добавление результата
 divElement.appendChild(document.createElement("br"));
 }
 divElement.appendChild(document.createElement("br"));
 //получение количества проголосовавших
 var totalVoted = xml.documentElement.getElementsByTagName("totalVoted")[0].firstChild.data;
 divElement.appendChild(document.createTextNode("Всего проголосовало: "+totalVoted)); //добавление на страницу
 }

В этой статье я рассказал, как можно организовать опрос пользователей с использованием технологии AJAX. Надеюсь, этот пример поможет вам при создании опросов на вашем сайте.