Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Разбираем Ajax-автозаполнение форм с помощью библиотеки script.aculo.us.


Информационный Канал Subscribe.Ru

Здравствуйте, дорогие читатели!

Поздравляю вас с наступающим новым годом и особенно с грядущими за ним каникулами! Пусть будет успех во всех ваших начинаниях. Пусть сбудутся ваши мечты!

Был обрадован неожиданно возросшим числом подписчиков. Так что можно сказать, что моя мечта создать полезный ресурс в Интернет, начинает сбываться благодаря вам.

В этом предпраздничном выпуске я продолжаю темы начатые предыдущем:

  1. script.aculo.us - установка, автозаполнение форм на основе Ajax, визуальные эффекты.
  2. Очередные две главы статьи "Ненавязчивый JavaScript"

script.aculo.us

script.aculo.us - это JavaScript библиотека, с помощью которой можно запросто реализовывать визуальные эффекты, перетаскивание(Drag-and-drop) элементов по странице, динамическую сортировку списков и дополнительную функциональность для форм(автозаполнение).

Я остановлюсь на автозаполнении форм. Во-первых, там как раз и применяется легендарный Ajax, во-вторых, уже существует замечательная статья о визуальных эффектах script.aculo.us на русском: http://shevtsov.fanstvo.com/2005/12/21/posobie-po-vizualnyim-effektam-scriptaculous, и наконец, я считаю автозаполнение самым полезным, из всего предлагаемого этой библиотекой.

С чего начнём?

Конечно же с установки. Сначала скачаем свежую версию с сайта разработчиков, на момент написания это 1.5.1. http://script.aculo.us/downloads

В скачанном архиве можно найти кроме самой библиотеки, ещё и целую кучу тестов, буквально на каждый эффект script.aculo.us. Остаётся только найти нужный и выцепить его из кода. :)

Далее, если вы хотите использовать скачанные скрипты - а ведь вы хотите! - создайте каталог для библиотеки, пусть это будет, допустим, /javascripts

Кладёте в него prototype.js, scriptaculous.js, builder.js, effects.js, dragdrop.js, slider.js и controls.js, и, наконец, вставляете в раздел HEAD такие две строки:

scriptaculous.js автоматически загружает остальные классы.

Классы автозаполнения формы

Их три: Autocompleter.Base, Ajax.Autocompleter и Autocompleter.Local.

Autocompleter.Base

Появился начиная с версии V1.1b1. Отвечает за прорисовку меню автозаполнения, отслеживание клавиатурных нажатий или событий мыши

Autocompleter.Local

Автозаполнение с помощью локального массива. Есть некоторые заданный массив, из которого и будут браться данные для заполнения.

Перечислю основные опции:

  • choices: сколько элементов показывать в списке автозаполнения.
  • partialSearch: если false идёт поиск совпадений только от начала строк в массиве автозаполнения, если true, то поиск идёт от начала любого слова в строке. Если надо искать совпадения в произвольном месте строки, надо установить свойство fullSearch в true.
  • fullSearch: Поиск в произвольном месте строки автозаполнения.
  • partialChars: сколько символов надо внести, прежде чем запустится поиск частичных совпадений. По умолчанию 2.

Ajax.Autocompleter

Формирует список, для автозаполнения обращаясь к серверу.

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

Класс ожидает от сервера ожидает ответ в виде

Помните об этом, программируя скрипт сервера.

А где же примеры?

Ну что это я - классы назвал, а примеров использования не дал! Первые примеры вы найдёте среди тестов, прилагающихся к архиву. Другой пример вы можете посмотреть на моём сайте. С его помощью я собираю небольшую статистику: из каких городов пришли мои досточтимые читатели.

Первое поле "Ваше имя" - автозаполнение основанное на Ajax.Autocompleter. Первоначально список имён мал, но каждое новое имя остаётся в базе данных и используется в дальнейшем для подсказки.

Второе поле "Город" - в нём автозаполнение идёт с помощью Autocompleter.Local. Задан в файле cit1251.js задан массив citiesArray а в нём все города России, Украины, Казахстана и Белоруссии, которые известны на сегодня Википедии.

Если файл cit1251.js вам пригодится, буду рад. Итак, проходим по ссылке, смотрим код

http://aboutweb2.spb.ru/test/test_aculous3.html

Желаю удачи в программировании Ajax приложений, и помните, что Ajax.Autocompleter передаёт запрос серверу в кодировке UTF-8


Далее у нас по плану третья и четвёртая главы "Ненавязчивого JavaScript". Автор Christian Heilmann.


Создание и уничтожение элементов страницы.

Основное достоинство DOM это возможность не только читать, но также изменять содержание и структуру находящегося в наличии документа. Для этого в нашем распоряжении есть несколько методов

Создание нового содержания

createElement(element)
Создание нового элемента
createTextNode(string))
Создание нового текстового узла со значением string.

Только что созданные элементы не добавляются в документ немедленно, они пребывают в чистилище до тех пор, пока мы не добавим их куда-нибудь в дерево узлов.

Javascript:
mynewparagraph=document.createElement('p');
mynewtext=document.createTextNode('this is a new paragraph');

Изменение существующего содержания

setAttribute(attribute,value)
Добавляет новый атрибут с соответствующим значением к объекту
appendChild(child)
Добавляет дочерний узел к объекту, этот узел должен быть объектом, строку использовать нельзя.
cloneNode()
Копирует узел со всеми дочерними узлами.
hasChildNodes()
Проверяет, имеет ли объект дочерние узлы и если да, возвращает true
insertBefore(newchild,oldchild)
Добавляет новый узел newchild перед узлом oldchild в дереве документа.
removeChild(oldchild)
Удаляет дочерний узел oldchild.
replaceChild(newchild,oldchild)
Перемещает узел newchild на место узла oldchild
removeAttribute(attribute)
Удаляет атрибут attribute объекта.

Пример с картинкой

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

HTML:

Итак, в случае, когда Javascript и DOM доступны, мы хотим:

  • Удалить надпись "(new window)" из ссылки
  • Добавить обработчик события для вызова функции popw()

Эта функция должна:

  • Показывать изображение, соответствующее ссылке, под ссылкой, если его ещё нет.
  • Удалять изображение, если оно уже есть (чтобы не добавлять снова и снова изображение нажимая по ссылке)
  • Сделать, чтобы изображение исчезало, когда пользователи кликают по нему.

Первую задачу решить нетрудно:

Javascript:
function imgpop()
{
 var il,imga,imgatxt;

// получаем все  LI из списка изображений, пробежаться по ним циклом
 il=document.getElementById('imglist').getElementsByTagName('li');
 for(i=0;i<il.length;i++)
 {

// вычленяем первую ссылку из LI
 imga=il[i].getElementsByTagName('a')[0];

// удаляем выражение (new window) из текста ссылки
// (который является содержанием(nodeValue) первого узла)  
 imgatxt=imga.firstChild;
 imgatxt.nodeValue=imgatxt.nodeValue.replace(/ \(new window\)/,'');

// добавляем обработчик события для вызова popw();
 imga.onclick=function(){return popw(this);}
 //imga.onkeypress=function(){return popw(this);}
 }
}

Далее, для функции popw() мы должны использовать некоторые из методов описанных выше:

Javascript:
function popw(o)
{
 var newimg;
// если уже есть изображение в parentNode (li) 
 if(o.parentNode.getElementsByTagName('img').length>0)
 {
// удаляем его
  o.parentNode.removeChild(o.parentNode.getElementsByTagName('img')[0]);
 } else {
// иначе создаём новое изображение и добавляем обработчик, который удаляет изображение
// когда вы кликаете по нему
  newimg=document.createElement('img');
  newimg.style.display='block';
  newimg.onclick=function(){this.parentNode.removeChild(this);};
  newimg.src=o.href;
  o.parentNode.appendChild(newimg)
 }
 return false;
}
Посмотреть работу скрипта показа изображений

Пример: ссылка на указатель даты

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

Сначала напишем необходимый HTML. Чтобы видеть какие элементы должны выдавать ссылку на указатель даты, мы добавим к ним классы date.

HTML:

Мы перебираем все input’ы документа, и проверяем, у какого из них содержится класс date в className (помните, элементы могут иметь более чем один класс в атрибуте class!)

Когда мы находим этот класс, то создаём новый объект ссылки и её текст. Этот текст мы присоединяем к объекту ссылки как дочерний объект и добавляем обработчик события, чтобы вызвать наш скрипт указателя даты.

Как только ссылка создана, мы добавляем её после поля ввода.

Javascript:
function addPickerLink()
{
 var inputs,pickLink,pickText;

// перебираем все inputы
 inputs=document.getElementsByTagName('input');
 for(i=0;i<inputs.length;i++)
 {
  
// если class содержит 'date'
  if(/date/.test(inputs[i].className))
  {

// создать новую ссылку с текстом
   pickLink=document.createElement('a');
   pickText=document.createTextNode('pick a date');

// добавляем текст дочерним узлом к ссылке
   pickLink.appendChild(pickText);

// устанавливаем атрибут href в # и вызывать picker на клик или переход с клавиатуры
   pickLink.setAttribute('href','#');
   pickLink.onclick=function(){picker(this);return false;};
   //pickLink.onkeypress=function(){picker(this);return false;};

// добавляем новую ссылку к родителю поля ввода (это P)
   inputs[i].parentNode.appendChild(pickLink)
  }
 }
}
Посмотреть демонстрационный пример

Сейчас после всех полей с датами есть ссылка на picker().

Всё что нам надо, это сказать функции picker к чему употребить возвращаемое значение.

Ссылка передаёт сама себя функции picker() в виде объекта, и мы должны получить доступ к предыдущему узлу того же уровня - INPUT.

Javascript:
function picker(o)
{
 alert('This is a simulation only.') // это демонстрационная функция
 o.previousSibling.value='26/04/1975';  
}

Готово, но не вполне. Мы добавили новую ссылку как последнего ребёнка к родительскому узлу input, а ведь очень даже может быть, что предыдущий узел того же уровня, что и наша ссылка на самом деле не INPUT, а пробел! Следовательно, нам надо перебирать все предыдущие узлы того же уровня пока мы не наткнёмся на узел типа элемент.

Javascript:
function picker(o)
{
 alert('This is a simulation only.') // это демонстрационная функция
 while(o.previousSibling.nodeType!=1)
 {
  o=o.previousSibling;
 }
 o.previousSibling.value='26/04/1975';  
}

Перебор всегда операция нудная (hacky) и зачастую довольно медленная. Чтобы избежать перебора мы должны изменить нашу функцию.

Изменение функции addPickerLink().

Использовать addPickerLink() легко, но это делает нас зависимым от разметки. Что случится если, к примеру, нам нужно будет добавить впоследствии за тэгом input - тэг SPAN со звёздочкой (*), чтобы указать, что это поле необходимое?

Обходной манёвр - это использовать insertBefore() для следующего узла того же уровня, что и наше поле ввода.

Javascript:
function addPickerLink()
{
 var inputs,pickLink,pickText;

// перебираем все inputы
 inputs=document.getElementsByTagName('input');
 for(i=0;i<inputs.length;i++)
 {
  
// если class содержит 'date'
   if(/date/.test(inputs[i].className))
   {

// создаём новый элемент ссылки и текст
    pickLink=document.createElement('a');
    pickText=document.createTextNode('pick a date');
 
// добавляем текст как дочерний узел к ссылке
    pickLink.appendChild(pickText);

// устанавливаем атрибут href равным # и вызываем picker при клике или переходе с клавиатуры
    pickLink.setAttribute('href','#'); 
    pickLink.onclick=function(){picker(this)};
    //pickLink.onkeypress=function(){picker(this)};

// добавляем новую ссылку сразу после inputа
    inputs[i].parentNode.appendChild(pickLink)
    inputs[i].parentNode.insertBefore(pickLink,inputs[i].nextSibling);
    }
  }
}
Посмотреть демонстрационный пример.

Памятки

Ну, вот и всё, с описанными инструментами мы способны получить доступ к любому элементу документа и затем изменить этот элемент, и мы можем улучшить работу пользователя, не привязываясь к Javascript.

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

Некоторые проблемы общего характера:

  • Убедитесь что элемент существует, прежде чем попытаться обратиться к нему. Некоторые браузеры счастливы проверить object.nextSibling.nodeName и возвращают false когда следующего узла нет или же этот узел текстовый, но другие генерируют ошибку, когда вы пытаетесь обратиться к атрибуту несуществующего элемента.
  • Убедитесь, что код не привязан слишком сильно к вёрстке, перевод строки могут быть прочитаны как новый узел, разметка может поменяться, а ведь вам неохота менять ваш скрипт с каждой сменой дизайна.
  • Чтение содержания элемента осуществляется чтением значения его childNodes, а не самого элемента! document.getElementsByTagName('h2')[0].nodeValue пуст, document.getElementsByTagName('h2')[0].firstChild.nodeValue - нет.
  • Проверяя nodeNames и атрибуты, убедитесь, что вы указываете правильный регистр , так как некоторые браузеры представляют элементы в вернем регистре, другие же в нижнем.
  • HTML сгенерированный DOM в большинстве случаев не well-formed, если вы хотите повторно использовать HTML из сгенерированной браузером странице, вы должны вычистить его.
  • Избегайте частого перебора элементов, если вы можете сами создать разметку, с которой вам предстоит работать, взамен перебора стремитесь использовать ID.
  • Знайте ваш синтаксис. Много раз(Many a time) getElementsById может послужить причиной частого переписывания скрипта. (rescripting)
  • Знайте ваши объекты Javascript и HTML атрибуты. Это означает не проверяйте атрибут, которого нет.
  • Не рассчитывайте, что на других сайтах придерживаются ваших приёмов вёрстки. Например, стоит проверять, что className содержит вашу строку, а не то, что он ей равен, так как некоторым программистам нравиться использовать несколько классов на элемент.

Как насчёт innerHTML?

Когда появился Internet Explorer 4, родился innerHTML, быстрый способ создания и изменения контента. Этот способ прочитать содержание элементов много проще чем рекомендованный W3C. Это главным образом случай, если элемент содержит childNodes, которые сами элементы, а мы хотим прочитать всё содержание. Чтобы сделать это только с помощью DOM, вам надо пройти сквозь мучительные испытания – проверять тип и читать значения каждого узла. Свойство InnerHTML много проще использовать, но у него есть несколько недостатков. Например, вы не можете получить обратно ссылки на элементы, созданные с его помощью, так как итоговое значение скорее строка, чем объекты. Кроме того, innerHTML поддерживается только HTML, а не XML, в отличие от DOM, которая обеспечивает переносимость на любую разметку. Просмотрите раздел DOM в Quirksmode.org или всестороннее исследование в Developer-x, чтобы сравнить поддержку DOM разными браузерами.

Вызываем дождь (скрипты)

Главная техника, которую мы используем, чтобы сохранить наш Javascript ненавязчивым, это хранение скрипта в отдельном файле, что предпочтительней, чем смешивать его с разметкой страницы. Чтобы исполнить функции в нашем .js файле, нам надо вызвать их, когда страница загружена. Это может быть достигнуто различными способами, каждый имеет свои достоинства и недостатки.

Старая школа

Когда-то давно, когда мы были молоды и невинны, мы добавляли атрибут onload к элементу body.

HTML: