5: HTML-формы

Bash CGI

5: HTML-формы

Элемент разметки FORM и его компоненты

Контейнер (элемент разметки) FORM позволяет определить в рамках HTML-документа форму ввода. В рамках этого контейнера размещаются все поля ввода, куда пользователь может поместить свою информацию. Если контейнер формы открыт, т.е. в документе указан тег начала контейнера <FORM ...>, то обязательно нужно указать и тег конца контейнера </FORM>.

В общем случае контейнер имеет следующий вид:

<FORM 
NAME=...
ACTION=url
METHOD=POST|GET|PUT|...
enctype=application/x-www-form-urlencoded|
        multipart/form-data
[target=window_name]
>
...
</FORM>

Атрибут NAME используется для именования формы. Это делается главным образом в JavaScript-программах. Атрибут ACTION задает URL, по которому отправляются данные из формы. Атрибут METHOD определяет метод передачи данных (фактически, речь идет о формировании сообщения, передаваемого по сети). Атрибут ENCTYPE определяет тип кодирования данных в теле сообщения и разбиение сообщения на части. Необязательный атрибут TARGET позволяет адресовать окно, в котором будет отображаться результат обработки данных из формы.

В рамках обзора применения контейнера FORM мы рассмотрим:

  • передачу данных по электронной почте;
  • передачу данных скрипту через атрибут ACTION;
  • передачу данных через Server Side Include.

Инициировать обмен можно при помощи JavaScript-кода, но рассматривать данный способ программирования обмена данными мы здесь не будем.

FORM (mailto)

Контейнер FORM позволяет определить в рамках HTML-документа форму ввода. В рамках этого контейнера размещаются все поля ввода, в которые пользователь может поместить информацию. Часто автор страниц Web-сайта по тем или иным причинам не имеет возможности программировать на стороне сервера. Однако это не означает, что он не может применять формы. Формы можно применять для отправки почты. Однако, как и в любом деле, здесь есть свои особенности, например:

<FORM ACTION=mailto:[email protected]>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

В данном примере (cgimail1.htm) мы пытаемся отправить значение поля формы n1 по электронной почте абоненту [email protected]. После заполнения поля и выбора кнопки "Отправить", браузер откроет окно программы почтового клиента, что не входило в наши планы. При этом само значение поля куда-то исчезнет.

Почему открывается новое окно? Несмотря на полный произвол, который царит в Web, и жесточайшую конкуренцию между Netscape и Microsoft, логика, заложенная в архитектуру World Wide Web Бернерсом Ли, обеими компаниями соблюдается. Дело в том, что, согласно спецификации RFC 822 (формат текстового сообщения Internet), на которую опираются протоколы HTTP и SMTP (Simple Mail Transfer Protocol, простой протокол электронной почты), сообщение может состоять из двух частей: заголовка и тела. В том виде, в каком мы используем контейнер FORM, метод доступа к ресурсу не указан и, следовательно, по умолчанию выбирается GET. У нас нет тела сообщения, а есть только заголовок.

Кроме того, в примере мы применяем схему URL mailto. Она соответствует спецификации протокола SMTP (обмен почтовыми сообщениями в Internet). В этой схеме, в отличие от схемы HTTP, расширенный путь после доменного имени стандартом не предусмотрен.

Итак, для того, чтобы получить тело сообщения, необходимо указать метод POST (cgimail2.htm). В этом случае сообщение должно уйти абоненту без открытия окна почтового клиента:

<FORM METHOD=post 
      ACTION=mailto:[email protected]>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

Любопытно, что в данном случае мы использовали протокол, отличный от HTTP — SMTP. В нем нет понятия метода доступа вообще. Тем не менее логика разбиения текстовых сообщений для всех протоколов одна и та же, как, собственно, и предполагалось при создании Web-технологии — унификация доступа к ресурсам.

На этом примере хорошо видны отличия URI от URL. В данном случае были возможны различные механизмы обработки данных в запросе на ресурс, который задается URI. Но конкретная реализация преобразования данных в запрос в рамках Web — это и есть URL, т.е. URI в рамках World Wide Web.

Расширим наш пример, добавив в него отправку абоненту внешнего файла по электронной почте. Такая задача встречается довольно часто. Например, поддержка архива электронных публикаций. Здесь нет необходимости в немедленном опубликовании материалов. Все статьи должны пройти экспертизу, которая требует определенного времени. Для этого модифицируем наш пример, добавив в него поле типа FILE:

<FORM METHOD=post 
      ACTION=mailto:[email protected]>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT NAME=file TYPE=file>
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

Почему в данном случае нельзя использовать метод GET, объяснялось выше. Метод POST должен обеспечить нам размещение всего файла в теле сообщения.

Однако все это верно, пока мы работаем с текстовыми файлами и находимся в рамках RFC822. А если нам нужно передать файл с длинными строками (Postscript) или просто двоичный файл? В таком случае необходимо обратиться к формату MIME. Это можно сделать при помощи еще одного атрибута контейнера FORMENCTYPE:

<FORM
ENCTYPE=multipart/form-data
METHOD=post
ACTION=mailto:[email protected]>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT NAME=file TYPE=file>
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

В данном случае по почте отправляется сообщение не в стандарте RFC822, а в стандарте MIME. Тело сообщения будет состоять из нескольких частей, а файл будет преобразован в ASCII-символы в соответствии со спецификацией BASE-64. Стандартный почтовый клиент воспринимает такой файл как присоединенный и позволяет его либо просмотреть, либо сохранить на диске.

FORM (HTTP)

Основной целью введения форм в HTML было обеспечение ввода данных в прикладную программу из универсального мультипротокольного браузера. При этом нужно отдавать себе отчет, что прикладная программа естественным образом должна выполняться на компьютере, где функционирует HTTP-сервер. Она не может работать в пустоте. Программу должен кто-то загружать, настраивать в адресном пространстве компьютера (linking), передавать ей управление и удалять из памяти после ее завершения.

Раз запрос от клиента принимает сервер, следовательно, и инициировать изложенные выше действия должен именно он.

Механизм инициирования такой прикладной программы определен в спецификации Common Gateway Interface. Там же задан и порядок обмена данными между HTTP-сервером и программой, которая в спецификации CGI именуется скриптом.

Метод GET

Основная задача формы — это предоставление шаблона ввода данных, которые будут переданы скрипту. Сам скрипт при этом указывается через URL, который задается в атрибуте ACTION:

<FORM ACTION=script.cgi>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT NAME=n2 VALUE="Поле2">
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

В данном примере скрипт просто распечатывает пару "имя поля формы — значение поля формы" в виде HTML-таблицы (formcgi1.htm). Правда, если присмотреться внимательно к происходящему на экране, можно обнаружить любопытную метаморфозу с URL скрипта при выборе кнопки "Отправить". В поле location окна браузера к скрипту после символа "?" приписываются пары "поле-значение", разделенные символом "&".

Данный запрос из формы определяют как запрос типа URLENCODED, переданный по методу GET. При передаче значений по методу GET формируется только заголовок HTTP-сообщения и не формируется его тело. Поэтому все содержание полей формы помещается в URL и таким образом передается скрипту. Из текста скрипта (formcgi2.htm) видно, что данные извлекаются из переменной окружения QUERY_STRING, в которую сервер помещает запрос.

Запросы, которые передаются в методе GET, можно условно разделить на два типа: ISINDEX и FORM-URLENCODED. FORM-URLENCODED мы только что рассмотрели, а ISINDEX был описан в разделах "Заголовок HTML-документа" и "Спецификация Common Gateway Interface", поэтому не будем повторяться.

Метод POST

Очевидно, что в строку URL нельзя втиснуть бесконечное число символов. И браузер, и среда, в которой функционирует сервер, имеют ограничения либо, как в случае браузера, по длине поля location, либо по длине области переменных окружения. Правда, последнее для современных систем не очень актуально. Например, операционная система IRIX 6.2 позволяет размещать в области переменных окружения данные объемом до 4 Mбайт. Тем не менее, для передачи относительно больших объемов предпочтительнее использовать метод доступа POST:

<FORM METHOD=post ACTION=script.cgi>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT NAME=n2 VALUE="Поле2">
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

В нашем примере в контейнере FORM появился атрибут METHOD, который принял значение POST. Результат работы скрипта не изменился, но сам скрипт претерпел существенные изменения. Теперь запрос принимается со стандартного ввода, а не из переменной окружения QUERY_STRING.

При методе POST данные передаются как тело HTTP-сообщения, и скрипт читает их со стандартного ввода. При этом есть один существенный нюанс, который ограничивает круг средств разработки скриптов для приема данных по POST. Он заключается в том, что сервер не закрывает канал передачи данных скрипту после передачи последнего символа запроса. В переменной CONTENT_LENGTH сервер сообщает, сколько данных со стандартного ввода нужно считать. Таким образом, язык программирования сценариев или универсальный язык программирования должны уметь читать определенное количество символов из стандартного ввода. Например, многие разновидности командных языков UNIX (Bourn-shell, Kernel-shell и т.п.) могут читать только строками и ждут закрытия входного потока.

Обычно при описании программирования CGI-скриптов рассматривают только методы GET и POST. В принципе, в форме можно указывать любые другие методы, например, PUT. Просто серверы не имеют стандартных модулей обработки этих методов, поэтому, кроме формы и скрипта, в случае нестандартного метода требуется произвести еще и соответствующую настройку сервера.

Кодирование

Существует два типа кодирования содержания (тела) HTTP-сообщения, которые можно определить в форме:

  • application/x-www-form-urlencoded
  • multipart/form-data

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

В URL документа можно использовать только символы набора Latin1. Это первая половина таблицы ASCII за вычетом первых 20 символов. Все остальные символы заменяются своими шестнадцатеричными эквивалентами. Кроме того, такие символы, как "+" или "&", играют роль разделителей или коннекторов. Если они встречаются в значении поля, то тоже заменяются на шестнадцатеричный эквивалент. Наиболее характерно это для работы с русским алфавитом. Поэтому скрипт, который принимает запросы, должен уметь эти символы декодировать.

Второй тип применяется для передачи двоичной информации в теле HTTP-сообщения. Если проводить аналогии с электронной почтой, то multipart/form-data обеспечивает присоединение файла данных (attachment) к HTTP-запросу. Наиболее типичным примером является передача файла с машины пользователя на сервер:

<FORM ACTION=script.cgi METHOD=post 
      ENCTYPE=multipart/form-data>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT NAME=n2 TYPE=file>
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

В данном случае HTTP-сообщение будет очень похоже на почтовое сообщение в стандарте MIME (собственно, это и есть MIME-сообщение, только передается оно по протоколу HTTP). Естественно, что для приема такого сообщения нужен скрипт, который бы смог разобрать его на части, а потом декодировать необходимую информацию.

FORM (SSI)

Когда говорят о формах, обычно предполагается, что в контейнере FORM обязательно должен быть указан адрес скрипта. Этот скрипт примет данные и "на лету" сгенерирует страницу, которая и будет возвращена пользователю. Из этого правила существует, по крайней мере, два исключения.

Во-первых, атрибут ACTION можно не указывать в том случае, если данные, введенные в форму, обрабатываются JavaScript-программой. В этом случае достаточно дать форме имя, чтобы к ее элементам (контейнерам) можно было обращаться. Передачу данных можно реализовать через метод submit, который будет выполняться при нажатии на гипертекстовую ссылку, например, formssi1.htm. Более подробно данный материал описан в главе "Программирование на JavaScript".

Во-вторых, принять данные можно через скрипт, который встроен в документ как Server Side Include. Этот способ мы рассмотрим более подробно.

Если не изменять базового адреса документа через контейнер BASE, то базовым адресом будет адрес загруженного документа. Если при этом в документе разместить форму вида:

<FORM>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT NAME=n2 VALUE="Поле2">
<INPUT TYPE=BUTTON VALUE="Отправить">
</FORM>

то после перезагрузки документа мы получим этот же документ, только в URL после символа "?" будет добавлено содержание формы (formssi2.htm).

Если теперь несколько видоизменить документ — вставить в него Server Side Include — получим:

<FORM>
<INPUT NAME=n1 VALUE="Поле1">
<INPUT NAME=n2 VALUE="Поле2">
<INPUT TYPE=BUTTON VALUE="Отправить">
<HR>
<!--#exec cgi=./cgi.cgi -->
</FORM>

Сам скрипт принимает запрос из QUERY_STRING и распечатывает его в виде HTML-таблицы (formssi3.htm). При этом результат распечатывается вслед за формой после горизонтального отчеркивания.

Точно так же можно обработать данные и методом POST, только для этого необходимо указать его в атрибуте METHOD контейнера FORM.

INPUT

Контейнер INPUT является самым распространенным контейнером HTML-формы. Существует целых 10 типов этого контейнера (text, image, submit, reset, hidden, password, file, checkbox, radio, button), причем каждый из них отображается по-разному.

В общем виде контейнер имеет вид:

<INPUT
NAME="Имя"
TYPE="Тип"
[вариации параметров, зависящие от типа]
>

Чаще всего контейнер INPUT применяется для организации текстового поля ввода: например, для ввода списка ключевых слов или для заполнения регистрационных форм.

INPUT (text)

Тип text контейнера INPUT определяет текстовое поле ввода, в котором пользователь (читатель) Web-страницы (узла) может ввести свою информацию. В DTD HTML 4.0 на поле INPUT ограничения по длине текстового поля не определены. Однако такие ограничения существуют. Они меняются от браузера к браузеру. Наиболее разумное ограничение — 256 символов.

Поле типа text имеет в общем случае следующий вид:

<INPUT NAME="Имя" TYPE=text SIZE=number 
       MAXLENGTH=number>

Атрибут NAME используется для именования поля как элемента формы. Имя поля попадает в запрос (левая часть пары "имя_поля-значение"), а также применяется в JavaScript для чтения и изменения значений текстовых полей формы.

Атрибут SIZE задает размер видимой на экране части текстового поля. Ниже приведен простой пример:

<FORM>
<INPUT SIZE=10>
<INPUT SIZE=20>
</FORM>

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

Атрибут MAXLENGTH задает максимальный размер поля. Он полезен в тех случаях, когда требуется ограничить вводимые данные по длине. Дополним поля из предыдущего примера этим атрибутом:

<FORM>
<INPUT SIZE=10 MAXLENGTH=15>
<INPUT SIZE=20 MAXLENGTH=15>
</FORM>

Максимальная длина поля равна в 15 символам. В первом случае строчка будет "прокручиваться" в горизонтальном направлении справа налево при превышении размера видимой области, равной 10 символам. Во втором случае ввод остановится во второй трети поля. В текстовых полях ввода используются шрифты фиксированной ширины.

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

<FORM>
<TABLE>
<TR>
<TD>Имя: </TD>
<TD><INPUT SIZE=5 MAXLENGTH=15></TD>
</TR>
<TR>
<TD>Фамилия: </TD>
<TD><INPUT SIZE=10 MAXLENGTH=15></TD>
</TR>
</TABLE>
</FORM>

В данном примере хорошо видно выравнивание полей по столбцам.

INPUT (password)

Тип password определяет текстовое поле, которое позволяет скрыть набираемый текст от посторонних глаз. По своим атрибутам поле типа password не отличается от поля типа text. При использовании этого поля следует понимать, что пароль или любая другая информация, которая вводится в поле типа password, будет передаваться по сети в виде ASCII-символов, т.е. будет доступна для просмотра при условии ее захвата посторонним лицом.

<FORM>
Пароль: <INPUT SIZE=10 TYPE=password>
</FORM>

В данном примере показано, как отображается поле этого типа при длине видимой части поля в 10 символов.

В принципе, существует несколько способов усилить защиту информации. Если включить штатный режим работы через SSL в браузере и сервере, то по сети будут передаваться зашифрованные сообщения. Более простым и не столь надежным методом может быть использование JavaScript.

INPUT (hidden)

Тип hidden был введен в формы по причине отсутствия поддержки сеансов в протоколе HTTP. Впоследствии были разработаны более эффективные способы эмуляции сеансов, например, cookie. Тем не менее поля hidden до сих пор могут использоваться для поддержки сеансов. В ряде случаев, когда запросы вынуждены проходить через сито систем защиты, это единственный способ реализации поддержки сеансов.

<FORM>
<INPUT MAXLENGTH=250 TYPE=hidden>
</FORM>

В данном случае мы задали невидимое поле шириной в 250 символов. Очевидно, что в случае невидимого поля задавать атрибут размера видимой части поля бессмысленно, поэтому атрибут SIZE в невидимых полях не употребляется.

При организации сеансовой работы через hidden-поля нужно учитывать некоторые нюансы, связанные с необходимостью инициировать событие передачи данных из формы. Это событие onSubmit. Оно наступает тогда, когда пользователь нажимает на кнопку типа submit или вызывает это событие из JavaScript.

Если на странице нет видимых полей, то там, естественно, нет и кнопки submit. В этом случае непосредственно вызвать событие передачи данных из формы не получится. Остается только второй способ — JavaScript. Если переход осуществляется по гипертекстовой ссылке на том же Web-узле, событие можно обрабатывать скриптом, связанным со ссылкой (hide2.htm). При этом следует учитывать факт отображения URL в полях location и status браузера (hide3.htm).

INPUT (checkbox)

Тип checkbox применяется в качестве селектора. Если, например, в заявке на комплектующие нужно выбирать из нескольких заранее определенных позиций, то можно применить поле типа checkbox:

<FORM>
1. Mouse — <INPUT NAME=mouse TYPE=checkbox>
2. Keyboard — <INPUT NAME=key TYPE=checkbox>
3. Monitor — <INPUT NAME=monitor TYPE=checkbox>
</FORM>

При использовании полей checkbox следует учитывать, что они хороши там, где не нужно выбирать. Например, на вопрос "Являетесь ли вы членом профсоюза?" может быть только два взаимоисключающих ответа: да или нет. В этом случае применяют поле другого типа — radio.

Отображение поля типа checkbox в запросе зависит от параметров, которые указаны в контейнере INPUT. Очевидно, что атрибуты длины и видимости в случае checkbox не применяются. Для checkbox, как и для любого другого поля, необходимо применение атрибута NAME, иначе значение поля не будет учтено в запросе. Кроме того, в checkbox используются атрибуты VALUE и CHECKED.

По умолчанию, если поле отмечено как выбранное пользователем, в запрос попадает пара "name=on", где name — имя поля. Атрибут VALUE позволяет изменить значение выбранного поля. Например:

<FORM>
Mouse — <INPUT NAME=mouse VALUE=mouse 
               TYPE=checkbox>
</FORM>

В этом случае вместо "on" в правой части равенства появится "mouse".

Атрибут CHECKED определяет состояние поля по умолчанию, т.е. в момент первоначальной загрузки страницы или выбора кнопки Reset. Если он указан, то поле считается по умолчанию выбранным (отмеченный прямоугольник):

<FORM>
Mouse — <INPUT NAME=mouse VALUE=mouse 
               TYPE=checkbox CHECKED>
</FORM>

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

Важным отличием поля типа checkbox от поля типа radio является обработка полей с одинаковыми именами. В случае поля типа checkbox — это разные поля, и их значения никак не связаны между собой. При одновременном выборе полей с одинаковыми именами в запрос попадут все выбранные поля. При этом пары "имя=значение" будут просто повторяться. Другой вариант можно реализовать только через поля типа radio. Можно, конечно, исхитриться и сделать альтернативным вариант через поля типа checkbox, используя JavaScript.

INPUT (radio)

Тип radio контейнера INPUT определяет поле "селектор". Данный тип применяется там, где необходимо обеспечить выбор из нескольких заданных взаимоисключающих вариантов. Например, в анкете может быть графа "Пол":

<FORM>
Пол:
<INPUT NAME=sex TYPE=radio>Мужской
<INPUT NAME=sex TYPE=radio>Женский
</FORM>

В данном случае при выборе одного из вариантов со второго автоматически снимается отметка. Это главное отличие типа поля radio от типа checkbox. Обратите внимание на то, что имена полей одинаковые.

Если наш пример оставить как он есть, то скрипт, который будет принимать данные, не получит сведений о том, какой из вариантов был выбран. В любом случае будет выдаваться запрос типа: "?sex=on". Если вариантов не выбирать, то соответствующая пара "имя_поля-значение" вообще не появится в запросе.

Для того, чтобы указать выбранный вариант в контейнере INPUT, нужно ввести атрибут VALUE:

<FORM>
Пол:
<INPUT NAME=sex TYPE=radio VALUE=m>Мужской
<INPUT NAME=sex TYPE=radio VALUE=f>Женский
</FORM>

В данном случае вместо "on" передается соответствующее значение. Скрипт теперь в состоянии различить выбранный вариант.

Если в контейнерах INPUT типа radio задать разные имена, то разницы между этим типом и типом checkbox почти не будет:

<FORM>
Пол:
<INPUT NAME=sex1 TYPE=radio VALUE=m>Мужской
<INPUT NAME=sex2 TYPE=radio VALUE=f>Женский
</FORM>

Слово "почти" означает отсутствие возможности отменить выбор альтернативы, если она уже была выбрана. Таким образом, предполагается, что один из вариантов должен быть выбран обязательно, что должно заметно влиять на применение полей INPUT типа radio.

В данном контексте следует рассматривать и применение атрибута CHECKED в полях этого типа. Как и в checkbox, атрибут CHECKED позволяет определить значение поля по умолчанию. В нашем случае выбор по умолчанию из набора вариантов:

<FORM>
Пол:
<INPUT NAME=sex TYPE=radio VALUE=m 
       CHECKED>Мужской
<INPUT NAME=sex TYPE=radio VALUE=f>Женский
</FORM>

Пусть в нашем примере речь идет о приеме на вредную работу, на которую нанимаются преимущественно мужчины. Альтернативой по умолчанию станет значение "m" для поля с именем sex. Значение по умолчанию устанавливается либо при первичной загрузке страницы, либо при выборе кнопки типа reset.

INPUT (image)

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

В данном типе множество атрибутов контейнера INPUT дополняется атрибутами контейнера IMAGE:

<FORM>
<INPUT TYPE=image SRC=image.gif NAME=i 
       ALIGN=left BORDER=0>
</FORM>

При размещении поля этого типа атрибут NAME, кроме стандартных функций именования, выполняет еще и функции атрибута ALT из контейнера IMG. При наведении манипулятора "мышь" на картинку появляется значение поля NAME. Выбор данного поля манипулятором "мышь" приводит к немедленной передаче данных из формы серверу, а затем скрипту. При этом координаты манипулятора "мышь" передаются в виде "имя_поля.x=DD&имя_поля.y=DD".

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

В отличие от других полей, отсутствие атрибута NAME не приводит к отсутствию данных о поле в запросе. В этом случае будет просто передана пара x и y ("имя_поля.x=DD&имя_поля.y=DD").

Если такое поле в форме одно, имя поля можно вообще опустить. В этом случае нежелательно использовать имена x и/или y для именования других полей.

При наличии поля типа image меняется реакция на клавишу Enter клавиатуры компьютера, если фокус ввода находится в одном из полей формы. Происходит немедленная передача данных серверу. Это следует учитывать при разработке скриптов, которые принимают данные из форм с полем image. Пользователь может по ошибке нажать на Enter, еще не заполнив необходимые поля формы.

INPUT (button)

Во всех современных интерфейсах есть объекты, имитирующие кнопки управления. Интерфейс HTML-форм в этом смысле не является исключением. Контейнер INPUT позволяет создать кнопку при помощи типа button. Изначально в формах было только две кнопки: submit и Reset.

submit позволяет инициировать отправку данных серверу из формы и перезагрузку текущей страницы. В терминах JavaScript последовательность действий браузера, которую вызывает эта кнопка, называется событием onSubmit.

Reset позволяет выставить значения полей формы по умолчанию. Это бывает необходимо в случае неправильного ввода данных. В терминах JavaScript событие, вызванное выбором кнопки Reset, называется событием onReset.

Тип button контейнера INPUT является обобщением и расширением случаев submit и reset на более широкий класс объектов, которые принято называть кнопками. Форма с кнопками может выглядеть, например, следующим образом:

<FORM>
<INPUT TYPE=button VALUE="Кнопка">
</FORM>

Кнопки, в отличие от типа image, не вызывают события submit в случае их нажатия, и данный пример — лишнее тому подтверждение. Более того, даже после нажатия на кнопку пара "имя_поля-значение" в запрос не попадает. В общем случае это понятно. Ведь браузер не фиксирует положение кнопки (хотя и мог бы), поэтому и передавать нечего.

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

Из всего сказанного пока не было понятно, каким образом используются кнопки. Дело в том, что с ними в JavaScript связано событие onClick, которое можно обработать функцией пользователя: например, чтобы послать данные формы.

Кнопка гораздо удобнее гипертекстовой ссылки, если не требуется перезагрузка страницы. При выборе гипертекстовой ссылки перезагрузка произойдет обязательно, если только не позаботиться об этом при указании атрибута HREF контейнера A (anchor). При выборе кнопки перезагрузка страницы не производится, и можно оставаться в пределах текущей страницы, управляя объектами интерфейса на ней, например, перезагружая картинки.

INPUT (submit)

Кнопка submit — едва ли не самый важный элемент формы. Она инициирует отправку данных формы на сервер. В первых реализациях HTML-форм в браузере Mosaic только при нажатии на эту кнопку происходила отправка данных. В настоящее время такое событие может произойти в нескольких случаях:

  • в форме только одно текстовое поле (fisub1.htm);
  • в форме есть поля типа image (fisub2.htm);
  • в форме указана кнопка submit (fisub3.htm).

При указании поля типа submit следует учитывать, что если полю дать имя, то оно появится в запросе:

<FORM>
<INPUT TYPE=submit VALUE="Отправить?">
<INPUT TYPE=submit NAME=s>
</FORM>

Пример демонстрирует сразу две особенности полей типа submit. Во-первых, если задать значение атрибута VALUE, то оно отобразится в качестве текста на кнопке (первая кнопка). При этом следует помнить, что если текст содержит пробелы, то его нужно заключить в кавычки. Во-вторых, если не указывать атрибута VALUE, то будет подставлено значение кнопки по умолчанию. В-третьих, если есть имя, то при нажатии на кнопку произойдет событие onSubmit, и в запросе, который будет послан на сервер, появится пара "имя_поля_submit-значение".

Здесь мы сталкиваемся с той же дилеммой, что и в полях типа image. Если задавать многословные значения, то их потом придется "выковыривать" в скрипте из запроса, если задавать короткие, то пользователь не поймет, что от него хотят.

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

INPUT (reset)

Кнопка Reset, наверное, есть самый простой элемент формы. Поле типа reset представляет собой кнопку, при нажатии на которую все исправления и назначения в форме принимают свои значения по умолчанию. Кнопка восстанавливает статус умолчания для всех полей формы. Статус по умолчанию можно установить только путем полной перезагрузки формы с сервера. Любопытно, но по Reload (обновление документа) установка статуса по умолчанию может не выполняться. Для того чтобы в этом убедиться достаточно внести исправления в текстовое поле примера, который расположен ниже, и после этого нажать на Reload — изменения останутся в силе (Netscape 4.0):

<FORM>
<INPUT TYPE=text VALUE="Отправить?">
<INPUT TYPE=reset NAME=s>
</FORM>

Выбор кнопки Reset вернет значение текстового поля в исходное состояние. Стоит отметить, что если перевести курсор в поле location браузера и нажать Enter, то в этом случае значение поля будет установлено равным значению по умолчанию. Таким образом, событие onReset не происходит при перезагрузке страницы с помощью кнопки Reload. Этот момент следует учитывать авторам страниц при разработке форм, а пользователям Web – при их заполнении.

INPUT (file)

Идея передачи больших объемов данных давно владела умами разработчиков программного обеспечения для World Wide Web. При этом возможностей полей textarea для решения этой задачи было явно недостаточно. Во-первых, можно передавать только текстовую информацию; во-вторых, текст нужно набирать вручную непосредственно в браузере; в-третьих, существует (точнее — существовало) ограничение на длину строки в поле textarea, которое перекочевало в формы из стандарта RFC822. Таким образом, в первой спецификации форм возможности передать любые данные с компьютера клиента на сервер не было.

Проблема, на первый взгляд, должна была решаться за счет метода доступа PUT, определенного в протоколе HTTP и призванного обеспечить размещение данных на стороне сервера. Но здесь обнаружился типичный недостаток любых теоретических установок, проявившийся еще в языке программирования Algol — отсутствие интерфейса для использования данного механизма. Ни один браузер не имеет стандартного средства для применения метода PUT. Реально данный метод стал использоваться только с появлением программ подготовки страниц.

Тем не менее типовая задача, которая возникает при создании архивов публикаций, заключается в необходимости отправить на сервер файл, содержащий публикацию. Первое, что приходит на ум — это электронная почта. MIME позволяет переслать составные почтовые сообщения и кодировать двоичные данные в них (fifile1.htm). Естественным решением является распространение этого способа и на Web.

<FORM ENCTYPE=multipart/form-data METHOD=post
      ACTION=mailto:[email protected]>
<INPUT TYPE=file NAME=file>
<INPUT TYPE=submit VALUE="Опубликовать">
</FORM>

В данном примере мы отправляем пользователю help на хосте intuit.ru файл по протоколу SMTP. Следует обратить внимание на контейнер FORM. Файл нельзя передать в качестве дополнения к URL, следовательно, нужен метод, который сможет, кроме заголовка, сформировать еще и тело сообщения. Для этой цели используется метод POST. Для того чтобы обеспечить составное тело сообщения, мы применяем тип кодирования содержания multipart/form-data.

По умолчанию используется другой метод кодирования — application/x-www-form-urlencoded, поэтому его надо переопределить через атрибут ENCTYPE. При передаче двоичного файла по SMTP используется кодирование BASE-64, т.е. такое же, как и при режиме attachment в программе почтового клиента (fifile2.htm).

Но кардинальное решение заключается в вызове скрипта, который примет файл и выполнит его преобразование, разместит файл на сервере, или сделает что-либо еще.

<FORM ENCTYPE=multipart/form-data METHOD=post
      ACTION=file.cgi>
<INPUT TYPE=file NAME=file>
<INPUT TYPE=submit VALUE="Опубликовать">
</FORM>

Одно из преимуществ данного подхода заключается в том, что файл присоединяется к сообщению как двоичные данные. При вставке содержимого файла в тело HTTP-сообщения никакого преобразования не происходит. В сущности, этот подход нарушает стандарт текстового сообщения Internet, на котором базируется протокол HTTP. Но для работы он чрезвычайно удобен (fifile3.htm).

Механизм передачи файлов от клиента к серверу получил название File-upload. В отличие от обычной обработки запросов типа form-urlencoded при File-upload необходимо выполнить последовательно три основных группы действий:

  • выделить части составного сообщения;
  • найти описатели каждой из частей сообщения;
  • выделить и обработать содержание каждой из частей составного сообщения.

При этом само сообщение практически повторяет формат MIME, т.е. точно так же есть граница, разделяющая части (boundary), Content-encoding и Content-type (fifile4.htm). Выполнить разбор составного сообщения не так легко. Тем более, что передаваться могут файлы разных форматов. Некоторые из них могут при этом претерпевать и конвертацию в транспортные форматы. Поэтому целесообразнее использовать библиотеки готовых модулей для работы в режиме File-upload.

Наиболее популярным средством разработки скриптов является язык Perl. Для Perl существует несколько стандартных модулей, которые ориентированы на работу с объектами World Wide Web. Естественно, что данные модули поддерживают и режим File-upload.

Самым простым из них, пожалуй, является модуль CGI_Lite. Данный модуль ориентирован только на разбор данных из форм. Режим File-upload реализован в нем достаточно просто и эффективно.

Другим популярным средством является набор модулей CGI_modules. Данный набор, кроме формирования HTML-страниц средствами функций из своей библиотеки, позволяет работать и c File-upload.

Еще одно такое средство — модуль CGI.pm. Он является прообразом многих компонентов из CGI_modules. В нем предусмотрен также механизм обработки File-upload.

Можно назвать еще ряд инструментов, которые позволяют обрабатывать составные сообщения. Однако приведенных примеров вполне достаточно для того, чтобы продемонстрировать возможности отправки файлов через Web-браузер.

SELECT

Контейнер SELECT применяется в формах для создания ниспадающих списков или списков прокрутки. При этом можно организовать выбор из множества вариантов только одного или отметить сразу несколько вариантов отчетов. В некотором смысле этот контейнер реализует возможности сразу двух типов контейнера INPUT: checkbox и radio.

На самом деле ниспадающее меню или список прокрутки реализуется при вкладывании внутрь контейнера SELECT контейнеров OPTION. Именно они определяют варианты выбора или элементы списка:

<FORM>
<SELECT>
</SELECT>
</FORM>

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

Для реализации реального выпадающего меню внутрь контейнера SELECT нужно вложить контейнеры OPTION:

<FORM>
<SELECT>
<OPTION>Понедельник
<OPTION>Вторник
<OPTION>Среда
<OPTION>Четверг
<OPTION>Пятница
<OPTION>Суббота
<OPTION>Воскресенье
</SELECT>
</FORM>

Рис. 23.1. 

По умолчанию в качестве текущей опции устанавливается первая опция списка. Как и в случае с полями INPUT, если после выбора опции нажать кнопку Reload, изменения выбора не произойдет. Значение по умолчанию устанавливается либо кнопкой Reset, либо при наборе в поле location браузера, либо при переходе по гипертекстовой ссылке. Для принудительного указания выбора по умолчанию следует воспользоваться атрибутом SELECT контейнера OPTION. (открыть)

Выбранное значение альтернативы становится значением поля SELECT, и в запросе передается пара "имя_поля_select-значение_альтернативы" (fisel2.htm).

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

Для того, чтобы сформировать список прокрутки в контейнере SELECT, нужно указать атрибут SIZE:

<FORM>
<SELECT SIZE=5>
<OPTION>Понедельник
<OPTION>Вторник
<OPTION>Среда
<OPTION>Четверг
<OPTION>Пятница
<OPTION>Суббота
<OPTION>Воскресенье
</SELECT>
</FORM>


Рис. 23.2. 

В данном случае мы реализовали окно прокрутки для пяти вариантов. Интересно отметить, что, например, в Netscape Navigator при перезагрузке по Reload выбранная альтернатива остается, а вот список устанавливается на начало. Так, в нашем случае при выборе "Воскресенье" после перезагрузки эта альтернатива окажется невидимой. В ряде случаев это вводит пользователя в заблуждение. (открыть)

Атрибут SIZE не изменяет самого характера работы с полем. Оно продолжает оставаться списком вариантов, из которого можно выбрать только один. Для организации множественного выбора в контейнере SELECT необходимо указать атрибут MULTIPLE:

<FORM>
<SELECT SIZE=5 MULTIPLE>
<OPTION SELECTED>Понедельник
<OPTION>Вторник
<OPTION selected>Среда
<OPTION>Четверг
<OPTION>Пятница
<OPTION>Суббота
<OPTION>Воскресенье
</SELECT>
</FORM>


Рис. 23.3. 

В данном примере по умолчанию выбрано два дня недели: понедельник и среда. По идее, пользователь должен иметь возможность либо устанавливать отметку о выборе опции, либо снимать ее. При этом таких отметок в одном списке может быть несколько. Однако практика показывает, что это не всегда так. Например, в Netscape Navigator 4.01 для Windows 3.1 при первоначальной загрузке страницы, действительно, отмечено две опции по умолчанию. Когда же пользователь начинает отмечать дополнительные опции, то выбрать удается только одну из них, при этом отметка по умолчанию снимается, т.е. мы имеем дело со списком альтернатив. Аналогично ведет себя и браузер Mosaic, например. Таким образом, обозначенная возможность множественного выбора поддерживается не всегда. (открыть)

OPTION

Контейнер OPTION никогда не используется сам по себе. Его применение имеет смысл только в контексте контейнера SELECT. В контейнере OPTION можно указывать два атрибута: VALUE и SELECTED.

Атрибут VALUE позволяет задать полю SELECT значение, отличное от альтернативы, которая определена в OPTION. Это позволяет существенно упростить обработку запроса. Например, при выборе дней недели вместо их полных имен можно использовать сокращения: (открыть)

<FORM>
<SELECT NAME=s>
<OPTION VALUE=1>Понедельник
<OPTION VALUE=2>Вторник
<OPTION VALUE=3>Среда
<OPTION VALUE=4>Четверг
<OPTION VALUE=5>Пятница
<OPTION VALUE=6>Суббота
<OPTION VALUE=7>Воскресенье
</SELECT>
<INPUT TYPE=submit>
</FORM>


Рис. 23.4. 

Если в данном примере выбрать кнопку "Submit Query", то в строке location можно убедиться в том, что поле s действительно приняло значение, которое определено в VALUE контейнера OPTION.

Второй возможный атрибут, SELECTED, определяет значение (значения) поля SELECT по умолчанию. Если задано несколько выбранных по умолчанию опций, то в обычном (селекторном) SELECT выбирается в качестве опции по умолчанию последняя, если это множественный выбор, то — все: (открыть)

<FORM>
<SELECT NAME=s>
<OPTION VALUE=1 selected>Понедельник
<OPTION VALUE=2>Вторник
<OPTION VALUE=3 selected>Среда
<OPTION VALUE=4>Четверг
<OPTION VALUE=5>Пятница
<OPTION VALUE=6>Суббота
<OPTION VALUE=7>Воскресенье
</SELECT>
<INPUT TYPE=submit>
</FORM>


Рис. 23.5. 

Любопытно, что контейнер OPTION не именуется. Более того, при программировании в JavaScript все действия над значениями опций выполняются в рамках объекта SELECT.

TEXTAREA

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

<FORM>
<TEXTAREA NAME=s>
Здесь можно ввести произвольный текст
</TEXTAREA>
<INPUT TYPE=submit>
</FORM>

Содержание поля textarea можно передать как методом get (fiarea1.htm), т.е. в URL скрипта, так и методом POST (fiarea2.htm).

Атрибуты контейнера textarea позволяют сформировать на экране окно, где будет отображаться значение поля, и правила этого отображения. Атрибут COLS определяет число столбцов в окне. Текст отображается в поле textarea фонтом фиксированной ширины, и этот атрибут задает ширину поля в символах:

<FORM>
<TEXTAREA NAME=s COLS=5>
Здесь можно ввести произвольный текст
</TEXTAREA>
<INPUT TYPE=submit>
</FORM>

Аналогично ширине поля можно задать и его высоту в строках текста:

<FORM>
<TEXTAREA NAME=s COLS=15 ROWS=5>
Здесь можно ввести произвольный текст
</TEXTAREA>
<INPUT TYPE=submit>
</FORM>

И последний штрих — управление отображением текста. Во всех предыдущих примерах текст "вылезал" за правый край экрана, и его приходилось листать по горизонтали. Это достаточно неудобно. Кроме того, всегда существует дилемма: передавать текст на сервер как он есть (с переводом строк) или вытянуть в одну длинную строку. В Netscape для решения этих задач используют атрибут WRAP. Этот атрибут может принимать несколько значений:

  • off — отключить выравнивание внутри поля (fiarea3.htm);
  • virtual — включить выравнивание, но передавать как длинную строку (fiarea4.htm);
  • physical — включить выравнивание, но передавать вместе с переводом строк (fiarea5.htm).

Наиболее интересен второй случай. В практике обработки данных скриптом очень часто приходится вытягивать ввод в одну строку и потом сравнивать ее с шаблоном. В случае WRAP=virtual мы избегаем первого шага:

<FORM>
<TEXTAREA NAME=s COLS=15 ROWS=5 WRAP=virtual>
Здесь можно ввести произвольный текст
</TEXTAREA>
<INPUT TYPE=submit>
</FORM>