Формы
Изменение значений и параметров элементов
Две предыдущие главы почти не содержали примеров. Поэтому вы, скорее всего, уже не можете дождаться новых приемов и скриптов, которые можно опробовать в деле. Могу вас обрадовать – эта, а также две следующие главы будут сосредоточены в основном на коде. Эти главы, если можно так выразиться, будут практическим подтверждением той информации, которую вы уже получили.
Начнем с тех элементов страницы, которые являются основой всех взаимодействий пользователя с веб-сайтом, а именно с форм.
В первую очередь, построим HTML-код, на основе которого будут выполняться все скрипты-примеры. Условимся, что скрипт для работы будет находиться во внешнем файле – впрочем, взглянув на структуру HTML-документа, вы и сами это увидите.
Наш код будет включать все общеупотребимые элементы форм – кроме кнопок-картинок.
Я предлагаю использовать следующий код (он довольно большой, к сожалению):
Но, к счастью, все примеры будут работать только с этим кодом – так что приведенный выше HTML вам вообще не нужно будет менять.
Здесь нужно сделать несколько комментариев по поводу этого кода.
Строчка
<input type="button" value="Process Code" onClick="process();" />
может привести в некоторое замешательство атрибутом onClick. Не вдаваясь в подробности (для этих подробностей будет отведена специальная глава), можно сказать, что этот атрибут заставит выполняться функцию process() при нажатии на кнопку.
Все результаты работы мы будем создавать в div-е с ID равным result. То есть – будем их динамически формировать при помощи уже описанных ранее методов.
Конечно, вы можете спросить, зачем вызывать функцию process(), куда мы поместим все операции по работе с формой, по нажатию кнопки, а не сразу. Но для этого есть веская причина – если вызвать функцию сразу в загружаемом скрипте, она может выполниться еще до того, как сформируется страница, и, таким образом, вызовет ошибку (по причине отсутствия формы, с которой функция работает).
Доступ к формам и элементам
Первым боевым заданием, на основе которого можно будет рассмотреть способы доступа к формам и элементам формы, будет построение списка всех этих форм и элементов. После выполнения скрипта хотелось бы видеть список (ненумерованный) всех форм страницы со вложенными элементами. Для каждого элемента должен указываться тип, имя и значение (если это группа элементов – то показываться выбранный элемент).
На первый взгляд, работа, как говорится, неподъемная, но могу вас заверить – в результате работы код будет не очень большим. К сожалению, некоторые из операций вы еще не встречали – в основном, это будут операции со массивами, но они простые и понятные (и к тому же – я буду их пояснять в процессе создания кода).
Для начала, стоит описать общее решение задачи. Звучать оно будет примерно так:
«Получить список всех форм и элементов с атрибутами; сформировать HTML-список из внутреннего представления».
При создании кода мы будем использовать «нисходящее программирование», то есть – сначала реализовывать общие части решения, и детализировать и расширять их в процессе разработки.
Как вы видите, описание задачи с легкостью преобразуется в код, который эту задачу решает. Здесь, по принципам нисходящего программирования, решение будет использовать еще ненаписанные функции – для простоты, считаем что они уже есть.
Вот код:
function process() { // Главная функция. var structure = get_forms_elements(); create_list(structure); }
Этот код работать не будет – для его запуска необходимо написать функции get_forms_elements() и create_list().
Но теперь задача разбита на две задачи поменьше – и каждую из них решить проще.
Строчка, содержащая две косые черты «//» -- это так называемый комментарий. Весь текст, находящийся после символов комментарий до конца строки не воспринимается интерпретатором JavaScript, и может содержать что угодно. Чаще всего такие комментарии используются для пояснения кода, описания входящих параметров и так далее.
Есть и еще один тип комментариев – текст, находящийся между «/*» и «*/».
Этот комментарий может занимать несколько строк, главное – чтобы внутри комментария не было символов «*/» (потому что эти символы показывают конец комментария и вызовут ошибку). И не забывайте, что весь текст в коде JavaScript желательно писать в кодировке UTF-8
Итак, приступаем к решению новых задач. Первая из них – это получение списка элементов формы.
Здесь мы поступим таким же образом – разобъем задачу на задачи поменьше.
Например, так:
function get_forms_elements() { var forms = document.forms; var elements = []; for (var i = 0; i < forms.length; i++ ) { elements[i] = get_elements_from_form(forms[i]); } return elements; }
Здесь вводится новая «несуществующая» функция – get_elements_from_form(), которая получает все элементы из переданной ей формы.
Весь остальной код, кроме этой функции, вполне работоспособен и уже решает некоторую задачу, а именно – обход форм.
Рассмотрим код построчно:
var forms = document.forms;
Как вы можете понять, эта строка присваивает переменной forms содержимое переменной documen.forms. Ее практический смысл – к списку форм из объекта document можно будет обращаться по короткому имени (так как обе переменные содержат ссылку на один и тот же массив). Такое сокращение длины имени – достаточно частое действие, особенно если исходная переменная содержит четыре-пять ссылок на внутреннюю структуру.
В дальнейшем, комментироваться будут только незнакомые вам конструкции, так что не расслабляйтесь.
Строка объявления цикла
for (var i = 0; i < forms.length; i++ ) {
содержит еще не описанное мной выражение – forms.length
Если я еще не упоминал об этом (или если я упоминал, но давно и вы забыли), расскажу о доступе к элементам массивов и объектов.
Для того чтобы получить доступ к элементу массива, используется оператор «выбор по индексу». Это обычные квадратные скобки, такие же как и в определении массива.
Например, такая запись
var a = b[2];
занесет в переменную a содержимое второго элемента массива.
Запись же
a = b[0];
занесет в a нулевой элемент этого же массива.
Да, в программировании на языке JavaScript, как и во многих других, элементы массива нумеруются с нулевого.
Теперь об объектах. С ними все интереснее – во-первых, для доступа к свойствам объекта можно использовать точку, указывая после нее название свойства.
Например, вот так:
var forms = document.forms
Думаю, такая запись вам уже встречалась неоднократно. Теперь вы точно знаете, что именно она означает.
Но, кроме этой записи, можно также использовать обращение, аналогичное обращению к массиву. Например, предыдущую операцию можно переписать так:
var forms = document['forms']
И она будет делать то же самое.
Вторая запись более универсальна, потому что вместо явного указания индекса можно использовать переменную, и тогда индексом будет содержимое этой переменной (если оно строковое или численное).
И – небольшое дополнение по поводу массивов. Любой массив тоже является объектом, и кроме собственно элементов содержит несколько специальных значений и методов.
Наиболее используемым является свойство «length», то есть – размер массива.
Поэтому в цикле, объявленном как
for (var i = 0; i < forms.length; i++ ) {
переменная i будет изменяться от нулевого индекса массива до последнего индекса (который равен length – 1) включительно.
Обычно именно таким образом обрабатывают все элементы массива.
Строчка
elements[i] = get_elements_from_form(forms[i]);
не требует особых комментариев, учитывая вышенаписанное – здесь в элемент массива elements заносится результат работы функции get_elements_from_form(), которой каждый раз передается значение элемента массива forms.
В целом, вся функция попросту вызывает get_elements_from_form() для каждой формы и возвращает массив результатов.
Теперь настало время написать функцию get_elements_from_form().
Эта функция будет одним из ключевых элементов скрипта, и ее задача – получить все элементы с атрибутами.
Для того чтобы получить атрибуты, нужно знать следующее: каждая форма содержит список элементов, каждый из которых в свою очередь является объектом и содержит свойства -- атрибуты, значения и так далее. Эти объекты создаются браузером автоматически после загрузки HTML-документа.
Самыми используемыми атрибутами элементов являются имя, ID и значение. Доступ к ним выполняется при помощи свойств name, id и value соответственно.
Для наших целей этих свойств вполне достаточно, хотя нужно упомянуть, что эти свойства – не единственные, которые есть у элементов форм.
В коде функции, который я приведу ниже, будет показано, каким образом можно получить доступ к этим свойствам.
Как вы видите, эта функция получилась достаточно объемной, и сейчас вы поймете почему.
Все дело в radiobuttons. Как вы знаете, группа radio-кнопок в HTML-форме обладает интересным свойством -- у всех радиокнопок одной группы одинаковое имя (не ID). Это, во-первых, делает их группой, а во-вторых, значением этой группы является значение выбранного элемента.
С чекбоксами происходит похожая вещь – значение возвращается только если чекбокс отмечен.
Именно поэтому простым считыванием свойства value дело не заканчивается – приходится также проверять свойство checked кнопки, и уже на основании этого свойства выбирать, какое же значение будет у элемента.
Интерес в приведенном коде представляют следующие строки:
current_el = {name: temp_el.name, type: 'checkbox', value: temp_el.value};
Во всех подобных строках формируется новый объект, свойства которого задаются не литеральными выражениями, а значением переменных.
Так, в приведенной строке свойству name будет назначено значение свойства name переменной temp_el
elements.push(current_el);
Эта строка добавляет элемент в конец массива. Кроме метода push() массив имеет еще несколько методов, которые оперируют с данными массива. Этим методам будет посвящен отдельный раздел.
for (var rg in radio_groups) {
Это объявление «цикла по всем ключам».
Такое объявление используется для того, чтобы перебрать все ключи какого-либо объекта. Согласитесь, если в случае когда индексы были числами, было достаточно перебрать все числа от 0 до максимального индекса, в случае когда индексы строковые (то есть – это ключи) делать такой перебор неэффективно – да и как перебрать все строки?
Именно поэтому в языке JavaScript введена такая конструкция.
Внутри тела цикла переменная rg (в нашем случае, вообще там будет любая переменная) последовательно принимает значения всех ключей (за небольшими исключениями, но это в данном случае несущественно) объекта. И эту переменную можно использовать для доступа к свойству объекта.
Фактически, у нас уже может быть выполнена первая часть общей функции process(). Для проверки, правильно ли все работает, стоит каким-либо образом проверить данные, которые эта часть возвращает (то есть – содержимое переменной structure после выполнения функции get_forms_elements() ).
Для этого можно, например, вставить вместо вызова еще не написанной функции create_list() следующие строки:
str = structure[0][0]; alert (str.name + ' ' + str.value + ' ' + str.type);
Как результат, должно появиться окошко с именем, значением и типом нулевого элемента нулевой формы – в нашем случае это hidden – можете сами посмотреть html-код.
Если все работает, можно переходить к написанию функции-генератора списка.
Список, который должен получиться в результате, будем создавать при помощи DOM-функций.
Лично я написал бы такой код:
Как вы видите, я ввожу дополнительную функцию ce для того, чтобы не писать постоянно document.createElement().
Весь остальной код фактически объяснен в комментариях внутри самого кода – все выражения, которые вы здесь видите, уже вам знакомы или встречались раньше.
Кроме того, эта функция показывает те примеры работы с DOM-деревом, которые были описаны раньше, но не имели примеров.
Теперь нужно все функции собрать в один большой файл test.js (не забудьте убрать тестовый код, который вы использовали для проверки работоспособности функции get_forms_elements() и поставить на место вызов create_list()).
Изменение значений и параметров элементов
Здесь все с точностью до наоборот. Если в предыдущем коде мы брали значения из объектов – элементов форм, то в новом коде мы будем наоборот, заносить туда значения.
Никаких особых премудростей не будет – разве что такие «специальные штуки» как селект-боксы со списками значений... Собственно, именно этот случай мы и будем рассматривать.
Задача, которую нужно будет решить, формулируется так: добавить содержимое текстового поля к первому списку, если отмечен левый чекбокс, и ко второму списку, если отмечен правый чекбокс. После добавления чекбоксы и поле ввода нужно очистить.
Возьмем за основу уже существующий HTML-код со всеми полями. В этом коде есть и чекбоксы, которые мы сможем проверять, и выпадающие списки.
Опять разобъем задачу на несколько задач меньшего размера. Нам нужно во-первых, получить значение поля ввода (это мы уже делали), во-вторых, проверить, отмечены ли чекбоксы (это тоже), и, в третьих, добавить значение поля ввода к списку вариантов. Это несколько сложнее.
Итак, вот шаблон кода:
function process() { // Главная функция. var to_add = get_text(); if (is_checked('check_id_a')) { insert_value(to_add, 'select_id_from'); } if (is_checked('check_id_c')) { insert_value(to_add, 'select_id_to'); } clear_text_and_checkboxes(); }
Думаю, этот код даже не нуждается в пояснениях.
Код для функций получения значений тоже достаточно прост:
function get_text() { var el = document.getElementById('text_id'); return el.value; } function is_checked(what) { // Не забыть проверить, является ли элемент чекбоксом el = document.getElementById(what); if (el && el.type == 'checkbox') { return el.checked; } }
Но, при этом, так как функция is_checked() получает ID элемента извне – она предварительно проверяет, существует ли вообще элемент с таким ID, и является ли он чекбоксом. И только если проверки прошли, она возвращает состояние чекбокса, отмечен он или нет.
С добавлением элемента к выпадающему списку все в принципе тоже не так уж и сложно.
Это добавление можно делать как при помощи appendChild(), так и при помощи более раннего метода add(). В этом примере я буду использовать второй способ.
Код получится примерно таким:
function insert_value(what, into) { var el = document.getElementById(into); if (!el || el.type != 'select-one') { return false; } // до этой строки скрипт доходит только если указанный элемент -- выпадающий список var new_option = document.createElement('option'); new_option.text = what; new_option.value = 'option_'+what; el.add(new_option, null); // добавляем элемент в конец списка // этот элемент будет автоматически выбран el.selectedIndex = el.length -1; return true; }
Здесь, как вы понимаете, тоже идет проверка на тип элемента. Следует запомнить, что для выпадающих списков (которые задаются при помощи тега select) тип будет не select, а select-one для одиночных списков и select-multiple для списков с множественным выделением.
Функция add() списка требует два аргумента – первый это новый элемент (его нужно предварительно создать), а второй – это индекс элемента, перед которым нужно вставить этот элемент. К счастью, можно указать вместо этого параметра null, и тогда элемент будет добавлен в конец списка.
После добавления мы устанавливаем выбранным последний элемент – у списка есть параметр length, который равен числу элементов (почти как в массиве, только сами элементы списка доступны при помощи дополнительного свойства списка – options)
Функция очистки еще проще:
function clear_text_and_checkboxes() { document.getElementById('text_id').value=''; document.getElementById('check_id_a').checked=false; document.getElementById('check_id_c').checked=false; }
Здесь используется то свойство JavaScript, что необязательно записывать результат работы функции в переменную, чтобы потом обратиться к свойству этого результата. Можно выполнить обращение сразу после вызова (так как здесь – результат функции getElementById() это объект, у него есть свойство value, и этому свойству можно присвоить значение – и все в одной строке).
Конечно, работа с формами не ограничивается только описанными примерами. У элементов форм можно менять их тип (например, можно чекбокс сделать полем пароля – простой заменой параметра type), или запретить поле (установить атрибут disabled этого поля), и так далее. Но все эти вещи делаются практически так же как и уже описанные.
Теперь, как обычно во всех практических главах – задание.
Вам предстоит на основе все того же HTML-кода и примеров из главы создать код, который поместит все элементы из первого списка во второй, а из второго – в первый.
Операция remove() для списков существует, получает один параметр – индекс элемента.