[Назад] [Содеожание] [Дальше]

Сценарии JavaScript в активных страницах Web

© Александр Фролов, Григорий Фролов
Том 34, М.: Диалог-МИФИ, 1998, 288 стр.

3 Работа с формами

  • Иерархия объектов в формах
  • Форма и ее свойства
  • Объекты, входящие в состав форм
  • Проверка заполнения формы
  • Шестнадцатеричный калькулятор
  • Электронные часы
  • Сценарии JavaScript очень часто применяются для предварительной обработки данных, введенных пользователями в формах. Проверенные данные затем могут предаваться расширениям CGI или ISAPI сервера Web. Кроме того, с помощью форм и сценариев JavaScript можно создавать активные страницы, способные получать от пользователя и обрабатывать произвольную информацию.

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

    Иерархия объектов в формах

    Как мы уже говорили, при загрузке документа HTML в окно браузера Internet создается объект класса Document. Если в этом документе определены формы, то они доступны сценариям JavaScript как свойства объекта Document.

    В разделе “Ссылки и метки в документе” предыдущей главы мы определили форму, задав для нее имя Sel с помощью параметра NAME оператора <FORM>:

    
    <FORM NAME="Sel">
      <SELECT NAME="ListOfLinks">
      </SELECT>
      <INPUT TYPE="button" VALUE="Jump!" onClick="urlJump();">
    </FORM>
    

    В результате у объекта Document появилось свойство Sel. К этому свойству можно адресоваться следующим образом:

    
    Document.Sel
    

    Любая форма содержит определения полей и органов управления, таких как списки, переключатели и кнопки. При определении полей и органов управления им можно задать имя, указав его в параметре NAME соответствующего оператора языка HTML. В примере, приведенном выше, в форме определен список с именем ListOfLinks.

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

    
    Document.Sel.ListOfLinks
    

    Так как поля формы и органы управления являются объектами, то для них определены свои свойства и методы. Например, свойство selectedIndex, содержащее номер выбранного пользователем элемента списка, доступно следующим образом:

    
    Document.Sel.ListOfLinks.selectedIndex
    

    С помощью рис. 2.2 вы можете проследить иерархию объектов документа и, в частности, иерархию объектов в формах.

    Заметим, что в качестве одного из свойств объекта Document определено свойство forms. Это массив всех форм, расположенных в документе HTML. Размер массива равен document.forms.length.

    Вы можете адресоваться к формам как к элементам массива forms, причем первый элемент массива (с индексом 0) соответствует самой первой форме, определенной в документе, второй - следующей и так далее. Однако удобнее обращаться к формам по их именам, заданным в параметре NAME оператора <FORMS>.

    Форма и ее свойства

    Подробно о том, как создавать формы в документах HTML, мы рассказывали в 29 томе “Библиотеки системного программиста”. Сейчас мы повторим основные моменты, сделав акцент на вопросах, важных для обработки форм средствами сценариев JavaScript.

    Оператор <FORM>

    В наиболее общем виде оператор <FORM>, предназначенный для создания форм в документах HTML, выглядит следующим образом:

    
    <FORM
      NAME="Имя_формы"
      TARGET="Имя_окна"
      ACTION="Адрес_URL_расширения_сервера"
      METHOD=GET или POST
      ENCTYPE="Кодировка_данных"
      onSubmit="Обработчик_события_Submit">
    
      . . .
      определение полей и органов управления
      . . .
    </FORM>
    

    Параметр NAME задает имя формы. Вы можете его не указывать, если форма не предназначена для совместной работы со сценарием JavaScript, однако это не наш случай. Нам это имя нужно для адресации формы как свойства объекта Document, поэтому мы всегда будем его задавать.

    Назначение параметра TARGET аналогично назначению этого же параметра в операторе <A>. Когда форма используется для передачи запроса расширению CGI или ISAPI сервера Web, ответ, полученный от сервера, отображается в окне. Имя этого окна задано параметром TARGET. Если ответ должен отображаться в том же окне, что и форма, то параметр TARGET задавать не нужно.

    С помощью параметра ACTION указывается адрес URL загрузочного файла программы CGI или библиотеки динамической компоновки DLL расширения ISAPI, а также передаваемые расширению параметры. В том случае, когда форма предназначена для передачи данных расширению сервера Web, параметр ACTION является обязательным. Но если данные, введенные в форме, обрабатываются сценарием JavaScript локально и не передаются серверу Web, этот параметр можно опустить.

    Параметр METHOD задает метод передачи данных из формы расширению сервера Web и может принимать значения GET или POST . Подробнее об этом вы можете прочитать в только что упомянутом нами 29 томе “Библиотеки системного программиста”. Если данные из полей формы обрабатываются сценарием JavaScript локально, параметр METHOD задавать не нужно.

    Параметр ENCTYPE задает тип MIME передаваемых данных и используется очень редко. Если форма предназначена для передачи текстовых данных (как это обычно бывает), этот параметр по умолчанию имеет значение application/x-www-form-urlencoded. В этом случае для передаваемых данных используется так называемая кодировка URL. Тип данных multipart/form-data позволяет передавать как текстовые, так и двоичные данные. Очевидно, при локальной обработке данных сценарием JavaScript параметр ENCTYPE не задается.

    Помимо параметров, для формы можно определить обработчик события, связанный с кнопкой типа SUBMIT. Такая кнопка предназначена для посылки данных из заполненной формы расширению сервера Web. Назначив обработчик события, сценарий JavaScript может управлять этим процессом.

    Свойства объекта form

    Объект form имеет два набора свойств, состав одного из которых является фиксированным, а состав другого зависит от того, какие поля и органы управления определены в форме.

    Первый набор свойств приведен ниже:

    Свойство

    Описание

    action

    Содержит значение параметра ACTION

    elements

    Массив всех элементов (полей и органов управления), определенных в форме

    encoding

    Содержит значение параметра ENCTYPE

    length

    Размер массива elements

    method

    Содержит значение параметра METHOD

    target

    Содержит значение параметра TARGET

    Большинство свойств первого набора просто отражает значения соответствующих параметров оператора <FORM>. Вы можете их использовать в сценариях JavaScript для проверки параметров.

    Что же касается массива elements, то в нем находятся объекты, соответствующие полям и органам управления, определенным в форме. Эти объекты образуют второй набор свойств объекта form:

    Свойство

    Описание

    button

    Кнопка с заданной надписью

    checkbox

    Переключатель типа Check Box. Может использоваться в составе набора независимых друг от друга переключателей или отдельно

    hidden

    Скрытое поле, которое не отображается. Содержимое этого поля может быть считано и проанализировано расширением сервера Web, либо сценарием JavaScript

    password

    Текстовое поле для ввода паролей. Набранный в этом поле текст не отображается на экране

    radio

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

    reset

    Кнопка, с помощью которой пользователь может сбросить содержимое полей ввода и состояние переключателей в их исходное состояние

    select

    Список произвольных текстовых строк

    submit

    Кнопка для посылки данных из заполненной формы расширению сервера Web. Для этой кнопки можно задать произвольную надпись

    text

    Однострочное текстовое поле

    textarea

    Многострочное текстовое поле

    Объекты, входящие в состав форм

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

    Кнопка button

    В общем виде кнопка класса button определяется в форме с помощью оператора <INPUT> следующим образом:

    
    <INPUT TYPE="button"
      NAME="Имя_кнопки"
      VALUE="Надпись_на_кнопке"
      onClick="Обработчик_события">
    

    Параметр TYPE оператора <INPUT> должен иметь значение button, как это показано в нашем примере.

    С помощью параметра NAME задается имя объекта, соответствующего кнопке (а не надпись на кнопке). Надпись на кнопке указывается с помощью параметра VALUE.

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

    Свойства объекта button

    Объект button имеет два свойства, отражающие значения соответствующих параметров оператора <INPUT>:

    Свойство

    Описание

    name

    Значение параметра NAME

    value

    Значение параметра VALUE

    Методы объекта button

    Для объекта button определен всего один метод, не имеющий параметров, - click:

    
    click()
    

    Вызов этого метода приводит к такому же эффекту, что и щелчок левой клавишей мыши по кнопке.

    Пример сценария

    В качестве первого примера мы приведем сценарий, выполняющего обработку щелчка по кнопке с надписью Click me, расположенной в форме (рис. 3.1).

    Рис. 3.1. Форма в документе HTML с кнопкой

    Если нажать на эту кнопку, сценарий отобразит в окне браузера свойства кнопки как объекта класса button (рис. 3.2).

    Рис. 3.2. Отображение свойств кнопки как объекта класса button

    Исходный текст документа HTML со встроенным в него сценарием JavaScript приведен в листинге 3.1.

    Листинг 3.1. Файл chapter3/button/button.html

    ;
      <HEAD>
        <TITLE>Button demo</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
        function btnClick()
        {
          var szTxt="";
          var szTxt1="";
          szTxt=document.TestForm.bt.value;
          szTxt1=document.TestForm.bt.name;
          document.write("<HR>");
          document.write("You press button: " + szTxt.bold()
    + ", name=" + szTxt1.bold());
          document.write("<HR>");
        }
        // -->
        </SCRIPT>
      </HEAD>
      <BODY BGCOLOR=white>
        <H1>Click Button</H1>
        <FORM NAME="TestForm">
          <P><INPUT TYPE="button" NAME="bt" VALUE="Click me!"
          onClick="btnClick();">
        </FORM>
      </BODY>
    </HTML>
    

    В теле документа HTML определена форма с именем TestForm, для чего в операторе <FORM> мы указали параметр NAME. Другие параметры этого оператора здесь не нужны.

    Форма содержит одну-единственную кнопку с именем bt и надписью Click me. Для этой кнопки в качестве обработчика события мы назначили функцию btnClick, определенную в заголовке документа HTML.

    Когда пользователь нажимает кнопку, функция btnClick получает управление и сохраняет в текстовых переменных szTxt и szTxt1 надпись и имя кнопки, соответственно:

    
    szTxt=document.TestForm.bt.value;
    szTxt1=document.TestForm.bt.name;
    

    Затем функция выводит в окно браузера строку, ограниченную сверху и снизу разделительной линией, отображающую значение свойств value и name:

    
    document.write("<HR>");
    document.write("You press button: " + szTxt.bold()
      + ", name=" + szTxt1.bold());
    document.write("<HR>");
    

    Переключатель checkbox

    Переключатели checkbox обычно применяются для выбора каких-либо независимых друг от друга параметров или возможностей.

    В форме переключатель checkbox создается с помощью оператора <INPUT> с параметром TYPE, равным строке "checkbox":

    
    <INPUT TYPE="checkbox"
      NAME="Имя_переключателя_checkbox"
      VALUE="Значение"
      CHECKED
      onClick="Обработчик_события">
      Текст, отображаемый рядом с переключателем
    

    Текст, отображаемый рядом с переключателем

    Параметр NAME задает имя переключателя. Это имя можно использовать для определения состояния этого переключателя в сценарии JavaScript.

    С помощью параметра VALUE вы можете определить строку, которая передается расширению сервера при посылке заполненной формы, если переключатель находится во включенном состоянии. Если этот параметр не указан, то по умолчанию посылается строка “on”. Сценарий JavaScript также может получить значение параметра VALUE.

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

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

    Свойства объекта checkbox

    Объект checkbox имеет несколько свойств, отражающих значения соответствующих параметров оператора <INPUT>:

    Свойство

    Описание

    name

    Значение параметра NAME

    checked

    Свойство типа Boolean, отражающее состояние переключателя. Если переключатель включен, свойство имеет значение true, в противном случае - false. С помощью этого свойства сценарий может изменять состояние переключателя

    value

    Значение параметра VALUE

    defaultChecked

    Свойство типа Boolean, отражающее значение параметра CHECKED. Если параметр CHECKED присутствует в определении переключателя, свойство имеет значение true, в противном случае - false. Сценарий может определять или устанавливать значение этого свойства

    Методы объекта checkbox

    Для объекта checkbox определен один метод click, не имеющий параметров:

    
    click()
    

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

    Переключатель radio

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

    Определение переключателя radio выглядит следующим образом:

    
    <INPUT TYPE="radio"
      NAME=" Имя_переключателя_radio"
      VALUE="Значение"
      CHECKED
      onClick="Обработчик_события">
      Текст, отображаемый рядом с переключателем
    

    Назначение параметров NAME, VALUE и CHECKED переключателя radio такое же как и назначение аналогичных параметров переключателя checkbox. Отличие заключается в том, что все переключатели radio, принадлежащие к одной группе, должны иметь одинаковые имена, определенные параметром NAME. Что же касается переключателей checkbox, то если их несколько, то все они должны называться по-разному.

    Для того чтобы расширение сервера Web или сценарий JavaScript, обрабатывающие форму, могли узнать, какой же из переключателей radio группы находится во включенном состоянии, все такие переключатели должны иметь различные значения VALUE.

    Кроме того, только один из переключателей radio может быть определен с параметром CHECKED.

    Свойства объекта radio

    Объект radio имеет следующие свойства:

    Свойство

    Описание

    name

    Значение параметра NAME

    checked

    Свойство типа Boolean, отражающее состояние переключателя. Если переключатель включен, свойство имеет значение true, в противном случае - false. С помощью этого свойства сценарий может изменять состояние переключателя

    length

    Количество переключателей типа radio, определенных в группе с именем, заданным параметром NAME

    value

    Значение параметра VALUE

    defaultChecked

    Свойство типа Boolean, отражающее значение параметра CHECKED. Если параметр CHECKED присутствует в определении переключателя, свойство имеет значение true, в противном случае - false. Сценарий может определять или устанавливать значение этого свойства

    Методы объекта radio

    Для объекта radio определен метод click, не имеющий параметров:

    
    click()
    

    При вызове этого метода переключатель выбирается для работы.

    Пример формы с переключателями

    В этом разделе мы приведем исходный текст документа HTML со сценарием, предназначенным для динамического создания новой страницы при помощи сценария JavaScript. Параметры этой страницы определяются состоянием переключателей типа checkbox и radio, расположенными в этом документе (рис. 3.3).

    Рис. 3.3. Форма с переключателями различных типов

    Наш документ содержит одну форму, в которой есть три переключателя с независимой фиксацией типа checkbox (расположенные в группе Page elements), три переключателя с зависимой фиксацией (образующие группу Text color), и две кнопки - Create Page и Reset.

    Если включен переключатель Show title, создаваемый сценарием JavaScript документ HTML будет снабжен заголовком. При включении переключателя Show horizontal lines информация о состоянии переключателей, отображаемая в документе, будет выделена сверху и снизу горизонтальными разделительными линиями.

    Переключатель Table view влияет на способ отображения информации. Если он включен, информация будет показана в табличном виде, а если выключен - в виде списка (рис. 3.4).

    Рис. 3.4. Отображение состояния переключателей в виде списка

    Внешний вид страницы при включении всех трех переключателей группы Page elements показан на рис. 3.5.

    Рис. 3.5. Отображение состояния переключателей в виде таблицы с заголовком, выделенной линиями

    Переключатели с зависимой фиксацией группы Text color, расположенные в исходном документе HTML, задают цвет строк при отображении информации в виде списка или цвет названий переключателей при табличном отображении.

    В нижней части формы находятся две кнопки с надписями Create Page и Reset. Если нажать на первую из этих кнопок, будет вызвана функция JavaScript, которая определит текущее состояние переключателей и сформирует новый документ HTML. Этот документ появится в окне браузера вместо исходного. С помощью кнопки Reset можно установить начальное состояние переключателей.

    Текст документа HTML со сценарием, выполняющим все описанные выше действия, представлен в листинге 3.2.

    Листинг 3.2. Файл chapter3/checkradio/checkradio.html

    
    <HTML>
      <HEAD>
      <SCRIPT>
        <!--
        function resetRCHBox()
        {
          bTitle=false;
          bHorzLine=false;
          bTable=false;
          szColor="Red";
        }
    
        function chkRadio(form,value)
        {
          szColor = value;
        }
    
        function btnClick(form)
        {
          var szTxt="";
    
          document.write("<HEAD></HEAD><BODY>");
    
          if(bTitle)
            document.write("<H1>Switches Checkbox and Radio</H1>");
    
          if(bHorzLine)
            document.write("<HR>");
    
          if(bTable)
          {
            document.write("<TABLE>");
    
            szTxt=" " + bTitle;
            document.write("<TR><TD><FONT COLOR=" + szColor
    + ">Title:</TD><TD>" + szTxt.bold() + "</TD></TR>");
    
            szTxt=" " + bHorzLine;
            document.write("<TR><TD><FONT COLOR=" + szColor
    + ">HorzLine:</TD><TD>" + szTxt.bold() + "</TD></TR>");
    
            szTxt=" " + bTable;
            document.write("<TR><TD><FONT COLOR=" + szColor
    + ">Table:</TD><TD>" + szTxt.bold() + "</TD></TR>");
    
            document.write("<TR><TD><FONT COLOR=" + szColor
    + ">Color:</TD><TD>" + szColor.bold() + "</TD></TR>");
    
            document.write("</TABLE>");
          }
          else
          {
            document.write("<FONT COLOR=" + szColor + ">");
    
            szTxt=" " + bTitle;
            document.write("<BR>Title: " + szTxt.bold());
    
            szTxt=" " + bHorzLine;
            document.write("<BR>HorzLine: " + szTxt.bold());
    
            szTxt=" " + bTable;
            document.write("<BR>Table: " + szTxt.bold());
    
            document.write("<BR>Color: " + szColor.bold());
    
            document.write("</FONT>");
          }
    
          if(bHorzLine)
            document.write("<HR>");
    
          document.write("</BODY>");
        }
        // -->
      </SCRIPT>
        <TITLE>Переключатели checkbox и radio</TITLE>
      </HEAD>
      <BODY>
      <SCRIPT>
        <!--
        var bTitle=false;
        var bHorzLine=false;
        var bTable=false;
        var szColor="Red";
        // -->
      </SCRIPT>
        <FORM NAME="myform">
         <B>Page elements:</B>
    
         <P><INPUT TYPE="checkbox" NAME="chkTitle"
            onClick="if(this.checked) {bTitle=true;}"> Show title
    
         <BR><INPUT TYPE="checkbox" NAME="HorzLine"
            onClick="if(this.checked) {bHorzLine=true;}"> Show horizontal lines
    
         <BR><INPUT TYPE="checkbox" NAME="Table"
            onClick="if(this.checked) {bTable=true;}"> Table view
    
         <P><B>Text color:</B>
    
         <P><INPUT TYPE="radio" NAME="Color" CHECKED VALUE="Red"
            onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Red
    
         <BR><INPUT TYPE="radio" NAME="Color" VALUE="Green"
            onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Green
    
         <BR><INPUT TYPE="radio" NAME="Color" VALUE="Blue"
            onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Blue
    
         <TABLE>
           <TR><TD><INPUT TYPE="button" NAME="btn" VALUE="Create Page"
           onClick="btnClick(this.form);"></TD>
    
           <TD><INPUT TYPE="reset" NAME="resetBtn" VALUE="Reset"
            onClick="resetRCHBox();"></TD></TR>
         </TABLE>
        </FORM>
      </BODY>
    </HTML>
    

    В области заголовка документа HTML мы определили три функции с именами resetRCHBox, chkRadio и btnClick.

    Первая из этих функций предназначена для инициализации переменных, в которых хранится текущее состояние переключателей:

    
    function resetRCHBox()
    {
      bTitle=false;
      bHorzLine=false;
      bTable=false;
      szColor="Red";
    }
    

    Функция resetRCHBox вызывается в тот момент, когда пользователь нажимает в исходном документе кнопку Reset. Она устанавливает значения четырех переменных.

    Переменные bTitle, bHorzLine и bTable отражают состояние, соответственно, переключателей с независимой фиксацией Show title, Show horizontal lines и Table view. Если переключатель включен, в соответствующей переменной хранится значение true, если выключен - false.

    В переменной szColor находится цвет текста, выбранный с помощью группы переключателей с зависимой фиксацией Text color.

    Начальное состояние переменных, задаваемое функцией resetRCHBox, соответствует начальному состоянию переключателей сразу после загрузки исходного документа HTML в окно браузера.

    Для того чтобы обеспечить вызов функции resetRCHBox при нажатии на кнопку Reset, в определении этой кнопки задан обработчик события onClick, как это показано ниже:

    
    <INPUT TYPE="reset" NAME="resetBtn" VALUE="Reset"
            onClick="resetRCHBox();">
    

    Обработка события заключается в простом вызове функции.

    Теперь мы займемся переключателями с зависимой фиксацией.

    Эти переключатели определены в форме следующим образом:

    
    <INPUT TYPE="radio" NAME="Color" CHECKED VALUE="Red"
      onClick="if(this.checked)
        {chkRadio(this.form,this.value);}"> Red
    
    <BR><INPUT TYPE="radio" NAME="Color" VALUE="Green"
      onClick="if(this.checked)
        {chkRadio(this.form,this.value);}"> Green
    
    <BR><INPUT TYPE="radio" NAME="Color" VALUE="Blue"
      onClick="if(this.checked)
        {chkRadio(this.form,this.value);}"> Blue
    

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

    
    if(this.checked)
    {
      chkRadio(this.form,this.value);
    }
    

    Прежде всего обработчик проверяет состояние переключателя, вызывая для этого метод checked. Ссылка на объект, для которого вызывается этот метод (то есть на переключатель), выполняется с помощью ключевого слова this.

    В том случае, когда переключатель включен, обработчик вызывает функцию chkRadio, определенную в заголовке документа следующим образом:

    
    function chkRadio(form,value)
    {
      szColor = value;
    }
    

    Хотя мы передаем функции два параметра (ссылку на форму, содержащую переключатель, и значение параметра VALUE текущего переключателя), используется только второй параметр. Значение этого параметра, определяющее цвет текста, сохраняется в переменной szColor.

    Работа с переключателями типа checkbox выполняется немного проще:

    
    <INPUT TYPE="checkbox" NAME="chkTitle"
      onClick="if(this.checked) {bTitle=true;}"> Show title
    
    <BR><INPUT TYPE="checkbox" NAME="HorzLine"
      onClick="if(this.checked) {bHorzLine=true;}"> Show horizontal lines
    
     <BR><INPUT TYPE="checkbox" NAME="Table"
      onClick="if(this.checked) {bTable=true;}"> Table view
    

    Когда пользователь включает переключатель, обработчик события onClick устанавливает в состояние true соответствующую переменную. Например, при изменении состояния переключателя Show title выполняются следующие действия:

    
    if(this.checked)
    {
      bTitle=true;
    }
    

    Последний орган управления, который мы рассмотрим, это кнопка Create Page. Эта кнопка запускает процесс создания нового документа HTML. Для нее также определен обработчик события onClick:

    
    <INPUT TYPE="button" NAME="btn" VALUE="Create Page"
      onClick="btnClick(this.form);">
    

    Этот обработчик вызывает функцию btnClick, передавая ей в качестве параметра ссылку на форму.

    Функция btnClick определена в области заголовка документа HTML, исходный текст которого представлен в листинге 3.2.

    Внутри этой функции мы определили рабочую текстовую переменную szTxt, присвоив ей значение пустой строки:

    
    var szTxt="";
    

    Когда функция btnClick получает управление, прежде всего она формирует пустую область заголовка документа HTML:

    
    document.write("<HEAD></HEAD><BODY>");
    

    Далее функция анализирует содержимое переменной bTitle:

    
    if(bTitle)
      document.write("<H1>Switches Checkbox and Radio</H1>");
    

    Если перед тем как нажать кнопку Create Page пользователь включил переключатель Show title, в переменной bTitle будет находиться значение true. В этом случае сценарий снабдит формируемый документ заголовком “Switches Checkbox and Radio”, оформив его стилем <H1>.

    Аналогичным образом анализируется содержимое переменной bHorzLine:

    
    if(bHorzLine)
      document.write("<HR>");
    

    Эта переменная отражает состояние переключателя Show horizontal lines.

    Далее функция btnClick анализирует содержимое переменной bTable, отвечающей за способ отображения информации о состоянии переключателей в создаваемом документе HTML:

    
    if(bTable)
    {
      . . .
    }
    else
    {
      . . .
    }
    

    Если переключатель Table view находится во включенном состоянии, информация отображается в виде таблицы.

    При формировании первого столбца таблицы цвет текста внутри ячейки устанавливается в соответствии с содержимым переменной szColor:

    
    szTxt=" " + bTitle;
    document.write("<TR><TD><FONT COLOR=" + szColor
      + ">Title:</TD><TD>" + szTxt.bold() + "</TD></TR>");
    

    Напомним, что эта переменная хранит значение цвета текста, установленное группой переключателей с зависимой фиксацией Text color.

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

    
    document.write("<FONT COLOR=" + szColor + ">");
    

    Список select

    С помощью оператора <SELECT> вы можете разместить внутри формы список, допускающий выбор одной или просмотр нескольких строк. Формат оператора <SELECT> мы привели ниже:

    
    <SELECT NAME="Имя_списка_select"
       SIZE="Размер_списка"
       MULTIPLE
       onBlur="Обработчик_события"
       onChange="Обработчик_события"
       onFocus="Обработчик_события">
       <OPTION VALUE="Значение" SELECTED> Текст
       ...
       <OPTION> Текст
    </SELECT>
    

    Все параметры оператора <SELECT> необязательные, однако для того чтобы сценарий JavaScript мог работать со списком, необходимо указать по крайней мере параметр NAME, определяющий имя списка.

    Параметр SIZE задает размер видимой части списка в строках.

    Если указан необязательный параметр MULTIPLE, объект select является списком просмотра, а не списком выбора.

    Список может быть создан пустым и впоследствии заполнен сценарием JavaScript, или уже содержащим один или несколько элементов. Для определения элементов списка предназначен оператор <OPTION>.

    Оператор <OPTION> может иметь два параметра - VALUE и SELECTED.

    Параметр VALUE определяет значение, которое передается расширению сервера Web. С помощью параметра SELECTED отмечается строка списка, выделенная по умолчанию при начальном отображении формы.

    После оператора <OPTION> следует текст, отображаемый в строках списка.

    Свойства объекта select

    Ниже мы перечислили свойства объекта select, доступные сценарию JavaScript:

    Свойство

    Описание

    length

    Количество элементов (строк) в списке

    name

    Значение параметра NAME

    options

    Массив объектов options, соответствующих элементам массива, заданным при помощи оператора <OPTION>

    selectedIndex

    Номер выбранного элемента или первого элемента среди нескольких выбранных (если указан параметр MULTIPLE и пользователь выбрал в списке несколько элементов)

    Одним из свойств списка select является массив options. В этом массиве хранятся элементы списка, определенные оператором <OPTION>. Каждый элемент такого массива есть ни что иное как объект со следующим набором свойств:

    Свойство

    Описание

    defaultSelected

    Отражает состояние параметра SELECTED

    index

    Порядковый номер (индекс) элемента списка

    length

    Количество элементов в выбранном объекте

    name

    Значение параметра NAME

    selected

    С помощью свойства selected сценарий JavaScript может выбрать данный элемент

    selectedIndex

    Номер выбранного элемента

    text

    Текст, указанный после оператора <OPTION>

    value

    Значение параметра VALUE

    Методы объекта select

    Для объекта select определено два метода, не имеющих параметров, - focus и blur. Первый из этих методов позволяет передать списку фокус ввода, а второй - отобрать этот фокус у списка.

    Обработчики событий, связанные с объектом select

    Как видно из формата оператора <SELECT>, рассмотренного выше, для списка вы можете определить три обработчика события: onFocus, onBlur и onChange.

    События onFocus и onBlur возникают, когда список получает и теряет фокус ввода, соответственно. Что же касается события onChange, то оно создается, когда пользователь изменяет состояние списка, то есть выбирает в нем другой элемент.

    Примеры сценариев, работающих со списками

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

    Работа с готовым списком

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

    Рис. 3.6. Форма с двумя списками различного размера

    Первый из этих списков предназначен для выбора цвета, а второй - размера некоторого предмета (в данном случае не имеет значение, какого именно).

    Списки отличаются количеством элементов, видимых одновременно. Для того чтобы увидеть все элементы списка размеров вы можете воспользоваться полосой просмотра, расположенной в правой части этого списка. Что же касается первого списка, то для того чтобы выбрать из него нужный цвет, необходимо сделать щелчок по кнопке с треугольником. При этом список раскроется (рис. 3.7).

    Рис. 3.7. Форма с раскрытым списком

    Помимо списков, в форме есть две кнопки с надписями Complete и Reset. Кнопка Reset возвращает списки в исходное состояние, в котором они находились сразу после отображения документа HTML. Если же сделать щелчок по кнопке Complete, управление получит сценарий JavaScript, который отобразит выбранный цвет и размер на экране в виде диалоговой панели с сообщением (рис. 3.8).

    Рис. 3.8. Выбранный цвет и размер

    Исходный текст документа HTML, содержащего форму и сценарий для ее обработки, представлен в листинге 3.3.

    Листинг 3.3. Файл chapter3/select/select.html

    
    <HTML>
      <HEAD>
        <TITLE>Работа со списками</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
        function Complete()
        {
          var szElement="";
          szElement="Цвет: " +
            Sel.ColorList.options[Sel.ColorList.selectedIndex].value +
            " (" + Sel.ColorList.selectedIndex + ")" +
            "\nРазмер: " +
            Sel.SizeList.options[Sel.SizeList.selectedIndex].value +
            " (" + Sel.SizeList.selectedIndex + ")";
    
          alert(szElement);
        }
        // -->
        </SCRIPT>
      </HEAD>
      <BODY BGCOLOR=white>
        <H1>Работа со списками</H1>
    
        <FORM NAME="Sel">
          <P>Выберите цвет:<P>
          <SELECT NAME="ColorList">
            <OPTION VALUE=Черный SELECTED> Черный
            <OPTION VALUE=Белый> Белый
            <OPTION VALUE=Красный> Красный
            <OPTION VALUE=Оранжевый> Оранжевый
            <OPTION VALUE=Желтый> Желтый
            <OPTION VALUE=Зеленый> Зеленый
            <OPTION VALUE=Голубой> Голубой
            <OPTION VALUE=Синий> Синий
            <OPTION VALUE=Фиолетовый> Фиолетовый
          </SELECT>
    
          <P>Выберите размер:<P>
          <SELECT NAME="SizeList" SIZE=3>
            <OPTION VALUE=Стандартный SELECTED> Стандартный
            <OPTION VALUE=Большой> Большой
            <OPTION VALUE=Средний> Средний
            <OPTION VALUE=Маленький> Маленький
            <OPTION VALUE=Очень_маленький> Очень маленький
          </SELECT>
          <P>
          <TABLE>
            <TR><TD><INPUT TYPE="button" VALUE="Complete"
            onClick="Complete();"></TD>
    
            <TD><INPUT TYPE="reset" VALUE="Reset"></TD></TR>
          </TABLE>
        </FORM>
      </BODY>
    </HTML>
    

    Как видно из этого листинга, в документе определена форма с именем Sel, содержащая списки ColorList и SizeList. Во втором списке мы задали размер видимой части, указав в операторе <SELECT> параметр SIZE со значением, равным трем. В результате, как это видно из рис. 3.6, в видимой части списка отображается три элемента.

    Для кнопки с надписью Complete мы определили обработчик события onClick. Когда пользователь делает щелчок мышью по этой кнопке, вызывается функция Complete, которая определяет выбранные элементы для обоих списков и отображает их значения в диалоговой панели при помощи функции alert:

    
    szElement="Цвет: " +
    Sel.ColorList.options[Sel.ColorList.selectedIndex].value +
      " (" + Sel.ColorList.selectedIndex + ")" +
      "\nРазмер: " +
    Sel.SizeList.options[Sel.SizeList.selectedIndex].value +
      " (" + Sel.SizeList.selectedIndex + ")";
    alert(szElement);
    

    При этом прежде всего определяются номера элементов, выбранных из списков. Эти номера равны, соответственно, Sel.ColorList.selectedIndex (для списка цветов) и Sel.SizeList.selectedIndex (для списка размеров).

    Далее эти номера используются как индексы в массивах options соответствующих списков, содержащих свойства элементов списков. Нас интересует свойство value, при помощи которого можно определить значение для выбранных элементов списков. Каждый элемент наших списков имеет такое значение, заданное при помощи параметра VALUE оператора <OPTION>.

    Динамическое заполнение списка

    Сценарий JavaScript может изменять список, добавляя в него новые элементы. Пример такого сценария мы уже приводили в разделе второй главы с названием “Ссылки и метки в документе”. Мы предлагаем вам обратиться к этому разделу еще раз.

    Напомним, что там мы создали в документе HTML пустой список с именем ListOfLinks, расположив его в форме Sel:

    
    <FORM NAME="Sel">
          <SELECT NAME="ListOfLinks">
          </SELECT>
          <INPUT TYPE="button" VALUE="Jump!"
             onClick="urlJump();">
    </FORM>
    

    Перед заполнением этого списка мы создали массив элементов elem:

    
    elem = new Array();
    

    Заполнение этого массива выполнялось в цикле, причем для создания элемента массива мы вызывали конструктор Option, задавая для него четыре параметра:

    
    elem[i] = new Option("Elem" + i, i, false, false);
    

    Первый параметр задает текст, который отображается в списке, второй - значение, назначаемое элементу списка при помощи параметра VALUE. Третий и четвертый параметры соответствуют свойствам, соответственно, defaultSelected и selected.

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

    После того как элемент списка создан как объект класса option, его нужно записать в соответствующий элемент массива options:

    
    Sel.ListOfLinks.options[i] = elem[i];
    Sel.ListOfLinks.options[i].text = document.links[i];
    

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

    Определение выбранного из нашего списка элемента выполняется аналогично тому, как это сделано в предыдущем примере:

    
    function urlJump()
    {
      var szNewURL="";
      szNewURL =
        document.links[Sel.ListOfLinks.selectedIndex];
      window.location.href=szNewURL;
    }
    

    Вначале мы определяем номер выбранного элемента как значение Sel.ListOfLinks.selectedIndex. Затем это значение используется как индекс в массиве document.links, из которого в цикле происходило заполнение нашего списка.

    Однострочное поле text

    Наверное, наиболее часто в формах, размещенных на страницах серверов Web встречаются однострочные поля, предназначенные для ввода и редактирования текста. Для того чтобы встроить такое поле в форму, необходимо использовать оператор <INPUT> с параметром TYPE, равным значению “text”:

    
    <INPUT TYPE="text"
      NAME="Имя_поля_text"
      VALUE="Значение"
      SIZE=Размер_поля
      onBlur="Обработчик_события"
      onChange="Обработчик_события"
      onFocus="Обработчик_события"
      onSelect="Обработчик_события">
    

    Дополнительно можно указать параметры NAME, VALUE и SIZE, а также четыре обработчика событий, создаваемых текстовым полем.

    Параметр NAME позволяет задать имя поля, необходимое для обращения к свойствам объекта text, соответствующего этому полю.

    С помощью параметра VALUE можно записать в поле произвольную текстовую строку. Эта строка будет отображаться сразу после загрузки документа HTML в окно браузера.

    Параметр SIZE определяет размер (ширину) текстового поля в символах. Это размер видимой части поля. Он не ограничивает длину строки, которую можно ввести в данном поле.

    Свойства объекта text

    Сценариям JavaScript доступны три свойства поля редактирования как объекта класса text:

    Свойство

    Описание

    defaultValue

    Отражает состояние параметра VALUE

    name

    Значение параметра NAME

    value

    Текущее содержимое поля редактирования

    Сразу после отображения поля редактирования свойства defaultValue и value хранят одинаковые строки. Когда пользователь редактирует текст, все изменения отражаются в свойстве value.

    Заметим, что изменяя содержимое свойства value, сценарий JavaScript может изменить содержимое поля редактирования.

    Методы объекта text

    Для объекта text определены методы focus, blur и select, не имеющие параметров.

    С помощью метода focus сценарий JavaScript может передать фокус полю редактирования, а с помощью метода blur - отобрать фокус у этого поля.

    Вызов метода select приводит к выделению содержимого поля редактирования.

    Обработчики событий объекта text

    Обработчики событий вызываются в следующих случаях:

    Обработчик

    Когда вызывается

    onFocus

    Вызывается, когда поле редактирования получает фокус ввода

    onBlur

    Вызывается, когда поле редактирования теряет фокус ввода

    onChange

    При изменении содержимого поля редактирования

    onSelect

    При выделении содержимого поля редактирования

    Проверка анкеты

    Методику работы с текстовыми полями в сценариях JavaScript мы рассмотрим на примере документа HTML с формой для ввода анкеты, показанной на рис. 3.9.

    Рис. 3.9. Форма для ввода анкеты

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

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

    Рис. 3.10. Отображение содержимого полей анкеты

    Кнопка Reset устанавливает поля в исходное состояние.

    Исходный текст документа HTML с описанной выше формой и сценарием JavaScript вы найдете в листинге 3.4.

    Листинг 3.4. Файл chapter3/text/text.html

    
    <HTML>
      <HEAD>
        <TITLE>Работа с текстовыми полями</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
        function Complete()
        {
          var szElement="";
          szElement="Фамилия: " + Sel.family.value +
            "\nИмя: " + Sel.Name.value +
            "\nТелефон: " + Sel.PhoneNumber.value +
            "\nВозраст: " + Sel.Age.value;
    
          alert(szElement);
        }
        function CheckAge(age)
        {
          if(age < 18)
            return "0";
          else
            return age;
        }
        // -->
        </SCRIPT>
      </HEAD>
      <BODY BGCOLOR=white>
        <H1>Заполните анкету</H1>
    
        <FORM NAME="Sel">
          <TABLE>
           <TR><TD><B>Фамилия:</B></TD><TD><INPUT TYPE="text"
            NAME="family"
            onChange="this.value=this.value.toUpperCase()"
            SIZE="20" ></TD></TR>
    
          <TR><TD><B>Имя:</B></TD><TD><INPUT TYPE="text"
            NAME="Name" SIZE="20"></TD></TR>
    
          <TR><TD><B>Телефон:</B></TD><TD><INPUT TYPE="text"
            NAME="PhoneNumber" SIZE="10"></TD></TR>
    
          <TR><TD><B>Возраст:</B></TD><TD><INPUT TYPE="text"
            NAME="Age" VALUE="18" SIZE="4"
            onChange="this.value=CheckAge(this.value)"
            onFocus="this.select()"
            ></TD></TR>
          <P>
          <TABLE>
            <TR><TD><INPUT TYPE="button" VALUE="Complete"
            onClick="Complete();"></TD>
    
            <TD><INPUT TYPE="reset" VALUE="Reset"></TD></TR>
          </TABLE>
        </FORM>
      </BODY>
    </HTML>
    

    Для того чтобы преобразовать символы фамилии в прописные, для поля family мы определили обработчик события onChange:

    
    onChange="this.value=this.value.toUpperCase()"
    

    После внесения изменений в содержимое поля этот обработчик вызывает метод toUpperCase, определенный в классе строк. Преобразованное значение записывается снова в свойство this.value.

    Поле Age имеет два обработчика для событий onChange и onFocus:

    
    onChange="this.value=CheckAge(this.value)"
    onFocus="this.select()"
    

    Первый из этих обработчиков вызывает функцию проверки возраста CheckAge, передавая ей значение из поля Age. Возвращенное этой функцией значение снова записывается в то же самое поле.

    Функция CheckAge выглядит очень просто:

    
    function CheckAge(age)
    {
      if(age < 18)
        return "0";
      else
        return age;
    }
    

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

    Обработчик события onFocus вызывает метод select, выделяющий содержимое поля редактирования. Действие этого обработчика вы можете увидеть, нажимая клавишу табуляции до тех пор, пока фокус ввода не будет передан полю Age.

    Кнопку Complete пользователь нажимает после заполнения анкеты. Для нее мы определили обработчик события onClick:

    
    <INPUT TYPE="button" VALUE="Complete"
       onClick="Complete();">
    

    Этот обработчик вызывает функцию с именем Complete, в задачу которой входит отображение содержимого полей формы. Исходный текст функции Complete мы привели ниже:

    
    function Complete()
    {
      var szElement="";
      szElement="Фамилия: " + Sel.family.value +
        "\nИмя: " + Sel.Name.value +
        "\nТелефон: " + Sel.PhoneNumber.value +
        "\nВозраст: " + Sel.Age.value;
    
      alert(szElement);
    }
    

    Обратите внимание на то, как мы адресуемся к свойствам полей формы, указывая имя формы, имена полей и имя свойства value.

    Многострочное поле textarea

    В тех случаях, когда редактируемый текст должен занимать несколько строк, в форме между операторами <TEXTAREA> и </TEXTAREA> располагают многострочное текстовое поле:

    
    <TEXTAREA
      NAME="Имя_поля_textarea"
      ROWS="Количество_строк"
      COLS="Количество_столбцов"
      WRAP="Режим_свертки_текста"
      onBlur="Обработчик_события"
      onChange="Обработчик_события"
      onFocus="Обработчик_события"
      onSelect="Обработчик_события">
      . . .
      Отображаемый текст
      . . .
    </TEXTAREA>
    

    Здесь с помощью параметра NAME вы должны указать имя поля. Оно нужно для того чтобы сценарий JavaScript мог обращаться к свойствам и методам этого поля.

    Параметры ROWS и COLS определяют видимый размер многострочного поля редактирования, задавая, соответственно, количество строк и столбцов (количество символов, которые могут поместиться в одной строке).

    Параметр WRAP задает способ свертки текста и может иметь одно из трех следующих значений:

    Значение

    Способ свертки текста

    off

    Свертка выключена, строки отображаются так, как вводятся

    virtual

    Строки сворачиваются только при отображении в окне редактирования, но передаются расширению сервера Web и сценарию JavaScript точно в таком виде, в котором вводятся

    physical

    При свертке в передаваемый текст записываются символы новой строки

    Методы объекта textarea

    Для объекта textarea определены такие же методы, что и для объекта text. Это методы focus, blur и select, не имеющие параметров.

    С помощью метода focus сценарий JavaScript может передать фокус полю редактирования, а с помощью метода blur - отобрать фокус у этого поля.

    Вызов метода select приводит к выделению содержимого многострочного поля редактирования.

    Обработчики событий объекта textarea

    Обработчики событий вызываются в следующих случаях:

    Обработчик

    Когда вызывается

    onFocus

    Вызывается, когда поле редактирования получает фокус ввода

    onBlur

    Вызывается, когда поле редактирования теряет фокус ввода

    onChange

    При изменении содержимого поля редактирования

    onSelect

    При выделении содержимого поля редактирования

    Пример сценария, заполняющего поле textarea

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

    Например, пусть нам нужно создать форму, предназначенную для отправления через Internet отзыва о работе некоторой программы (рис. 3.11).

    Рис. 3.11. Форма для отправки отзыва о работе программы

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

    Для отправки отзыва нажмите кнопку Complete. В результате на экране появится диалоговая панель с текстом положительного отзыва (рис. 3.12).

    Рис. 3.12. Диалоговая панель с текстом положительного отзыва

    Для того чтобы сообщить изготовителю программы об ошибках, включите переключатель “Проблемы”. Сразу после этого сценарий запишет в многострочное поле текст соответствующего сообщения. Этот текст надо будет отредактировать и дополнить, описав, например, внешние проявления обнаруженной ошибки (рис. 3.13).

    Рис. 3.13. Отправка сообщения об ошибке

    Нажав кнопку Complete, вы увидите текст сообщения (рис. 3.14).

    Рис. 3.14. Полный текст сообщения об ошибке, обнаруженной в программе

    Исходный текст документа HTML, в котором определена форма для отправки отзыва, а также обрабатывающий эту форму сценарий JavaScript, представлен в листинге 3.5.

    Листинг 3.5. Файл chapter3/textarea/textarea.html

    
    <HTML>
      <HEAD>
        <TITLE>Работа с многострочным текстовым полем</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
        var szOK = "Спасибо!\nВаша программа работает без ошибок.";
        var szTrouble = "К сожалению, с вашей программой у нас возникли проблемы.";
        var szProbList = "\nСерийный номер программы: ХХХХХХ\nДата покупки: ХХ-ХХ-ХХ";
    
        function getDate()
        {
          var szDate = "";
          szDate = new Date();
          return szDate.toLocaleString() + "\n";
        }
    
        function chkRadio(form,value)
        {
          if(value == "Thanks")
            Sel.Comment.value = getDate() + szOK;
          else
            Sel.Comment.value = getDate() + szTrouble + szProbList;
        }
        function init()
        {
            Sel.Comment.value = getDate() + szOK;
        }
    
        function Complete()
        {
          szMsg = Sel.Comment.value;
          alert(szMsg);
        }
        // -->
        </SCRIPT>
      </HEAD>
      <BODY BGCOLOR=white>
        <H1>Пришлите ваш отзыв</H1>
    
        <FORM NAME="Sel">
    
          <P><INPUT TYPE="radio" NAME="TextSelect" CHECKED VALUE="Thanks"
            onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Благодарность
    
          <BR><INPUT TYPE="radio" NAME="TextSelect" VALUE="Trouble"
            onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Проблемы
    
          <P><TEXTAREA NAME="Comment"
            ROWS="5"   COLS="25" WRAP="physical">
          </TEXTAREA>
    
          <P><INPUT TYPE="button" VALUE="Complete" onClick="Complete();">
    
        </FORM>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
          init();
        // -->
        </SCRIPT>
      </BODY>
    </HTML>
    

    Обратите внимание, что в нашей форме многострочное поле редактирования текста textarea создано пустым:

    
    <TEXTAREA NAME="Comment"
      ROWS="5" COLS="25" WRAP="physical">
    </TEXTAREA>
    

    Это поле называется “Comment”, имеет пять строк, в которых размещается до 25 символов, и режим свертки текста physical, предполагающий добавление символов новой строки, разделяющих отдельные строки введенного текста.

    Как происходит заполнение этого поля?

    В самом конце области тела документа HTML, ограниченного операторами <BODY> и </BODY>, мы вставили вызов функции init:

    
    <SCRIPT LANGUAGE="JavaScript">
    <!--
      init();
    // -->
    </SCRIPT>
    

    Эта функция вызывается после завершения загрузки тела документа и выполняет начальное заполнение многострочного поля редактирования textarea:

    
    Sel.Comment.value = getDate() + szOK;
    

    Строка, которая записывается в это поле, образуется путем сложения строки текущей даты, полученной от функции getDate (эту функцию мы определили в нашем сценарии), со строкой szOK, содержащей текст положительного отзыва.

    Функция getDate определена следующим образом:

    
    function getDate()
    {
      var szDate = "";
      szDate = new Date();
      return szDate.toLocaleString() + "\n";
    }
    

    В этой функции мы сначала создаем объект класса Data, а затем вызываем для этого объекта метод toLocaleString, возвращающий текстовую строку даты.

    В верхней части формы находятся два переключателя с зависимой фиксацией. С помощью этих переключателей пользователь может выбрать вид отзыва (благодарность или сообщение об ошибке):

    
    <INPUT TYPE="radio" NAME="TextSelect" CHECKED VALUE="Thanks"
       onClick="if(this.checked)
         {chkRadio(this.form,this.value);}"> Благодарность
    
    <BR><INPUT TYPE="radio" NAME="TextSelect" VALUE="Trouble"
       onClick="if(this.checked)
         {chkRadio(this.form,this.value);}"> Проблемы
    

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

    Каждый из переключателей имеет обработчик события onClick, определенный следующим образом:

    
    if(this.checked)
    {
      chkRadio(this.form,this.value);
    }
    

    При изменении состояния переключателя вызывается функция chkRadio. Эта функция проверяет имя включенного переключателя и заполняет поле многострочного редактора textarea соответствующим сообщением:

    
    function chkRadio(form,value)
    {
      if(value == "Thanks")
        Sel.Comment.value = getDate() + szOK;
      else
        Sel.Comment.value = getDate() + szTrouble + szProbList;
    }
    

    Функция chkRadio комбинирует тексты сообщений из текущей даты и заранее проинициализированных строк szOK, szTrouble и szProbList.

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

    Для кнопки Complete мы определили обработчик события onClick:

    
    <INPUT TYPE="button" VALUE="Complete" onClick="Complete();">
    

    Этот обработчик вызывает функцию Complete, отображающую подготовленный отзыв на экране:

    
    function Complete()
    {
      szMsg = Sel.Comment.value;
      alert(szMsg);
    }
    

    Здесь текст извлекается из поля textarea как значение свойства value для соответствующего объекта, а затем отображается на экране при помощи встроенной функции alert.

    Однострочное поле password

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

    
    <INPUT TYPE="password"
      NAME="Имя_поля_text"
      VALUE="Значение"
      SIZE=Размер_поля>
    

    Для поля password можно указать параметры NAME, VALUE и SIZE. Заметим, что это поле не может иметь обработчики событий.

    Параметр NAME позволяет задать имя поля, которое необходимо для обращения к свойствам объекта password, соответствующего этому полю.

    С помощью параметра VALUE можно записать в поле произвольную текстовую строку.

    Параметр SIZE определяет размер (ширину) текстового поля в символах. Это размер видимой части поля. Он не ограничивает длину строки, которую можно ввести в данном поле.

    Поле password похоже на поле text, рассмотренное нами ранее. Главное отличие заключается в том, что символы введенного в этом поле текста заменяются на звездочки.

    Свойства объекта password

    Сценариям JavaScript доступны три свойства поля редактирования password:

    Свойство

    Описание

    defaultValue

    Отражает состояние параметра VALUE

    name

    Значение параметра NAME

    value

    Текущее содержимое поля редактирования

    Так же, как и для поля text, сразу после отображения поля редактирования свойства defaultValue и value хранят одинаковые строки. Когда пользователь редактирует текст, все изменения отражаются в свойстве value.

    Изменяя содержимое свойства value, сценарий может изменить содержимое поля редактирования типа password.

    Методы объекта password

    Для объекта password определены методы focus, blur и select, не имеющие параметров.

    С помощью метода focus сценарий JavaScript может передать фокус полю редактирования, а с помощью метода blur - отобрать фокус у этого поля. Вызов метода select приводит к выделению содержимого поля редактирования.

    Ввод идентификатора и пароля

    В качестве практического примера применения сценария JavaScript для обработки информации из полей text и password приведем документ HTML, предназначенный для регистрации пользователей.

    В форме регистрации новый пользователь должен ввести свой идентификатор, а также задать пароль (3.15).

    Рис. 3.15. Форма для регистрации нового пользователя

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

    Наш сценарий, обрабатывающий данные из формы, показанной на рис. 3.15, решает две задачи.

    Во-первых, он преобразует символы идентификатора пользователя в прописные. Во-вторых, сценарий проверяет идентичность паролей, введенных в полях “Пароль” и “Проверка пароля”.

    Если введенные пароли идентичны, то после нажатия на кнопку Complete пользователь увидит на экране диалоговую панель с введенным идентификатором и паролем (рис. 3.16).

    Рис. 3.16. Диалоговая панель с идентификатором и паролем нового пользователя

    Если пароли не совпадают, сценарий предлагает пользователю повторить ввод паролей (рис. 3.17).

    Рис. 3.17. Приглашение для повторного ввода пароля

    Исходный текст нашего документа HTML, регистрирующего новых пользователей, показан в листинге 3.6.

    Листинг 3.6. Файл chapter3/password/password.html

    
    <HTML>
      <HEAD>
        <TITLE>Ввод и проверка пароля</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
        function Complete()
        {
          if(Sel.pwd.value != Sel.pwd1.value)
            alert("Ошибка при вводе пароля\nПопробуйте еще раз");
          else
          {
            var szId="";
            var szPwd="";
            szId = Sel.id.value;
            szPwd = Sel.pwd.value;
            alert("Регистрация выполнена:\n" + "ID=" +
              szId + "\nPassword=" + szPwd);
          }
        }
        // -->
        </SCRIPT>
      </HEAD>
      <BODY BGCOLOR=white>
        <H1>Регистрация</H1>
    
        <FORM NAME="Sel">
          <TABLE>
           <TR><TD><B>Идентификатор:</B></TD><TD><INPUT TYPE="text"
            NAME="id" onChange="this.value=this.value.toUpperCase()"
            SIZE="20" ></TD></TR>
    
          <TR><TD><B>Пароль:</B></TD><TD><INPUT TYPE="password"
            NAME="pwd" SIZE="20"></TD></TR>
    
          <TR><TD><B>Проверка пароля:</B></TD><TD><INPUT TYPE="password"
            NAME="pwd1" SIZE="20"></TD></TR>
          </TABLE>
          <P>
          <TABLE>
            <TR><TD><INPUT TYPE="button" VALUE="Complete"
            onClick="Complete();"></TD>
    
            <TD><INPUT TYPE="reset" VALUE="Reset"></TD></TR>
          </TABLE>
        </FORM>
      </BODY>
    </HTML>
    

    Преобразование символов идентификатора пользователя выполняет обработчик события onChange, определенный для поля id типа text:

    
    <INPUT TYPE="text" NAME="id"
      onChange="this.value=this.value.toUpperCase()" SIZE="20">
    

    Это преобразование выполняет функция toUpperCase, которой мы уже пользовались раньше.

    Что же касается проверки пароля, то этим занимается функция Complete, определенная в качестве обработчика события onClick для одноименной кнопки, предназначенной для посылки заполненной формы.

    Вот исходный текст этой функции:

    
    function Complete()
    {
      if(Sel.pwd.value != Sel.pwd1.value)
        alert("Ошибка при вводе пароля\nПопробуйте еще раз");
      else
      {
        var szId="";
        var szPwd="";
        szId = Sel.id.value;
        szPwd = Sel.pwd.value;
        alert("Регистрация выполнена:\n" + "ID=" +
          szId + "\nPassword=" + szPwd);
      }
    }
    

    Если пользователь ввел разные пароли, значения свойств Sel.pwd.value и Sel.pwd1.value не совпадают. В этом случае функция Complete отображает диалоговую панель с сообщением об ошибке.

    При совпадении паролей функция Complete извлекает значения идентификатора пользователя Sel.id.value и его пароля Sel.pwd.value, а затем отображает их на экране.

    Проверка заполнения формы

    Для того чтобы сократить количество обращений к расширению сервера Web можно с помощью сценария JavaScript выполнить локальную проверку правильности заполнения формы. О том как проверить содержимое отдельных полей формы вы уже знаете из предыдущих разделов этой главы. Сейчас же мы остановимся на взаимодействии формы, сценария JavaScript и расширения сервера Web, такого как программа CGI или приложение ISAPI.

    Если вы ранее занимались созданием расширений сервера Web, обрабатывающих данные, полученные от форм, то знаете, что для посылки данных расширению используется кнопка типа submit. Эта кнопка встраивается в форму следующим образом:

    
    <INPUT TYPE="submit"
      NAME="Имя_кнопки_submit"
      VALUE="Текст_на_поверхности_кнопки"
      onClick="Обработчик_события">
    

    Параметры NAME и VALUE здесь имеют такое же назначение, что и для изученной нами ранее кнопки типа button.

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

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

    Простейший выход из этой ситуации - замена кнопки типа submit на обычную кнопку button. С этой кнопкой должен быть связан сценарий JavaScript, который проверяет данные, введенные пользователем в поля формы, и посылающий их серверу Web явным образом.

    В этом разделе мы приведем исходные тексты документа HTML, предназначенного для ввода идентификатора пользователя и пароля (листинг 3.7). Это документ аналогичен приведенному в предыдущем разделе. В нашем примере с помощью сценария JavaScript мы не только проверяем пароль, но и отправляем данные расширению сервера Web, если пароль введен правильно.

    Листинг 3.7. Файл chapter3/validation/validation.html

    
    <HTML>
      <HEAD>
        <TITLE>Ввод и проверка пароля</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
        function Complete()
        {
          if(Sel.pwd.value != Sel.pwd1.value)
            alert("Ошибка при вводе пароля\nПопробуйте еще раз");
          else
          {
            var szId="";
            var szPwd="";
            szId = Sel.id.value;
            szPwd = Sel.pwd.value;
            alert("Регистрация выполнена:\n" + "ID=" +
              szId + "\nPassword=" + szPwd);
    
            document.forms[0].submit();
          }
        }
        // -->
        </SCRIPT>
      </HEAD>
      <BODY BGCOLOR=white>
        <H1>Регистрация</H1>
    
        <FORM METHOD=POST NAME="Sel" ACTION="http://frolov/scripts/controls.exe">
          <TABLE>
           <TR><TD><B>Идентификатор:</B></TD><TD><INPUT TYPE="text"
            NAME="id" onChange="this.value=this.value.toUpperCase()"
            SIZE="20" ></TD></TR>
    
          <TR><TD><B>Пароль:</B></TD><TD><INPUT TYPE="password"
            NAME="pwd" SIZE="20"></TD></TR>
    
          <TR><TD><B>Проверка пароля:</B></TD><TD><INPUT TYPE="password"
            NAME="pwd1" SIZE="20"></TD></TR>
          </TABLE>
          <P>
          <TABLE>
            <TR><TD><INPUT TYPE="button" VALUE="Complete"
            onClick="Complete();"></TD>
    
            <TD><INPUT TYPE="reset" VALUE="Reset"></TD></TR>
          </TABLE>
        </FORM>
      </BODY>
    </HTML>
    

    При определении формы для оператора <FROM> мы указали параметр ACTION:

    
    <FORM METHOD=POST NAME="Sel" ACTION="http://frolov/scripts/controls.exe">
    

    Этот параметр задает путь к загрузочному файлу программы CGI, расположенному в каталоге сценариев сервера Web. Отметим, что такой каталог создается администратором сервера Web. С помощью специальной программы администратор указывает права доступа к этому каталогу, разрешающие исполнение расположенных там загрузочных файлов CGI и библиотек динамической компоновки DLL приложений ISAPI. Подробно об этом вы можете прочитать в 29 томе нашей “Библиотеки системного программиста”, который называется “Сервер Web своими руками”.

    Функция Complete, которая вызывается при нажатии на одноименную кнопку, проверяет пароли, введенные в полях pwd и pwd1. Если эти пароли совпадают, сценарий посылает данные серверу с помощью метода submit, определенного в форме:

    
    document.forms[0].submit();
    

    Здесь мы обращаемся к первой (и единственной) форме, определенной в нашем документе HTML.

    Программа CGI с именем control.exe отображает полученные данные в динамически создаваемом документе HTML (рис. 3.18).

    Рис. 3.18. Данные, полученные программой CGI от формы

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

    Исходный текст программы CGI, с которой работает наш документ HTML, показан в листинге 3.8.

    Листинг 3.8. Файл chapter3/validation/controls/controls.c

    
    // ===============================================
    // Программа CGI controls.c
    // Получение данных от формы и их
    // отображение в документе HTML,
    // создаваемом динамически
    //
    // (C) Фролов А.В., 1997, 1998
    // E-mail: frolov@glas.apc.org
    // WWW:    http://www.glasnet.ru/~frolov
    //         или
    //         http://www.dials.ccas.ru/frolov
    // ===============================================
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // Прототипы функций перекодировки
    void DecodeStr(char *szString);
    char DecodeHex(char *str);
    
    // ------------------------------------------------
    // Функция main
    // Точка входа программы CGI
    // ------------------------------------------------
    void main(int argc, char *argv[])
    {
      int lSize;
      FILE * fileReceived;
      char * szMethod;
      char szBuf[8196];
      char szSrcBuf[8196];
      char * szPtr;
      char * szParam;
    
      // Вывод заголовка HTTP и разделительной строки
      printf("Content-type: text/html\n\n");
    
      // Вывод начального форагмента документа HTML,
      // формируемого динамически
      printf("<!DOCTYPE HTML PUBLIC"
        " \"-//W3C//DTD HTML 3.2//EN\">");
      printf("<HTML><HEAD><TITLE>User Registration"
        "</TITLE></HEAD><BODY BGCOLOR=#FFFFFF>");
    
      // Определяем метод передачи данных
      szMethod = getenv("REQUEST_METHOD");
    
      // Обработка метода POST
      if(!strcmp(szMethod, "POST"))
      {
        // Определяем размер данных, полученных от браузера
        // при передаче данных из полей формы
        lSize = atoi(getenv("CONTENT_LENGTH"));
    
        // Читаем эти данные в буфер szBuf из
        // стандартного потока ввода STDIN
        fread(szBuf, lSize, 1, stdin);
    
        // Создаем файл, в который будут записаны
        // принятые данные
        fileReceived = fopen("received.dat", "w");
    
        // Выполняем запись принятых данных
        fwrite(szBuf, lSize, 1, fileReceived);
    
        // Закрываем файл принятых данных
        fclose(fileReceived);
    
        // Отображаем значения некоторых переменных среды
        printf("<H2>Переменные среды</H2>");
    
        // Метод доступа
        printf("REQUEST_METHOD = %s", getenv("REQUEST_METHOD"));
    
        // Размер полученных данных в байтах
        printf("<BR>CONTENT_LENGTH = %ld", lSize);
    
        // Тип полученных данных
        printf("<BR>CONTENT_TYPE = %s", getenv("CONTENT_TYPE"));
    
        // Закрываем буфер данных двоичным нулем,
        // превращая его таким образом в строку
        szBuf[lSize] = '\0';
    
        // Делаем копию принятых данных в буфер szSrcBuf
        strcpy(szSrcBuf, szBuf);
    
        // Отображаем принятые данные без обработки
        printf("<H2>Принятые данные</H2>");
        printf("<P>%s", szSrcBuf);
    
        // Выполняем перекодировку принятых данных
        DecodeStr(szSrcBuf);
    
        // Отображаем результат перекодировки
        printf("<H2>Данные после перекодировки</H2>");
        printf("<P>%s", szSrcBuf);
    
        // Выводим список значений полей формы
        printf("<H2>Список значений полей</H2>");
    
        // Дописываем в конец буфера принятых данных
        // символ "&", который используется в качестве
        // разделителя значений полей
        szBuf[lSize] = '&';
        szBuf[lSize + 1] = '\0';
    
        // Цикл по полям формы
        for(szParam = szBuf;;)
        {
          // Ищем очередной разделитель
          szPtr = strchr(szParam, '&');
    
          // Если он найден, раскодируем строку параметров
          if(szPtr != NULL)
          {
            *szPtr = '\0';
            DecodeStr(szParam);
    
            // Выводим в документ значение параметра
            printf("%s<BR>", szParam);
    
            // Переходим к следующему параметру
            szParam = szPtr + 1;
    
            // Если достигнут конец буфера, завершаем цикл
            if(szParam >= (szBuf + lSize))
              break;
          }
          else
            break;
        }
    
        // Выводим завершающий фрагмент документа HTML
        printf("</BODY></HTML>");
        return;
      }
    }
    
    // ------------------------------------------------
    // Функция DecodeStr
    // Раскодирование строки из кодировки URL
    // ------------------------------------------------
    void DecodeStr(char *szString)
    {
      int src;
      int dst;
      char ch;
    
      // Цикл по строке
      for(src=0, dst=0; szString[src]; src++, dst++)
      {
        // Получаем очередной символ перекодируемой строки
        ch = szString[src];
    
        // Заменяем символ "+" на пробел
        ch = (ch == '+') ? ' ' : ch;
    
        // Сохраняем результат
        szString[dst] = ch;
    
        // Обработка шестнадцатеричных кодов вида "%xx"
        if(ch == '%')
        {
          // Выполняем преобразование строки "%xx"
          // в код символа
          szString[dst] = DecodeHex(&szString[src + 1]);
          src += 2;
        }
      }
    
      // Закрываем строку двоичным нулем
      szString[dst] = '\0';
    }
    
    // ------------------------------------------------
    // Функция DecodeHex
    // Раскодирование строки "%xx"
    // ------------------------------------------------
    char DecodeHex(char *str)
    {
      char ch;
    
      // Обрабатываем старший разряд
      if(str[0] >= 'A')
        ch = ((str[0] & 0xdf) - 'A') + 10;
      else
        ch = str[0] - '0';
    
      // Сдвигаем его влево на 4 бита
      ch <<= 4;
    
      // Обрабатываем младший разряд и складываем
      // его со старшим
      if(str[1] >= 'A')
        ch += ((str[1] & 0xdf) - 'A') + 10;
      else
        ch += str[1] - '0';
    
      // Возвращаем результат перекодировки
      return ch;
    }
    

    Это упрощенный вариант программы CGI, которую мы описали в упомянутом выше 29 томе “Библиотеки системного программиста”. Там вы найдете ее полное описание.

    Шестнадцатеричный калькулятор

    На языке сценариев JavaScript можно составлять достаточно большие программы. В качестве примера мы приведем исходный текст сценария, выполняющего функции шестнадцатеричного калькулятора (рис. 3.19).

    Рис. 3.19. Калькулятор, выполненный на языке сценариев JavaScript

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

    С помощью кнопок 0 - F можно вводить шестнадцатеричные числа, которые будут отображаться в текстовом поле Hex. В поле Dec находится десятичное значение введенного шестнадцатеричного числа или результата вычислений.

    Кнопки “+”, “-“, “*” и “/” предназначены, соответственно, для выполнения операции сложения, вычитания, умножения и деления. При помощи кнопки “Enter” можно получить результат вычислений. Кнопка CE позволяет стереть текущее введенное число, а кнопка C - сбросить промежуточный результат вычислений и текущее введенное число.

    Исходный текст документа HTML с калькулятором представлен в листинге 3.9.

    Листинг 3.9. Файл chapter3/hexcalc/hexcalc.html

    
    <HTML>
    <HEAD>
      <TITLE>Hexadecimal calculator</TITLE>
      <SCRIPT>
      <!--
      var total = 0;
      var lastOperation = "+";
      var newnumber = true;
    
      function dec2hex(nDec)
      {
        var szHexTable="0123456789ABCDEF";
        var szResult = "";
        var szBuf="";
        var nRem = 0;
        var bNegative=false;
    
        if(nDec < 0)
        {
          bNegative=true;
          nDec = -nDec;
        }
    
        nTmp=nDec;
    
        while(true)
        {
          nRem = nTmp % 16;
          nTmp = nTmp / 16;
    
          if(Math.floor(nTmp) < 16)
            break;
    
          szBuf=szHexTable.charAt(nRem);
          szResult = szBuf.concat(szResult);
        }
    
        szBuf=szHexTable.charAt(nRem);
        szResult = szBuf.concat(szResult);
    
        if(Math.floor(nTmp) != 0)
        {
          szBuf=szHexTable.charAt(Math.floor(nTmp));
          szResult = szBuf.concat(szResult);
        }
    
        if(bNegative == true)
          return ("-" + szResult);
        else
          return szResult;
      }
    
      function putNumber(btn,form)
      {
        var szOld = "";
        var szNew = "";
        if(newnumber)
        {
          form.displayHex.value = "";
          form.displayDec.value = "";
          newnumber = false;
        }
    
        szOld = form.displayHex.value;
    
        szNew = szOld.concat(btn.name);
        nCurrent = eval("0x" + szNew);
    
        form.displayHex.value = szNew;
        form.displayDec.value = nCurrent;
      }
    
      function clearNumber(form)
      {
        form.displayHex.value = "0";
        form.displayDec.value = "0";
        newnumber = true;
      }
    
      function clearAll(form)
      {
        total = 0;
        lastOperation = "+";
        clearNumber(form);
      }
    
      function plusOp(form)
      {
        var result;
        result = total + " " + lastOperation + " " + form.displayDec.value;
        total = eval(result);
        lastOperation = "+";
    
        form.displayHex.value = dec2hex(total);
        form.displayDec.value = total;
    
        newnumber = true;
      }
    
      function minusOp(form)
      {
        var result;
    
        result = total + " " + lastOperation + " " + form.displayDec.value;
        total = eval(result);
        lastOperation = "-";
    
        form.displayHex.value = dec2hex(total);
        form.displayDec.value = total;
    
        newnumber = true;
      }
    
      function mulOp(form)
      {
        var result;
    
        result = total + " " + lastOperation + " " + form.displayDec.value;
        total = eval(result);
        lastOperation = "*";
    
        form.displayHex.value = dec2hex(total);
        form.displayDec.value = total;
    
        newnumber = true;
      }
    
      function divOp(form)
      {
        var result;
    
        result = total + " " + lastOperation + " " + form.displayDec.value;
        total = eval(result);
        lastOperation = "/";
    
        form.displayHex.value = dec2hex(total);
        form.displayDec.value = total;
    
        newnumber = true;
      }
    
      function getResult(form)
      {
        var result;
        result = total + lastOperation + eval("0x" + form.displayHex.value);
        total = eval(result);
    
        form.displayHex.value = dec2hex(total);
        form.displayDec.value = total;
    
        newnumber = true;
      }
    
      // -->
      </SCRIPT>
      </HEAD>
      <BODY>
        <FORM>
          <TABLE BORDER=2 BORDERCOLOR="Black" BGCOLOR="Yellow">
            <TR>
              <TD>Hex:</TD>
              <TD COLSPAN=3><INPUT TYPE=text NAME="displayHex" VALUE="0" onFocus="this.blur();"></TD>
            </TR>
            <TR>
              <TD>Dec:</TD>
              <TD COLSPAN=3><INPUT TYPE=text NAME="displayDec" VALUE="0" onFocus="this.blur();"></TD>
            </TR>
            <TR>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="C" VALUE=" C " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="D" VALUE=" D " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="E" VALUE=" E " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="F" VALUE=" F " onClick="putNumber(this,this.form);"></TD>
            </TR>
            <TR>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="8" VALUE=" 8 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="9" VALUE=" 9 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="A" VALUE=" A " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="B" VALUE=" B " onClick="putNumber(this,this.form);"></TD>
            </TR>
            <TR>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="4" VALUE=" 4 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="5" VALUE=" 5 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="6" VALUE=" 6 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="7" VALUE=" 7 " onClick="putNumber(this,this.form);"></TD>
            </TR>
            <TR>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="0" VALUE=" 0 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="1" VALUE=" 1 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="2" VALUE=" 2 " onClick="putNumber(this,this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="3" VALUE=" 3 " onClick="putNumber(this,this.form);"></TD>
            </TR>
            <TR>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="+" VALUE=" + " onClick="plusOp(this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="-" VALUE="  -  " onClick="minusOp(this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="*" VALUE=" * " onClick="mulOp(this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="/" VALUE="  /  " onClick="divOp(this.form);"></TD>
            </TR>
            <TR>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="C" VALUE=" C " onClick="clearAll(this.form);"></TD>
              <TD ALIGN=CENTER><INPUT TYPE="button" NAME="CE" VALUE="CE" onClick="clearNumber(this.form);"></TD>
              <TD COLSPAN=2 ALIGN=CENTER><INPUT TYPE="button" NAME="Enter" VALUE="Enter" onClick="getResult(this.form);"></TD>
            </TR>
          </TABLE>
        </FORM>
      </BODY>
    </HTML>
    

    Рассмотрим наиболее интересные моменты.

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

    
      . . .
    <INPUT TYPE=text NAME="displayHex" VALUE="0" onFocus="this.blur();">
      . . .
    <INPUT TYPE=text NAME="displayDec" VALUE="0" onFocus="this.blur();">
      . . .
    

    Это значение устанавливается параметром VALUE.

    Обратите внимание, что для текстовых полей displayHex и displayDec мы предусмотрели обработчики событий onFocus. Этот обработчик получает управление, когда пользователь передает полю фокус ввода. Задача обработчика заключается в том, чтобы снова отобрать фокус ввода, предотвратив непосредственное редактирование числа пользователем.

    С каждой из кнопок, связанной с вводом шестнадцатеричного числа, связан обработчик события onClick, вызывающий функцию putNumber, например:

    
    INPUT TYPE="button" NAME="F" VALUE=" F " onClick="putNumber(this,this.form);">
    

    Этой функции передаются два параметра - нажатая кнопка (как объект класса button) и форма, в которой эта кнопка находится.

    Задача функции putNumber - ввод числа и его отображение в двух текстовых полях, расположенных в верхней части калькулятора:

    
    function putNumber(btn,form)
    {
      var szOld = "";
      var szNew = "";
      if(newnumber)
      {
        form.displayHex.value = "";
        form.displayDec.value = "";
        newnumber = false;
      }
    
      szOld = form.displayHex.value;
    
      szNew = szOld.concat(btn.name);
      nCurrent = eval("0x" + szNew);
    
      form.displayHex.value = szNew;
      form.displayDec.value = nCurrent;
    }
    

    В самом начале функция putNumber проверяет двоичную переменную newnumber. Если значение этой переменной равно true, пользователь вводит первую цифру нового числа. В этом случае функция putNumber сбрасывает содержимое текстовых полей displayHex и displayDec, а также устанавливает значение newnumber, равное false.

    Далее функция добавляет введенную пользователем цифру спереди к переменной szOld, равной текущему значению из поля displayHex. При этом она вызывает метод concat из класса String, предназначенный для слияния (конкатенации) строк.

    На следующем этапе к введенному шестнадцатеричному числу добавляется префикс “0x”, после чего происходит вычисление текущего значения функцией eval. Эта функция пытается интерпретировать текстовую строку, переданную ей в качестве параметра, как арифметическое выражение, возвращая результат интерпретации в виде численного значения. Этот результат сохраняется в переменной nCurrent и отображается в текстовом поле displayDec (исходное шестнадцатеричное число отображается в поле displayHex).

    Если после ввода числа пользователь нажимает одну из четырех кнопок, предназначенных для выполнения операций сложения, вычитания, умножения и деления, вызываются функции, назначенные для этих кнопок в обработчике события onClick. Это функции plusOp (сложение), minusOp (вычитание), mulOp (умножение) и divOp (деление).

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

    
    function plusOp(form)
    {
      var result;
      result = total + " " + lastOperation + " " +
        form.displayDec.value;
      total = eval(result);
      lastOperation = "+";
    
      form.displayHex.value = dec2hex(total);
      form.displayDec.value = total;
    
      newnumber = true;
    }
    

    Здесь глобальная переменная total, имеющая начальное значение, равное нулю, используется для хранения промежуточных результатов вычислений. Она складывается с пробелом и текстовой строкой lastOperation, затем еще с одним пробелом и, наконец, со строкой десятичного представления введенного числа, извлеченного из поля displayDec.

    Строка lastOperation предназначена для хранения кода операции, которая выполнялась в последний раз. Дополнительные пробелы нужны для корректной работы с отрицательными числами.

    С помощью функции eval функция plusOp вычисляет результат операции и записывает его в переменную total. Затем в переменную lastOperation записывается код операции сложения - строка “+”.

    На следующем этапе функция plusOp преобразует значение total с помощью функции dec2hex и отображает результат в шестнадцатеричном виде в поле displayHex, а также в двоичном - в поле displayDec.

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

    Функция getResult вызывается, когда пользователь нажимает на клавишу нашего калькулятора с надписью “Enter”:

    
    function getResult(form)
    {
      var result;
      result = total + lastOperation + eval("0x" +
        form.displayHex.value);
      total = eval(result);
    
      form.displayHex.value = dec2hex(total);
      form.displayDec.value = total;
    
      newnumber = true;
    }
    

    От только что описанной функции plusOp эта функция отличается лишь тем, что она не изменяет значение переменной lastOperation (так как данная кнопка служит для получения итогового результата, а не для выполнения арифметической операции).

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

    В начале своей работы функция dec2hex проверяет знак исходного числа. Отрицательные числа преобразуются в положительные, при этом в переменную bNegative записывается значение true.

    Алгоритм преобразования десятичного числа в шестнадцатеричное основан на делении исходного числа на 16 в цикле. Если целая часть результата деления, вычисляемая с помощью метода Math.floor, оказывается меньше 16, цикл завершается. В противном случае остаток от деления рассматривается как значение текущего шестнадцатеричного разряда.

    Для того чтобы получить символическое представление шестнадцатеричного числа по его значению, мы извлекаем нужный символ из строки szHexTable, вызывая для этого метод charAt:

    
    szBuf=szHexTable.charAt(nRem);
    szResult = szBuf.concat(szResult);
    

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

    Электронные часы

    С помощью сценария JavaScript и формы вы можете сделать достаточно сложные электронные часы, которые будут показывать не только время, но и дату в различных форматах с учетом смещения часового пояса (рис. 3.20).

    Рис. 3.20. Электронные часы в документе HTML

    Исходный текст документа HTML с электронными часами представлен в листинге 3.10.

    Листинг 3.10. Файл chapter3/clock/clock.html

    
    <HTML>
      <HEAD>
        <TITLE>Электронные часы</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
        <!--
    
        nTimer = 0;
        var szCurrentTime = "";
        var szCurrentDate = "";
        var szTimeZone = "";
        var szGMT = "";
        var szLocal = "";
    
        function updateClock()
        {
          var dtDate   = new Date();
          var nHours   = dtDate.getHours();
          var nMinutes = dtDate.getMinutes();
          var nSeconds = dtDate.getSeconds();
    
          szCurrentTime = nHours + ":" + nMinutes +
            ":" + nSeconds;
    
          szCurrentDate = dtDate.getDate() + "." +
            dtDate.getMonth() + "." + dtDate.getYear();
    
          szTimeZone = dtDate.getTimezoneOffset() / 60;
    
          szGMT = dtDate.toGMTString();
    
          szLocal = dtDate.toLocaleString();
    
          Clock.time.value = szCurrentTime;
          Clock.dat.value  = szCurrentDate;
          Clock.timeZone.value = szTimeZone;
          Clock.gmt.value  = szGMT;
          Clock.loc.value  = szLocal;
    
          nTimer = setTimeout("updateClock()", 1000);
        }
        // -->
        </SCRIPT>
      </HEAD>
      <BODY BGCOLOR=white>
        <H1>Электронные часы</H1>
    
        <FORM NAME="Clock">
          <TABLE>
           <TR><TD><B>Дата:</B></TD><TD><INPUT TYPE="text"
             NAME="dat" SIZE="20" ></TD>
           </TR>
           <TR><TD><B>Время:</B></TD><TD><INPUT TYPE="text"
             NAME="time" SIZE="20" ></TD>
           </TR>
           <TR><TD><B>Смещение часового пояса:</B></TD><TD><INPUT TYPE="text"
             NAME="timeZone" SIZE="20" ></TD>
           </TR>
           <TR><TD><B>Дата GMT:</B></TD><TD><INPUT TYPE="text"
             NAME="gmt" SIZE="30" ></TD>
           </TR>
           <TR><TD><B>Локальная дата:</B></TD><TD><INPUT TYPE="text"
             NAME="loc" SIZE="30" ></TD>
           </TR>
          </TABLE>
        </FORM>
    
        <SCRIPT LANGUAGE="JavaScript">
        <!--
          updateClock();
        // -->
        </SCRIPT>
    
      </BODY>
    </HTML>
    

    Часы запускаются сразу после загрузки документа HTML в окно браузера. При этом управление получает функция updateClock. Эта функция определена в заголовке, а ее вызов находится в конце области тела документа HTML.

    Получив управление, функция updateClock создает объект Data для текущей даты, установленной в компьютере:

    
    var dtDate   = new Date();
    

    Затем функция определяет три компоненты времени (часы, минуты и секунды), вызывая для этого методы getHours, getMinutes и getSeconds, определенные в классе Date:

    
    var nHours   = dtDate.getHours();
    var nMinutes = dtDate.getMinutes();
    var nSeconds = dtDate.getSeconds();
    

    Полученные таким образом значения комбинируются в текстовой строке szCurrentTime:

    
    szCurrentTime = nHours + ":" + nMinutes +
            ":" + nSeconds;
    

    В дальнейшем время будет записано функцией updateClock в поле time формы Clock, как это показано ниже:

    
    Clock.time.value = szCurrentTime;
    

    Строка текущей даты получается в результате вызова методов getDate, getMonth и getYear:

    
    szCurrentDate = dtDate.getDate() + "." +
            dtDate.getMonth() + "." + dtDate.getYear();
    

    Эта строка записывается в поле dat формы Clock:

    
    Clock.dat.value  = szCurrentDate;
    

    Для вычисления смещения часового пояса мы воспользовались методом getTimezoneOffset, возвращающим значение в минутах:

    
    szTimeZone = dtDate.getTimezoneOffset() / 60;
    

    Для того чтобы вычислить смещение в часах, мы разделили результат, полученный от метода getTimezoneOffset, на 60.

    Вычисленное смещение часового пояса записывается в поле timeZone формы Clock:

    
    Clock.timeZone.value = szTimeZone;
    

    Для получения времени по Гринвичу мы воспользовались методом toGMTString:

    
    szGMT = dtDate.toGMTString();
    

    Полученное значение будет записано в поле gmt формы Clock:

    
    Clock.gmt.value  = szGMT;
    

    И, наконец, локальную дату и время мы определяем с помощью метода toLocaleString:

    
    szLocal = dtDate.toLocaleString();
    

    Результат записывается в поле loc формы Clock:

    
    Clock.loc.value  = szLocal;
    

    Периодическое выполнение функции updateClock достигается с помощью метода setTimeout:

    
    nTimer = setTimeout("updateClock()", 1000);
    

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

    [Назад] [Содеожание] [Дальше]


    Hosted by uCoz