Введение в JavaScript

         

Фреймы (Frames)


Фреймы — это несколько видоизмененные окна. Отличаются они от обычных окон тем, что размещаются внутри них. У фрейма не может быть ни панели инструментов, ни меню, как в обычном окне. В качестве поля статуса фрейм использует поле статуса окна, в котором он размещен. Существует и ряд других отличий.

Мы остановимся на:

иерархии фреймов;именовании фреймов;передаче данных во фрейм.

Естественно, что иерархия определяет и правила именования фреймов, и способы передачи фокуса фрейму.



Иерархия фреймов


Рассмотрим сначала простой пример. Разделим экран на две вертикальные колонки: (открыть)

<HTML> <HEAD> </HEAD> <FRAMESET COLS="50%,*"> <FRAME NAME=left SRC=left.html> <FRAME NAME=right SRC=right.html> </FRAMESET> </HTML>


Рис. 14.3.  Фрейм с двумя вертикальными колонками

Назовем окно, в которое помещают фреймы, _top(_parent).

Усложним пример: разобьем правый фрейм на два по горизонтали: (открыть)

<HTML> <HEAD> </HEAD> <FRAMESET COLS="50%,*"> <FRAME NAME=left SRC=left.html> <FRAMESET ROWS="50%,*"> <FRAME NAME=top SRC=top.html> <FRAME NAME=bottom SRC=bottom.html> </FRAMESET> </FRAMESET> </HTML>


Рис. 14.4.  Правый фрейм, разбитый на два по горизонтали

Обратите внимание на два момента: во-первых, следует различать _top и top, во-вторых, исчез фрейм   right. По поводу первого замечания: _top — это зарезервированное имя старшего окна, а top — имя фрейма, которое назначил ему автор страницы. По поводу второго замечания: старшим окном для всех фреймов является все окно браузера, фрейма с именем right в данном случае не существует.

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

Первый документ:

<HTML> <HEAD> </HEAD> <FRAMESET COLS="50%,*"> <FRAME NAME=left SRC=left.html> <FRAME NAME=right SRC=right.html> </FRAMESET> </HTML>

Второй документ (right.htm):

<HTML> <HEAD> </HEAD> <FRAMESET ROWS="50%,*"> <FRAME NAME=top SRC=top.html> <FRAME NAME=bottom SRC=bottom.html> </FRAMESET> </HTML>

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



Таким образом, мы получили тот же результат, что и с тремя фреймами и одним старшим окном. Однако этот вариант более гибкий: он позволяет задействовать фрейм, содержащий горизонтальную разбивку. Именно такой прием используется на домашней странице "Web-инжиниринг".

Такая интерпретация фреймовой структуры страницы находит отражение и в именовании фреймов JavaScript.



Именование фреймов


Обратиться к фрейму можно либо по имени, либо как к элементу массива frames[]. Рассмотрим HTML-документ:

<HTML> <HEAD> ... </HEAD> <FRAMESET COLS="20%,*"> <FRAME NAME=left SRC=frame1.htm> <FRAME NAME=right SRC=frame2.htm> </FRAMESET> </HTML>

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

top.frames[1].images[1].src="pic.gif";

или

top.right.images[1].src="pic.gif";

В связи с индексированием фреймов возникает вопрос о том, как они нумеруются в одномерном встроенном массиве фреймов объекта Window. Проиллюстрируем это на примере: (открыть)

<FRAMESET ROWS="50,*,50"> <FRAME NAME=top SRC=top.html> <FRAMESET COLS="100,*,100"> <FRAME NAME=left SRC=left.html> <FRAME NAME=center SRC=center.html> <FRAME NAME=right SRC=right.html> </FRAMESET> <FRAME NAME=bottom SRC=bottom.html> </FRAMESET>


Рис. 14.5.  Центральный фрейм, разбитый на три вертикальных

Построим теперь нечто перпендикулярное предыдущему примеру – столбец из трех фреймов: (открыть)

<FRAMESET COLS="100,*,100"> <FRAME NAME=left SRC=top.html> <FRAMESET ROWS="60,*,60"> <FRAME NAME=top SRC=left.html> <FRAME NAME=center SRC=center.html> <FRAME NAME=bottom SRC=right.html> </FRAMESET> <FRAME NAME=right SRC=bottom.html> </FRAMESET>


Рис. 14.6.  Центральный фрейм, разбитый на три горизонтальных

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



История посещений (History)


История посещений (трасса) страниц World Wide Web позволяет пользователю вернуться к странице, которую он просматривал несколько минут (часов, дней) назад. История посещений в JavaScript трансформируется в объект класса history. Этот объект указывает на массив URL-страниц, которые пользователь посещал и которые он может получить, выбрав из меню браузера режим GO. Методы объекта history позволяют загружать страницы, используя URL из этого массива.

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

<FORM><INPUT TYPE=button VALUE="Назад" onClick=history.back()></FORM>

Данный код отображает кнопку "Назад", нажав на которую мы вернемся на предыдущую страницу.



Методы


Методы Location предназначены для управления загрузкой и перезагрузкой страницы. Это управление заключается в том, что можно либо перезагрузить документ (reload), либо загрузить (replace). При этом в историю просмотра страниц (history) информация не заносится:

window.location.reload(true); window.location.replace('#top');

Метод reload() полностью моделирует поведение браузера при нажатии на кнопку Reload в панели инструментов. Если вызывать метод без аргумента или указать его равным true, то браузер проверит время последней модификации документа и загрузит его либо из кеша (если документ не был модифицирован), либо с сервера. Такое поведение соответствует простому нажатию на кнопку Reload. Если в качестве аргумента указать false, то браузер перезагрузит текущий документ с сервера, несмотря ни на что. Такое поведение соответствует одновременному нажатию на Reload и кнопку клавиатуры Shift (Reload+Shift).

Метод replace() позволяет заменить одну страницу другой таким образом, что это замещение не будет отражено в трассе просмотра HTML-страниц (history), и при нажатии на кнопку Back из панели инструментов пользователь всегда будет попадать на первую загруженную обычным способом (по гипертекстовой ссылке) страницу. Напомним, что при изменении свойств Location также происходит перезагрузка страниц, но в этом случае записи об их посещении в history пропадают.



Поле location


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


Рис. 14.2.  Поле Location

Вообще говоря, Location — это объект. Из-за изменений в версиях JavaScript класс Location входит как подкласс и в класс Window, и в класс Document. Мы будем рассматривать Location только как window.location. Кроме того, Location — это еще и подкласс класса URL, к которому относятся также объекты классов Area и Link. Location наследует все свойства URL, что позволяет получить доступ к любой части схемы URL.

Рассмотрим характеристики и способы использования объекта Location:

свойства;методы;событий, характеризующих Location, нет.

Как мы видим, список характеристик объекта Location неполный.



Поле статуса


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


Рис. 14.1.  Поле статуса

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

window.status;window.defaultStatus.

Разница между ними заключается в том, что браузер на самом деле имеет несколько состояний, связанных с некоторыми событиями. Состояние браузера отражается в сообщении в поле статуса. По большому счету, существует только два состояния: нет никаких событий (defaultStatus) и происходят какие-то события (status).



Microsoft Internet Explorer


navigator.appName = Microsoft Internet Explorer navigator.appCodeName = Mozilla navigator.appVersion = 4.0 (compatible; MSIE 5.5; Windows 98) navigator.userAgent = Mozilla/4.0 (compatible; MSIE 5.5; Windows 98)
Листинг 14.1.
Закрыть окно

<SCRIPT> document.write("<P ID=red>"); if(navigator.javaEnabled()==true) document.write("Ваша программа поддерживает исполнение Java-апплетов"); if(navigator.javaEnabled()==false) document.write("<FONT COLOR=red> Ваша программа не поддерживает исполнение Java-апплетов</FONT>"); </SCRIPT> </example>
Листинг 14.2.
Закрыть окно

<SCRIPT> if(navigator.mimeTypes['image/gif']!=null) document.write("Ваш браузер поддерживает GIF<BR>"); if(navigator.mimeTypes['image/tif']==null) document.write(" Ваш браузер не поддерживает TIFF"); </SCRIPT>
Листинг 14.3.
Закрыть окно

<FORM> <INPUT TYPE=button VALUE=" Простое окно" onClick="window.open( 'about:blank','test1', 'directories=no,height=200,location=no,menubar=no,resizable=no,scrollbars=no,status=no,toolbar=no,width=200'); "> <INPUT TYPE=button VALUE="Сложное окно" onClick="window.open( 'about:blank','test2', 'directories=yes,height=200,location=yes,menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes,width=200'); "> </FORM>
Листинг 14.4.
Закрыть окно

function myfocus(a) { id = window.open("","example","scrollbars,width=300,height=200"); //открываем окно и заводим переменную с указателем на него //если окно с таким именем существует, то новое окно не создается, //а открывается поток для записи в окно с этим именем if(a==1) { id.document.open(); //открываем поток ввода в уже созданное окно id.document.write("<CENTER>>Открыли окно в первый раз"); //Пишем в этот поток } if(a==2) { id.document.open(); id.document.write("<CENTER>Открыли окно во второй раз"); } if(a==3) { id.focus(); //передаем фокус, затем выполняем те же действия, что и в предыдущем случае id.document.open(); id.document.write("<CENTER>Открыли окно в третий раз"); } id.document.write("<FORM><INPUT TYPE=button onClick='window.close();' VALUE='Закрыть окно'></CENTER>"); id.document.close(); }
Листинг 14.5.
Закрыть окно

var flag= 0; var idp=null; function myclock() { if(flag==1) { d = new Date(); window.document.c.f.value = d.getHours()+":"+d.getMinutes()+":"+d.getSeconds(); } idp=setTimeout("myclock();",500); } function flagss() { if(flag==0) flag=1; else flag=0; } ... <FORM NAME=c> Текущее время:<INPUT NAME=f size=8><INPUT TYPE=button VALUE="Start/Stop" onClick="flagss();myclock();"> </FORM>
Листинг 14.6.
Закрыть окно

var idp1 = null; function start() { d = new Date(); window.document.c1.f1.value = d.getHours()+":"+d.getMinutes()+":"+d.getSeconds(); idp1=setTimeout("start();",500); } function stop() { clearTimeout(idp1);idp1=null; } ... <FORM NAME=c1> Текущее время:<INPUT NAME=f1 size=8> <INPUT TYPE=button VALUE="Start" onClick="if(idp1==null)start();"> <INPUT TYPE=button VALUE="Stop" onClick="if(idp1!=null)stop();"> </FORM>
Листинг 14.7.
Закрыть окно

function load() { if(self.document.f.s.options[document.f.s.selectedIndex].text=="top") { document.f.target = "mytop"; self.top.frames[2].document.open(); self.top.frames[2].document.close(); } else { document.f.target = "mybottom"; self.top.frames[1].document.open(); self.top.frames[1].document.close(); } return true; }
Листинг 14.8.
Закрыть окно

function load() { if(self.document.f.s.options[document.f.s.selectedIndex].text=="top") { window.open("./framer.htm","mytop"); self.top.frames[2].document.open(); self.top.frames[2].document.close(); } else { window.open("./framer.htm","mybottom"); self.top.frames[1].document.open(); self.top.frames[1].document.close(); } return false; }
Листинг 14.9.
Закрыть окно

Программируем defaultStatus


Свойство defaultStatus определяет текст, отображаемый в поле статуса, когда никаких событий не происходит. В нашем документе мы определили его при загрузке документа:

<BODY onLoad="window.defaultStatus='Status bar programming';">

Это сообщение появляется в тот момент, когда загружены все компоненты страницы (текст, графика, апплеты и т.п.). Оно восстанавливается в строке статуса после возврата из любого события, которое может произойти при просмотре документа. Любопытно, что движение мыши по свободному от гипертекстовых ссылок полю страницы приводит к постоянному отображению defaultStatus.



Программируем status


Свойство status связано с отображением сообщений о событиях, отличных от простой загрузки страницы. Например, когда курсор мыши проходит над гипертекстовой ссылкой, URL, указанный в атрибуте HREF, отображается в поле статуса. При попадании курсора мыши на поле, свободное от ссылок, в поле статуса восстанавливается сообщение по умолчанию (Document:Done). Эта техника реализована на данной странице при переходе на описание свойств status и defaultStatus

<A HREF=#status onMouseover="window.status='Jump to status description';return true;" onMouseout="window.status='Status bar programming';return true;">window.status</A>

В документации по JavaScript указано, что обработчик событий mouseover и mouseout должен возвращать значение true. Это нужно для того, чтобы браузер не выполнял действий, заданных по умолчанию. Практика показывает, что Netscape Navigator 4.0 прекрасно обходится и без возврата значения true.



что браузер отображает страницу, расположенную


Предположим, что браузер отображает страницу, расположенную по адресу:
http://intuit.ru:80/r/dir/page?search#mark
Тогда свойства объекта Location примут следующие значения:
window.location.href = http://intuit.ru:80/r/dir/page?search#mark window.location.protocol = http; window.location.hostname = intuit.ru; window.location.host = intuit.ru:80; window.location.port = 80 window.location.pathname = /r/dir/; window.location.search = search; window.location.hash = mark;

Управление окнами


Что можно сделать с окном? Открыть (создать), закрыть (удалить), положить его поверх всех других открытых окон (передать фокус). Кроме того, можно управлять свойствами окна и свойствами подчиненных ему объектов. Описанию основных свойств посвящен раздел "Программируем свойства окна браузера", поэтому сосредоточимся на простых и наиболее популярных методах управления окнами:

alert();confirm();prompt();

open();

close();focus();

setTimeout();clearTimeout().

Здесь не указаны только два метода: scroll() и blur().

Первый позволяет прокрутить окно на определенную позицию. Но его очень сложно использовать, не зная координат окна. Последнее является обычным делом, если только не используется технология программирования слоев или CSS (Cascading Style Sheets).

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



Window.alert()


Метод alert() позволяет выдать окно предупреждения:

<A HREF="javascript:window.alert('Внимание')"> Повторите запрос!</A>

Все очень просто, но нужно иметь в виду, что сообщения выводятся системным шрифтом, следовательно, для получения предупреждений на русском языке нужна локализованная версия ОС.



Window.close()


Метод close() — это оборотная сторона медали метода open(). Он позволяет закрыть окно. Чаще всего возникает вопрос, какое из окон, собственно, следует закрыть. Если необходимо закрыть текущее, то:

window.close(); self.close();

Если необходимо закрыть родительское окно, т.е. окно, из которого было открыто текущее, то:

window.opener.close();

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

id=window.open(); ... id.close();

Как видно из последнего примера, закрывают окно не по имени (значение атрибута TARGET тут ни при чем), а используют указатель на объект.



Window.confirm()


Метод confirm() позволяет задать пользователю вопрос, на который тот может ответить либо положительно, либо отрицательно:

<FORM> <INPUT TYPE=button VALUE="Вы знаете JavaScript?" onClick="if(window.confirm('Знаю все')==true) { document.forms[0].elements[1].value='Да'; } else { document.forms[0].elements[1].value='Нет'; };"><BR> </FORM>

Все ограничения для сообщений на русском языке, которые были описаны для метода alert(), справедливы и для метода confirm().



Window.focus()


Метод focus() применяется для передачи фокуса в окно, с которым он использовался. Передача фокуса полезна как при открытии окна, так и при его закрытии, не говоря уже о случаях, когда нужно выбирать окна. Рассмотрим пример.

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

Листинг 14.5.

(html, txt)

Поскольку мы пишем содержание нового окна из окна старого (родителя), то в качестве указателя на объект используем значение переменной id.



Window.open()


У этого метода окна атрибутов больше, чем у некоторых объектов. Метод open() предназначен для создания новых окон. В общем случае его синтаксис выглядит следующим образом:

open("URL","window_name","param,param,...", replace);

где: URL — страница, которая будет загружена в новое окно, window_name — имя окна, которое можно использовать в атрибуте TARGET в контейнерах A и FORM.

Таблица 2.

ПараметрыНазначение
replaceПозволяет при открытии окна управлять записью в массив History
paramСписок параметров
widthШирина окна в пикселах
heightВысота окна в пикселах
toolbarСоздает окно с системными кнопками браузера
locationСоздает окно с полем location
directoriesСоздает окно с меню предпочтений пользователя
statusСоздает окно с полем статуса status
menubarСоздает окно с меню
scrollbarsСоздает окно с полосами прокрутки
resizableСоздает окно, размер которого можно будет изменять

Приведем следующий пример:

Листинг 14.4.

(html, txt)

При нажатии кнопки "простое окно" получаем окно со следующими параметрами:

directories=no - окно без менюheight=200 - высота 200 pxlocation=no - поле location отсутствуетmenubar=no - без менюresizable=no - размер изменять нельзяscrollbars=no - полосы прокрутки отсутствуютstatus=no - статусная строка отсутствуетtoolbar=no - системные кнопки браузера отсутствуютwidth=200 - ширина 200

При нажатии кнопки "сложное окно" получаем окно, где:

directories=yes - окно с менюheight=200 - высота 200 pxlocation=yes - поле location естьmenubar=yes - меню естьresizable=yes - размер изменять можноscrollbars=yes - есть полосы прокруткиstatus=yes - статусная строка естьtoolbar=yes - системные кнопки браузера естьwidth=200 - ширина 200



Window.prompt()


Метод prompt() позволяет принять от пользователя короткую строку текста, которая набирается в поле ввода информационного окна:

<FORM> <INPUT TYPE=button VALUE="Открыть окно ввода" onClick="document.forms[0].elements[1].value=window.prompt('Введите сообщение');"> <INPUT SIZE=30> </FORM>

Введенную пользователем строчку можно присвоить любой переменной и потом разбирать ее в JavaScript-программе.



Window.setTimeout()


Метод setTimeout() используется для создания нового потока вычислений, исполнение которого откладывается на время (ms), указанное вторым аргументом:

idt = setTimeout("JavaScript_код",Time);

Типичное применение этой функции — организация автоматического изменения свойств объектов. Например, можно запустить часы в поле формы:

Листинг 14.6.

(html, txt)

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



Action


Свойство action отвечает за вызов скрипта (CGI-скрипта). В нем указывается его (скрипта) URL. Но там, где можно указать URL, можно указать и его схему javascript:

<FORM METHOD=post ACTION="javascript:window.alert('We use JavaScript-code as an URL'); void(0);"> <INPUT TYPE=submit VALUE="Продемонстрировать JavaScript в action"> </FORM>

Обратите внимание на тот факт, что в контейнере FORM указан атрибут METHOD. В данном случае это сделано для того, чтобы к URL, заданному в action, не дописывался символ "?". Дело в том, что методом доступа по умолчанию является метод GET. В этом методе при обращении к ресурсу из формы создается элемент URL под названием search. Этот элемент предваряется символом "?", который дописывается к URL скрипта, а в нашем случае, к JavaScript-коду.

Конструкция вида

window.alert("String");void(0);?

провоцирует ошибку JavaScript.

Метод POST передает данные формы скрипту в теле HTTP-сообщения, поэтому символ "?" не добавляется к URL, и ошибка не генерируется. При этом применение void(0) отменяет перезагрузку документа, и браузер не генерирует событие submit, т.е. не обращается к серверу при нажатии на кнопку, как при стандартной обработке форм.



Cookies


Волшебные ключики, или cookies, не являются полями формы, но, тем не менее, отойдя от строгого рассмотрения иерархии объектов JavaScript, мы уделим им немного внимания, как одному из механизмов управления обменом данных. Основная функция cookie — поддержка сеанса работы между клиентом (браузером) и сервером.

cookie — это небольшой фрагмент текста, который передается от сервера браузеру и потом может быть возвращен обратно. Подробно о cookie рассказано в "Спецификации Cookie", которую можно найти в главе "Дополнения". Программа на JavaScript способна прочитать выставленное значение cookie и даже изменить его. Для этой цели используют свойство объекта DOCUMENT — cookie:

<FORM> <INPUT TYPE=button VALUE="Показать Cookies" onClick="window.alert(window.document.cookie);"> </FORM>

В данном случае cookies отображаются в виде одной большой строки со множеством значений. Свойство cookie документа можно переопределить:

function asign() { document.cookie="n1=3"; window.alert(document.cookie); } ... <FORM> <INPUT TYPE=button VALUE="Изменить n1" onClick="asign()"> </FORM>

Как видно из примера, программисту не нужно выделять cookie из строки. Браузер рассматривает cookies как ассоциированный массив (хеш) и изменяет значение cookie по имени "ключика".

Наконец, cookie можно удалить. Если быть более точным — деактивировать, указав время его действия:

function change_cookies() { a = new Array(); c = new Date(); a = document.cookie.split(';'); document.cookie=a[0]+"; expires="+c.toGMTString()+";"; window.alert(document.cookie); } ... <FORM> <INPUT TYPE=button VALUE="delete cookies" onClick="change_cookies()"> </FORM>

В данном случае мы "удаляем" cookie за счет параметра expire (времени, до которого cookie живет). Так как мы берем текущее время, то cookie исчезает из списка "ключиков". Многократно нажимая на кнопку, можно удалить все cookies для данной страницы.



Единственное поле в форме


Если в форме присутствует одно-единственное поле, и мы в него осуществили ввод и после этого нажали Enter, то браузер сгенерирует событие submit:

<FORM onSubmit="window.alert('Сделано');return false;"> <INPUT SIZE=10 MAXLENGTH=10> </FORM>

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

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



Elements[]


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

<FORM NAME=fe> <INPUT NAME=fe1 SIZE=30 MAXLENGTH=30> <INPUT TYPE=button VALUE="Ввести текст по имени" onClick="document.fe.fe1.value='Ввести текст по имени';"> <INPUT TYPE=button VALUE="Ввести текст по индексу" onClick="document.fe.elements[0].value='Ввести текст по индексу';"> <INPUT TYPE=reset VALUE="Очистить"> </FORM>

Как видно из этого примера, индексирование полей в массиве начинается с цифры "0". Общее число полей в форме доступно как результат обращения

document.forms[i].elements.length.



Encoding


Такое свойство у объекта Form есть, но не совсем понятно, как его использовать. Изменение свойства encoding оправдано только в том случае, когда в форме имеется поле типа file. В этом случае предполагается, что пользователю разрешена передача файла со своего локального диска на сервер. При этом если не указана кодировка multipart/form-data, то передаваться будет только имя файла, а если она указана, то и сам файл тоже.

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



Изменение значения поля ввода


Реакция на изменение значения поля ввода обрабатывается посредством программы, указанной в атрибуте onChange:

<FORM METHOD="post" onSubmit="return false;"> <INPUT SIZE="15" MAXLENGHT="15" VALUE="Тест" onChange="window.alert(document.forms[0].elements[0].value);"> <INPUT TYPE="button" VALUE="Изменить" onClick="document.forms[0].elements[0].value='Change';"> </FORM>

Если установить фокус на поле ввода и нажать Enter, ничего не произойдет. Если ввести что-либо в расположенное выше поле ввода, а потом нажать на Enter, то появится окно предупреждения с введенным текстом (для Netscape Navigator) или ничего не произойдет (для Internet Explorer последних версий). Если вы используете Internet Explorer последних версий, то окно предупреждения появится только после установки фокуса вне поля ввода. Это следует прокомментировать следующим образом: во-первых, обработчик onChange вызывается только тогда, когда ввод в поле закончен. Событие не вызывается при каждом нажатии на кнопки клавиатуры при вводе текста в поле. Во-вторых, обработчик события не вызывается при изменении значения атрибута VALUE из JavaScript-программы. В этом можно убедиться, нажав на кнопку Change - окно предупреждения не открывается. Но если ввести что-то в поле, а после этого нажать на Change, окно появится.

Отметим, что он работает по-разному для Internet Explorer и Netscape Navigator, а именно по-разному обрабатывается событие onChange. Для Internet Explorer при любом изменении поля событие обрабатывается незамедлительно, для Netscape Navigator — после потери фокуса активным полем.



Картинки


Кнопки-картинки — это те же кнопки, но только с возможностью отправки данных на сервер. Собственно, такие кнопки в JavaScript составляют две разновидности контейнера INPUT: image и submit. В JavaScript объект, связанный с данными кнопками, называется Submit.

<FORM> Активная кнопка: <INPUT TYPE=image SRC=images.gif onClick="return false;"> </FORM>

Как мы уже отмечали, данный объект обладает теми же свойствами, методами и событиями, что и объект Button. Но вот реакция в разных браузерах при обработке событий может быть различной. Так, в событии onClick в Internet Explorer можно отменить передачу данных на сервер, выдав в качестве значения возврата false. Netscape Navigator на такое поведение обработчика события вообще не реагирует, и отменять передачу можно только в атрибуте onSubmit контейнера FORM:

<FORM onSubmit="return false"> Активная кнопка: <INPUT TYPE=image SRC=images.gif border=0> </FORM>

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



Кнопка Submit


Кнопка Submit представляет собой разновидность поля ввода. Она ведет себя так же, как и обычная кнопка, но только еще генерирует событие submit (передачу данных на сервер). В этом, с точки зрения JavaScript-программирования, она абсолютно идентична графическим кнопкам:

<FORM> <INPUT TYPE=submit VALUE=submit> </FORM>

В данном примере мы просто перезагружаем страницу.

С точки зрения программирования наибольший интерес представляет возможность перехвата события submit и выполнение при этом действий, отличных от стандартных. Для этой цели у кнопки есть атрибут обработки события click (onClick):

<FORM> <INPUT TYPE=submit VALUE=Submit onClick="return false;"> </FORM>

Из этого примера видно, что кнопка Submit ведет себя несколько иначе, чем графическая кнопка в Netscape Navigator, но так же, как графическая кнопка в Internet Explorer (со временем различия наверняка исчезнут). При нажатии на кнопку перезагрузки страницы не происходит — передача данных на сервер отменена. Обработчик действует так же, как обработчик события submit в контейнере FORM.

Теперь можно написать собственную программу обработки события submit:

function my_submit() { if(window.confirm("Хотите перегрузить страницу?")) return true; else return false; } ... <FORM> <INPUT TYPE=submit VALUE=Submit onClick="return my_submit();"> </FORM>

Если подтвердить необходимость перезагрузки страницы, она действительно будет перезагружена, а при отказе (cancel) вы вернетесь в текущую страницу без перезагрузки. Действия могут быть и более сложными. В любом случае, если функция обработки возвращает значение true, то передача данных на сервер (в нашем примере — перезагрузка страницы) происходит, иначе (значение false) — данные не передаются.



Кнопки


Использование кнопок в Web вообще немыслимо без применения JavaScript. Создайте форму с кнопкой и посмотрите, что будет, если на эту кнопку нажать — кнопка продавливается, но ничего не происходит. Ни одно из стандартных событий формы (reset или submit) не вызывается. Конечно, данное замечание не касается кнопок Submit и Reset.

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

<FORM> <INPUT TYPE=button VALUE="Окно предупреждения" onClick="window.alert('Открыли окно');"> </FORM>

Текст, отображаемый на кнопке, определяется атрибутом VALUE контейнера INPUT. С этим атрибутом связано свойство value объекта Button. Любопытно, что, согласно спецификации, изменять значение данного атрибута нельзя. Однако в версии 4 Netscape Navigator и Internet Explorer это допустимо.

Следует отметить, что в Netscape Navigator размер кнопки фиксирован (первое значение должно быть самым длинным, иначе будет не очень красиво), а в Internet Explorer размер изменяется в зависимости от длины текста.



Контейнер FORM


Если рассматривать программирование на JavaScript в исторической перспективе, то первыми объектами, для которых были разработаны методы и свойства, стали поля форм. Обычно контейнер FORM и поля форм именованы:

<FORM NAME=f_name METHOD=get ACTION="javascript:void(0);"> <INPUT NAME=i_name SIZE=30 MAXLENGTH=30> </FORM>

Поэтому в программах на JavaScript к ним обращаются по имени:

window.document.f_name.i_name.value="Текстовое поле";

Того же эффекта можно достичь, используя массив форм загруженного документа:

window.document.forms[0].elements[0].value="Текстовое поле";

В данном примере не только к форме, но и к полю формы мы обращаемся как к элементу массива.

Рассмотрим подробнее объект Form, который соответствует контейнеру FORM.

СвойстваМетодыСобытия

action method target elements[] encoding

reset() submit()

onReset onSubmit

Сами по себе методы, свойства и события объекта Form используются нечасто. Их переопределение обычно связано с реакцией на изменения значений полей формы.



Length


В примерах перепрограммирования options[] активно используется свойство объекта Select length. Оно определяет количество альтернатив, заданных для поля выбора. При помощи этого свойства можно удалять и восстанавливать списки.

Определим посредством этого свойства число вариантов в предыдущем примере:

<FORM NAME=f3> Число вариантов: <INPUT NAME=i0 SIZE=1 MAXLENGTH=1 onFocus="out();"> </FORM> <SCRIPT> document.f3.i0.value=document.f1.s1.length; </SCRIPT>

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



Method


Свойство method определяет метод доступа к ресурсам HTTP-сервера из программы-браузера. В зависимости от того, как автор HTML-страницы собирается получать и обрабатывать данные из формы, он может выбрать тот или иной метод доступа. На практике чаще всего используются методы GET и POST.

JavaScript-программа может изменить значение этого свойства. В предыдущем разделе (action) метод доступа в форме был указан явно. Теперь мы его переопределим в момент исполнения программы:

Пример 15.1.

(html, txt)

По умолчанию установлен метод GET.

В данном примере стоит обратить внимание на два момента:

Прежде чем открывать окно предупреждения, следует нажать кнопку "Метод POST". Если этого не сделать, то появится сообщение об ошибке JavaScript. Здесь все выглядит достаточно логично. Формирование URL происходит при генерации события submit, а вызов скрипта — после того, как событие сгенерировано. Поэтому вставить переопределение метода в обработчик события нельзя, так как к этому моменту будет уже сгенерирован URL, который, в свою очередь, будет JavaScript-программой с символом "?" на конце. Переопределение метода должно быть выполнено раньше, чем произойдет событие submit. В тело документа через контейнер SCRIPT встроен JavaScript-код, который сообщает метод доступа, установленный в форме по умолчанию. Этот контейнер расположен сразу за контейнером FORM. Ставить его перед контейнером FORM нельзя, так как в момент получения интерпретатором управления объект FORM не будет создан, и, следовательно, работать с его свойствами не представляется возможным.

Никаких других особенностей свойство method не имеет. В данном свойстве можно указать и другие методы доступа, отличные от GET и POST, но это требует дополнительной настройки сервера.



Метод submit()


Метод submit() — это метод формы. Если в программе вызывается метод submit, то данные из формы, к которой применяется данный метод, передаются на сервер. Усовершенствуем пример с полем ввода и меню выбора (прежде чем выбирать альтернативы, прочтите комментарий под примером):

<FORM onSubmit="window.alert('Сделано');return false;"> <INPUT SIZE=10 MAXLENGTH=10> <SELECT onChange="form.submit();"> <OPTION>Вариант 1<OPTION>Вариант 2</SELECT> </FORM>

При выборе альтернативы пользователь сразу инициирует обмен данными с сервером. Событие submit в данном случае обработчиком событий не перехватывается, в отличие от нажатия Enter. Такое поведение браузера довольно логично. Если программист вызвал метод submit(), то, наверное, он предварительно проверил данные, которые отправляет на сервер.



Объект Option


Объект Option интересен тем, что в отличие от многих других объектов JavaScript, имеет конструктор. Это означает, что программист может сам создать объект Option:

opt = new Option([ text, [ value, [ defaultSelected, [ selected ] ] ] ]);

где:

text — строка текста, которая размещается в контейнере <LI> (<LI>текст);
value — значение, которое передается серверу при выборе альтернативы, связанной с объектом Option;
defaultSelected — альтернатива выбрана по умолчанию(true/false);
selected — альтернатива выбрана(true/false).

На первый взгляд не очень понятно, для чего может понадобиться программисту такой объект, ведь создать объект типа Select нельзя и, следовательно, нельзя приписать ему новый объект OPTION. Все объясняется, когда речь заходит об изменении списка альтернатив встроенных в документ объектов Select. Делать это можно, так как изменение списка альтернатив Select не приводит к переформатированию документа. Изменение списка альтернатив позволяет решить проблему создания вложенных меню, которых нет в HTML-формах, путем программирования обычных меню (options[]).

При программировании альтернатив (объект Option) следует обратить внимание на то, что среди свойств Option нет свойства name. Это означает, что к объекту нельзя обратиться по имени. Отсутствие свойства объясняется тем, что у контейнера OPTION нет атрибута NAME. К встроенным в документ объектам Option можно обращаться только как к элементам массива options[] объекта Select.



Обмен данными


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

нажата кнопка Submit; нажата графическая кнопка; нажата клавиша Enter в форме из одного поля; вызван метод submit().

При описании отображения контейнера FORM на объекты JavaScript было подробно рассказано об обработке события submit. В данном разделе мы сосредоточимся на сочетании JavaScript-программ в атрибутах полей и обработчиках событий. Особое внимание нужно уделить возможности перехвата/генерации события submit.



OnChange


Событие change наступает в тот момент, когда изменяется значение выбранного индекса в объекте Select. С изменением этого индекса в полях выбора единственного варианта на данной странице мы сталкивались неоднократно (selectedIndex и options[]). Данное событие обрабатывается JavaScript-программой, которая указывается в атрибуте onChange контейнера SELECT. В этом контексте интересно посмотреть, что происходит, когда мы имеем дело с multiple контейнером SELECT:

<FORM> Набор канцелярских товаров: <SELECT onChange="form.elements[1].value=''; for(i=0;i<form.elements[0].length;i++) if(form.elements[0].options[i].selected==true) form.elements[1].value = form.elements[1].value+i;"multiple> <OPTION>Вариант 1 <OPTION>Вариант 2 <OPTION>Вариант 3 <OPTION>Вариант 4 <OPTION>Вариант 5 <OPTION>Вариант 6 <OPTION>Вариант 7 </SELECT> Выбраны позиции: <INPUT NAME=s1 SIZE=7 MAXLENGTH=7 onFocus="form.elements[1].blur();"> </FORM>

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



OnReset


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

<FORM onReset="javascript:window.alert( 'Event Reset');return false;"> <INPUT VALUE="Значение по умолчанию"> <INPUT TYPE=reset VALUE="Восстановить"> </FORM>

В этом примере следует обратить внимание на то, что обработчик события reset возвращает логическое значение false. Это сделано для того, чтобы перехватить обработку события reset полностью. Если обработчик события возвращает значение false, то установка полей по умолчанию не производится; если обработчик событий возвращает значение true, то установка значений полей по умолчанию производится.



Options[]


Массив options[] — это свойство объекта Select. Элементы этого массива обладают теми же свойствами, что и объекты Option. Собственно, это и есть объекты Option, встроенные в документ. Они создаются по мере загрузки страницы браузером. Программист имеет возможность не только создавать новые объекты Option, но и удалять уже созданные браузером объекты:

<FORM NAME=f0> <SELECT NAME=s0> <OPTION>Первый вариант <OPTION>Второй вариант <OPTION>Третий вариант </SELECT> <INPUT TYPE=button VALUE="Удалить последний вариант" onClick="document.f0.s0.options[document.f0.s0.length-1]=null;"> <INPUT TYPE=reset VALUE=Reset> </FORM>

В данном примере при загрузке страницы с сервера определено три альтернативы. Они появляются, если выбрать поле select. После нажатия на кнопку удаления последнего варианта ("Delete last option") остается только две альтернативы. Если еще раз нажать на кнопку удаления альтернативы, останется только одна альтернатива и т.д. В конечном счете, вариантов может не остаться вообще, т.е. пользователь лишится возможности выбора. Кнопка Reset показывает, что альтернативы утеряны бесследно, так как после нажатия на эту кнопку содержание поля SELECT не восстанавливается.

Теперь, используя конструктор Option, сделаем процесс обратимым:

function def_f1() { document.f1.s1.options[0] = new Option("вариант Один","",true,true); document.f1.s1.options[1] = new Option("вариант Два"); document.f1.s1.options[2] = new Option("вариант Три"); return false; } ... <FORM NAME=f1 onReset="def_f1();"> <SELECT NAME=s1> <OPTION>вариант Один <OPTION>вариант Два <OPTION>вариант Три </SELECT> <INPUT TYPE=button VALUE="Удалить последний вариант" onClick="document.f1.s1.options[document.f1.s1.length-1]=null;"> <INPUT TYPE=reset VALUE=Reset> </FORM>

В данном случае мы обрабатываем событие reset (контейнер FORM). При этом создаем новые объекты типа Option и подчиняем их объекту Select. При этом первый элемент массива должен быть выбран по умолчанию, чтобы смоделировать поведение при начальной загрузке страницы.

В HTML-формах нельзя реализовать подменю. JavaScript позволяет обойти это ограничение и выполнить замену путем программирования поля select.



в качестве an


<FORM NAME=m ACTION="javascript:window.alert('Мы используем JavaScript-код в качестве an URL');void(0);"> <SCRIPT> document.write("<FONT COLOR=navy>По умолчанию установлен метод</FONT>"+document.m.method+"."); </SCRIPT> <INPUT TYPE=button onClick="window.document.main.document.m.method='post';" VALUE="Метод POST"> <INPUT TYPE=button onClick="window.document.main.document.m.method='get';" VALUE="Метод GET"> <INPUT TYPE=submit VALUE="JavaScript в ACTION"> </FORM>
Пример 15.1.
Закрыть окно

for(i=1;i<id.frames.length;i++) { if(id.frames[i].name== id.frames[0].document.f0.s0.options[id.frames[0].document.f0.s0.selectedIndex].text) { id.frames[i].document.open(); id.frames[i].document.write("<CENTER>Выбрали этот фрейм</CENTER>"); id.frames[i].document.close(); } else {id.frames[i].document.open(); id.frames[i].document.write("<CENTER> Этот фрейм не выбрали</CENTER>"); id.frames[i].document.close(); } }
Пример 15.2.
Закрыть окно

function test() { if(parseInt(document.sub.digit.value).toString()=="NaN") { window.alert(" Некорректные данные в поле формы."); return false; } else { return true; } } ... <FORM NAME=sub onSubmit="return test();" METHOD=post ACTION="javascript:window.alert('Данные подтверждены');void(0);"> <INPUT NAME=digit SIZE=1 MAXLENGTH=1><INPUT TYPE=submit VALUE="Отправить"> </FORM>
Пример 15.3.
Закрыть окно

<FORM>Число гипертекстовых ссылок: <INPUT SIZE=10 MAXLENGHT=10 VALUE="&{document.links.length};"> до момента обработки формы. <INPUT TYPE=button VALUE="Число всех гипертекстовых ссылок в документе" onClick="window.document.forms[0].elements[0].value=document.links.length;"> <INPUT TYPE=reset VALUE="Установить по умолчанию"> </FORM>
Пример 15.4.
Закрыть окно

Reset()


Метод reset(), не путать с обработчиком события onReset, позволяет установить значения полей формы по умолчанию. При этом использовать кнопку типа Reset не требуется:

<FORM NAME=r> <INPUT VALUE="Значение по умолчанию" SIZE=30 MAXLENGTH=30> <INPUT TYPE=button VALUE="Изменим текст в поле ввода" onClick="document.r.elements[0].value='Изменили текст';"> </FORM> <A HREF="javascript:document.r.reset();void(0);"> Установили значение по умолчанию</A>

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



Selected


Свойство selected объекта Option, на котором был построен пример с канцелярскими принадлежностями, может принимать два значения: истина (true) или ложь (false). В примере мы распечатываем индекс выбранной альтернативы, если значение свойства selected у объекта Option — true:

if(form.elements[0].options[i].selected==true) ...

Вообще говоря, свойство selected интересно именно в случае поля множественного выбора. В случае выбора единственного варианта его можно получить, указав на свойство selectedIndex объекта Select.



SelectedIndex


Свойство объекта Select, которое возвращает значение выбранного варианта, обозначается как selectedIndex.

<FORM> Вариант: <SELECT NAME=s0 onChange="form.elements[1].value=selectedIndex;"> <OPTION>Один <OPTION>Два </SELECT> Выбрали индекс: <INPUT SIZE=1 maxlength=1> </FORM>

В этом примере обратите внимание на обработчики событий. Сам обработчик onChange мы опишем позже. Главное сейчас не это. Посмотрите, как мы обращаемся к элементам текущей формы. Во-первых, мы используем имя form. Оно указывает на объект Form, к которому принадлежит поле. Во-вторых, мы ссылаемся на второй элемент формы. На данный момент он не определен, но событие произойдет только тогда, когда мы будем выбирать вариант. К этому моменту поле уже будет определено. В-третьих, мы ссылаемся на selectedIndex, не указывая полного имени формы. В данном контексте он относится к текущей форме.



Списки и выпадающие меню


В данном случае речь пойдет о выпадающих меню в контексте форм, а не в контексте слоев и технологии CSS.

Одним из важных элементов интерфейса пользователя является меню. В HTML-формах для реализации меню используются поля типа select (контейнер SELECT, который, в свою очередь, вмещают в себя контейнеры OPTION). Эти поля представляют собой списки вариантов выбора. При этом список может "выпадать" или прокручиваться внутри окна. Поля типа select позволяют выбрать из списка только один вариант, либо отметить несколько вариантов. Для управления полями типа select в JavaScript существуют объекты Select и Option.

Эти объекты характеризуются следующими свойствами, методами и событиями:

Объект SelectСвойстваМетодыСобытия

formlengthnameoptions[]selectedIndex type

blur() click() focus()

onBlur onChange onFocus

Объект OptionСвойстваМетодыСобытия

defaultSelected index selected text selectedIndex value

нетнет

Мы не будем описывать все свойства, методы и события этих двух объектов. Остановимся только на типичных способах применения их комбинаций. Несмотря на то, что объект Option в нашей таблице находится ниже, что отражает его подчиненное по отношению к Select положение, начнем с описания его свойств и особенностей.



Submit()


Метод submit() позволяет проинициировать передачу введенных в форму данных на сервер. При этом методом submit() инициируется тот же процесс, что и нажатием на кнопку типа Submit. Это позволяет отложить выполнение передачи данных на сервер:

<FORM NAME=s METHOD=post ACTION="javascript:window.alert('Данные подтверждены');void(0);"> Введите цифру или букву:<INPUT SIZE=1 MAXLENGTH=1> </FORM> <A HREF="javascript:document.s.submit();">Отправить данные</A>

Вообще говоря, можно написать скрипт, который будет передавать данные без ведома пользователя, с помощью метода submit(). Однако браузер выдает предупреждение о таком поведении кода на странице.



Target


Свойство target определяет имя окна, в которое следует загружать результат обращения к CGI-скрипту. Применение значения этого свойства внутри JavaScript-программ не оправдано, так как всегда можно получить идентификатор окна или задействовать встроенный массив frames[0] и свойства окна opener, top, parent и т.п. Для загрузки внешнего файла в некоторое окно всегда можно применить метод window.open(). Но тем не менее использовать это свойство можно:

Пример 15.2.

(html, txt)

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



Текст в полях ввода


Поля ввода (контейнер INPUT типа TEXT) являются одним из наиболее популярных объектов программирования на JavaScript. Это объясняется тем, что, помимо использования по прямому назначению, их применяют и в целях отладки программ, вводя в эти поля промежуточные значения переменных и свойств объектов.

Пример 15.4.

(html, txt)

В данном примере первое поле формы — это поле ввода. Используя подстановку, мы присваиваем ему значение по умолчанию, а потом при помощи кнопки изменяем это значение.

Объект Text (текстовое поле ввода) характеризуется следующими свойствами, методами и событиями:

СвойстваМетодыСобытия

defaultValue form name type value

blur() focus() select()

onBlur onChange onFocus

Свойства объекта Text — это стандартный набор свойств поля формы. В полях ввода можно изменять только значение свойства value.

Обычно при программировании полей ввода решают две типовых задачи: защита поля от ввода данных пользователем и реакция на изменение значения поля ввода.



Text


Свойство text представляет собой отображаемый в меню текст, который соответствует альтернативе:

<SELECT onChange= "form.elements[2].value= form.elements[0].options [form.elements[0 ].selectedIndex].text;"> </SELECT>

В данном примере свойство text выводится в текстовое поле формы.



Защита поля ввода


Для защиты поля от ввода в него символов применяют метод blur() в сочетании с обработчиком события onFocus:

<FORM> <INPUT SIZE=10 VALUE="1-е значение" onFocus="document.forms[0].elements[0].blur();"> <INPUT TYPE=button VALUE=Change onClick="document.forms[0].elements[0].value= '2-е значение';"> <INPUT TYPE=reset VALUE=Reset> </FORM>

В этом примере значение поля ввода можно изменить, только нажав на кнопки Change и Reset. При попытке установить курсор в поле ввода он немедленно оттуда убирается, и таким образом, значение поля не может быть изменено пользователем.



Графика и таблицы


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

горизонтальные и вертикальные меню;вложенные меню; навигационные графические блоки.

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

<IMG SRC="image1.gif"><IMG SRC="image2.gif"><IMG SRC="image3.gif"><IMG SRC="image4.gif">


Рис. 16.1. 

Элементы переносятся на новую строку, так как ширина раздела меньше общей ширины всех картинок. Проблема решается, если применить защиту от парсера — <PRE>: (открыть)

<PRE> <IMG SRC="image1.gif"><IMG SRC="image2.gif"><IMG SRC="image3.gif"><IMG SRC="image4.gif"> </PRE>


Рис. 16.2. 

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

<PRE> <A HREF="javascript:void(0);"><IMG SRC="image1.gif"></A><A HREF="javascript:void(0);"><IMG SRC="image2.gif"></A><A HREF="javascript:void(0);"><IMG SRC="image3.gif"></A><A HREF="javascript:void(0);"><IMG SRC="image4.gif"></A> </PRE>


Рис. 16.3. 

Этого можно достичь за счет применения атрибута BORDER равного 0: (открыть)

<PRE> <A HREF="javascript:void(0);"><IMG SRC="image1.gif" BORDER="0"></A><A HREF="javascript:void(0);"><IMG SRC="image2.gif" BORDER="0"></A><A HREF="javascript:void(0);"><IMG SRC="image3.gif" BORDER="0"></A><A HREF="javascript:void(0);"><IMG SRC="image4.gif" BORDER="0"></A> </PRE>



Рис. 16.4. 

Теперь попробуем тем же способом реализовать многострочное меню: (открыть)

Пример 16.1.

(html, txt)


Рис. 16.5. 

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

Пример 16.2.

(html, txt)


Рис. 16.6. 

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


Изменение картинки


Изменить картинку можно, только присвоив свойству src встроенного объекта Image новое значение. На странице "Программирование графики" показано, как это делается в простейшем случае. Очевидно, что медленная перезагрузка картинки с сервера не позволяет реализовать быстрое листание. Попробуем решить эту проблему.

Собственно, решение заключается в разведении по времени подкачки картинки и ее отображения. Для этой цели используют конструктор объекта Image:

<TABLE> <TD> <A HREF="javascript:void(0)"; onMouseover="document.m0.src=color[0].src; return true;" onMouseout="document.m0.src=mono[0].src; return true;"> <IMG NAME=m0 SRC="images0.gif" border=0> </A> </TD> ... </TABLE>

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

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

color = new Array(32); mono = new Array(32); for(i=0;i<32;i++) { mono[i] = new Image(); color[i] = new Image(); if(i.toString().length==2) { mono[i].src = "images0"+i+".gif"; color[i].src = "images0"+i+".gif"; } else { mono[i].src = "images00"+i+".gif"; color[i].src = "images00"+i+".gif"; } }

Еще один характерный прием — применение функции отложенного исполнения JavaScript-кода (eval()):

function def() { for(i=0;i<32;i++) { eval("document.m"+i+".src=mono["+i+"].src"); } for(i=0;i<5;i++) { eval("document.r"+i+".src=rm["+i+"].src"); } }

В данном случае eval() избавляет нас от необходимости набирать операции присваивания (32 строки — это не фунт изюму).


Мультипликация


Естественным продолжением идеи замещения значения атрибута SRC в контейнере IMG является мультипликация, т.е. последовательное изменение значения этого атрибута во времени. Для реализации мультипликации используют метод объекта Window — setTimeout().

Собственно, существует два способа запуска мультипликации:

onLoad(); onClick(), onChange() ...

Наиболее популярный — setTimeout() при onLoad().



Объект Image


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

Программирование графики в JavaScript опирается на объект Image, который характеризуется следующими свойствами, методами и событиями:

СвойстваМетодыСобытия

bordercompleteheighthspacenamesrcvspacewidthlowsrc

нет

onAbortonErroronLoad

Несмотря на такое обилие свойств, их абсолютное большинство можно только читать, но не изменять. Об этом свидетельствует, прежде всего, отсутствие методов. Но два свойства все же можно изменять: src и lowsrc. Этого оказывается достаточно для множества эффектов с картинками.

Все объекты класса Image можно разделить на встроенные и порожденные программистом. Встроенные объекты — это картинки контейнеров IMG. Если эти картинки поименовать, к ним можно обращаться по имени:

<A HREF="javascript:void(0);" onClick="window.alert('Image name:'+ document.images[0].name)"> <IMG NAME=intuit SRC=images.gif BORDER=0> </A>

Картинка активна. Если на нее нажать, получим имя контейнера IMG. Обращение document.images[0].name позволяет распечатать это имя в окне предупреждения. При этом само имя указано как name=intuit в контейнере IMG.

К встроенному графическому объекту можно обратиться и по индексу:

document.images[0];

В данном случае images[0] — это первая картинка документа.



Оптимизация отображения


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

Из всех способов оптимизации отображения картинок мы остановимся только на нескольких:

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

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



Оптимизация при загрузке


Практически в любом руководстве по разработке HTML-страниц отмечается, что при использовании контейнера IMG в теле HTML-страницы следует указывать атрибуты WIDTH и HEIGHT. Это продиктовано порядком загрузки компонентов страницы с сервера и алгоритмом работы HTML-парсера. Первым загружается текст разметки. После этого парсер разбирает текст и начинает загрузку дополнительных компонентов, в том числе графики. При этом загрузка картинок, в зависимости от типа HTTP-протокола, может идти последовательно или параллельно.

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

Таким образом указание высоты и ширины картинки позволит отобразить документ раньше, чем картинки будут получены с сервера. Это дает пользователю возможность читать документ или задействовать его гипертекстовые ссылки до момента полной загрузки (событие load).

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



Предварительная загрузка


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

Реальный эффект можно почувствовать только при отключении кэширования страниц на стороне клиента (браузера). Кэширование часто используют для ускорения работы со страницами Web-узла. Как правило, загрузка первой страницы — это достаточно длительный процесс. Самое главное, чтобы пользователь в этот момент был готов немного подождать. Поэтому, кроме графики, необходимой только на первой странице, ему можно передать и графику, которая на ней не отображается. Но зато при переходе к другим страницам узла она будет отображаться без задержки на передачу с сервера.

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



Пример IMG SRC=tree.gif


<PRE> <IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0><A HREF="javascript:void(0);"><IMG SRC="image1.gif" WIDTH=103 HEIGHT=21 BORDER=0></A> <IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0><A HREF="javascript:void(0);"><IMG SRC="image2.gif" WIDTH=103 HEIGHT=21 BORDER=0></A> <IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0><A HREF="javascript:void(0);"><IMG SRC="image3.gif" WIDTH=103 HEIGHT=21 BORDER=0></A> <IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0><A HREF="javascript:void(0);"><IMG SRC="image4.gif" WIDTH=103 HEIGHT=21 BORDER=0></A> </PRE>
Пример 16.1.
Закрыть окно

<table CELLPADDING=0 CELLSPACING=0 ALIGN=center> <TR> <TD><IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0></TD> <TD><A HREF="javascript:void(0);"><IMG SRC="image1.gif" WIDTH=103 HEIGHT=21 BORDER=0></A></TD> </TR> <TR> <TD><IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0></TD> <TD><A HREF="javascript:void(0);"><IMG SRC="image2.gif" WIDTH=103 HEIGHT=21 BORDER=0></A></TD> </TR> <TR> <TD><IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0></TD> <TD><A HREF="javascript:void(0);"><IMG SRC="image3.gif" WIDTH=103 HEIGHT=21 BORDER=0></A></TD> </TR> <TR> <TD><IMG SRC=tree.gif WIDTH=27 HEIGHT=21 BORDER=0></TD> <TD><A HREF="javascript:void(0);"><IMG SRC="image14.gif" WIDTH=103 HEIGHT=21 BORDER=0></A></TD> </TR> </TABLE>
Пример 16.2.
Закрыть окно

<table CELLPADDING=0 CELLSPACING=0 ALIGN=center> <TR> <TD><IMG SRC=image.gif WIDTH=20 HEIGHT=20 BORDER=0></TD> <TD><IMG SRC=image1.gif WIDTH=20 HEIGHT=20 BORDER=0></TD> </TR> <TR> <TD><IMG SRC=image2.gif WIDTH=20 HEIGHT=20 BORDER=0></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.manual.src='image3.gif';return true;" onMouseout="document.manual.src='image4.gif'; return true;"> <IMG SRC=image5.gif BORDER=0 WIDTH=20 HEIGHT=20></A></TD> </TR> <TR> <TD><IMG SRC=image6.gif WIDTH=20 HEIGHT=20 BORDER=0></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.desk.src='image7.gif';return true;" onMouseout="document.desk.src='image8.gif';return true;"> <IMG SRC=image9.gif BORDER=0 WIDTH=20 HEIGHT=20></A></TD> </TR> </TABLE>
Пример 16.3.
Закрыть окно

<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" ALIGN="center"> <TR ALIGN="center"> <TD><IMG NAME="e0" SRC="empty.gif" WIDTH="15" HEIGHT="8" BORDER="0"></TD> <TD><IMG NAME="e1" SRC="empty.gif" WIDTH="15" HEIGHT="8" BORDER="0"></TD> <TD><IMG NAME="e2" SRC="empty.gif" WIDTH="15" HEIGHT="8" BORDER="0"></TD> <TD><IMG NAME="e3" SRC="empty.gif" WIDTH="15" HEIGHT="8" BORDER="0"></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.e0.src='arrowdw.gif';return true;" onMouseout="document.e0.src='empty.gif';return true;"> <IMG SRC="image1.gif" BORDER="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.e1.src='arrowdw.gif';return true;" onMouseout="document.e1.src='empty.gif';return true;"> <IMG SRC="image2.gif" BORDER="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.e2.src='arrowdw.gif';return true;" onMouseout="document.e2.src='empty.gif';return true;"> <IMG SRC="image3.gif" BORDER="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.e3.src='arrowdw.gif';return true;" onMouseout="document.e3.src='empty.gif';return true;"> <IMG SRC="image4.gif" BORDER="0"></A></TD> </TR> </TABLE>
Пример 16.4.
Закрыть окно

<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" ALIGN="center"> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente1.src='corner.gif';" onMouseout="document.evente1.src='clear.gif';"> <IMG SRC="image1.gif" border="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.evente1.src='corner.gif';" onMouseout="document.evente1.src='clear.gif';"> <IMG NAME="evente1" SRC="clear.gif" border="0"></A></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente2.src='corner.gif';" onMouseout="document.evente2.src='clear.gif';"> <IMG SRC="image2.gif" border="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.evente2.src='corner.gif';" onMouseout="document.evente2.src='clear.gif';"> <IMG NAME="evente2" SRC="clear.gif" border="0"></A></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente3.src='corner.gif';" onMouseout="document.evente3.src='clear.gif';"> <IMG SRC="image3.gif" border="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.evente3.src='corner.gif';" onMouseout="document.evente3.src='clear.gif';"> <IMG NAME="evente3" SRC="clear.gif" border="0"></A></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente4.src='corner.gif';" onMouseout="document.evente4.src='clear.gif';"> <IMG SRC="image4.gif" border="0"></A></TD> <TD> <A HREF="javascript:void(0);" onMouseover="document.evente4.src='corner.gif';" onMouseout="document.evente4.src='clear.gif';"> <IMG NAME="evente4" SRC="clear.gif" border="0"> </A></TD> </TR> </TABLE>
Пример 16.5.
Закрыть окно

<SCRIPT> function submenu(a) { if(a==1) { document.menu00.src="image1.gif"; // 1 (активна) document.menu10.src="image2.gif"; // 2 document.menu01.src="image3.gif"; // 1 пункт вложенного меню 1 document.menu02.src="image4.gif"; // 2 пункт вложенного меню 1 } if(a==2) { document.menu00.src="image1.gif"; // 2 document.menu10.src="image2.gif"; // 1 (активна) document.menu01.src="image3.gif"; // 1 пункт вложенного меню 2 document.menu02.src="image4.gif"; // 2 пункт вложенного меню 2 } } </SCRIPT>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" ALIGN="center"> <TR> <TD><A HREF="javascript:void(0);" onMouseover="submenu(1);return true;"> <IMG NAME=menu00 SRC=image1.gif BORDER=0></a></td> <TD><IMG NAME=menu01 SRC=image3.gif BORDER=0></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="submenu(2);return true;"> <IMG NAME=menu10 SRC=image2.gif BORDER=0></td> <TD><IMG NAME=menu02 SRC=image4.gif BORDER=0></TD> </TR> </TABLE>
Пример 16.6.
Закрыть окно

Событие onLoad()


Событие onLoad() наступает в момент окончания загрузки документа браузером. Обработчик события указывается в контейнере BODY:

... <BODY onLoad="JavaScript_code"> ...

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

function movie() { eval("document.images[0].src='clock"+ i+".gif';"); i++;if(i>6) i=0; setTimeout("movie();",500); } ... <BODY onLoad="movie();"> ...

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

function movie() { eval("document.images[0].src='clock"+ i+".gif';"); i++; if(i<7) { setTimeout("movie();",500);} } ... <BODY onLoad="movie();">

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

Скрипт получает управление при событии onLoad().Заменяет картинку. Порождает новый скрипт и откладывает его исполнение на 500 миллисекунд.Текущий скрипт уничтожается JavaScript-интерпретатором.

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



Src и lowsrc


Свойства src и lowsrc определяют URL изображения, которое монтируется внутрь документа. При этом lowsrc определяет временное изображение, обычно маленькое, которое отображается, пока загружается основное изображение, чей URL указывается в атрибуте SRC контейнера IMG. Свойство src принимает значение атрибута SRC контейнера IMG. Программист может изменять значения и src, и lowsrc. Рассмотрим пример с src:

document.i2.src="images2.gif";

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



Вертикальные и горизонтальные меню


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

Пример 16.4.

(html, txt)


Рис. 16.7. 

В данном случае стрелочка бежит точно над тем элементом, на который указывает мышь. По большому счету, применение атрибута ALT у IMG и его дублирование в строке статуса является гораздо более информативным, чем добавление нового графического элемента. Правда, отображается содержание ALT с некоторой задержкой: (открыть)


Рис. 16.8. 

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

Пример 16.5.

(html, txt)


Рис. 16.9. 

При движении мыши у соответствующего компонента, попавшего в фокус мыши, "отгибается уголок". В данном случае "уголок" — это самостоятельная картинка. Все уголки реализованы в правой колонке таблицы. Для того чтобы гипертекстовая ссылка срабатывала по обеим картинкам (тексту и "уголку"), применяются одинаковые контейнеры A, охватывающие графические образы. В этом решении есть один недостаток: при переходе от текста к "уголку" последний "подмигивает". Картинки можно разместить и в одной ячейке таблицы, но тогда нужно задать ее ширину, иначе при изменении размеров окна браузера картинки могут "съехать". Чтобы убрать "подмигивание", необходимо сделать полноценные картинки замены.

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


Пример 16.4.


Рис. 16.7. 

В данном случае стрелочка бежит точно над тем элементом, на который указывает мышь. По большому счету, применение атрибута ALT у IMG и его дублирование в строке статуса является гораздо более информативным, чем добавление нового графического элемента. Правда, отображается содержание ALT с некоторой задержкой: (открыть)


Рис. 16.8. 

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

<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" ALIGN="center"> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente1.src='corner.gif';" onMouseout="document.evente1.src='clear.gif';"> <IMG SRC="image1.gif" border="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.evente1.src='corner.gif';" onMouseout="document.evente1.src='clear.gif';"> <IMG NAME="evente1" SRC="clear.gif" border="0"></A></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente2.src='corner.gif';" onMouseout="document.evente2.src='clear.gif';"> <IMG SRC="image2.gif" border="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.evente2.src='corner.gif';" onMouseout="document.evente2.src='clear.gif';"> <IMG NAME="evente2" SRC="clear.gif" border="0"></A></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente3.src='corner.gif';" onMouseout="document.evente3.src='clear.gif';"> <IMG SRC="image3.gif" border="0"></A></TD> <TD><A HREF="javascript:void(0);" onMouseover="document.evente3.src='corner.gif';" onMouseout="document.evente3.src='clear.gif';"> <IMG NAME="evente3" SRC="clear.gif" border="0"></A></TD> </TR> <TR> <TD><A HREF="javascript:void(0);" onMouseover="document.evente4.src='corner.gif';" onMouseout="document.evente4.src='clear.gif';"> <IMG SRC="image4.gif" border="0"></A></TD> <TD> <A HREF="javascript:void(0);" onMouseover="document.evente4.src='corner.gif';" onMouseout="document.evente4.src='clear.gif';"> <IMG NAME="evente4" SRC="clear.gif" border="0"> </A></TD> </TR> </TABLE>



Пример 16.5.


Рис. 16.9. 

При движении мыши у соответствующего компонента, попавшего в фокус мыши, "отгибается уголок". В данном случае "уголок" — это самостоятельная картинка. Все уголки реализованы в правой колонке таблицы. Для того чтобы гипертекстовая ссылка срабатывала по обеим картинкам (тексту и "уголку"), применяются одинаковые контейнеры A, охватывающие графические образы. В этом решении есть один недостаток: при переходе от текста к "уголку" последний "подмигивает". Картинки можно разместить и в одной ячейке таблицы, но тогда нужно задать ее ширину, иначе при изменении размеров окна браузера картинки могут "съехать". Чтобы убрать "подмигивание", необходимо сделать полноценные картинки замены.

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


Запуск и остановка мультипликации


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

var flag1=0; function movie() { if(flag1==0) { eval("document.images[0].src='clock"+ i+".gif';"); i++;if(i>6) i=0; } setTimeout("movie();",500); } ... <BODY onLoad="movie();"> ... <FORM> <INPUT TYPE=button VALUE="Start/Stop" onClick="if(flag1==0) flag1=1; else flag1=0;"> </FORM>

В данном случае мы просто обходим изменение картинки, но при этом не прекращаем порождение потока. Если мы поместим setTimeout() внутрь конструкции if(), то после нажатия на кнопку Start/Stop поток порождаться не будет, и запустить движение будет нельзя.

Существует еще один способ решения проблемы остановки и старта мультипликации. Он основан на применении метода clearTimeout(). Внешне все выглядит по-прежнему, но процесс идет совсем по-другому:

var flag1=0; var id1; function movie() { eval("document.images[0].src='clock"+ i+".gif';"); i++;if(i>6) i=0; id1 = setTimeout("movie();",500); } ... <BODY onLoad="movie();"> ... <FORM> <INPUT TYPE=button VALUE="Start/Stop" onClick="if(flag1==0) { id1=setTimeout('movie();',500); flag1=1;} else {clearTimeout(id1); flag1=0;};"> </FORM>

Обратите внимание на два изменения. Во-первых, объявлен и используется идентификатор потока (id1); во-вторых, применяется метод clearTimeout(), которому, собственно, идентификатор потока и передается в качестве аргумента. Чтобы остановить воспроизведение функции movie() достаточно "убить" поток.



Массивы встроенных гипертекстовых ссылок


К встроенным гипертекстовым ссылкам относятся собственно ссылки (<A HREF=...>...</A>) и ссылки "чувствительных" графических картинок. Они составляют встроенный массив гипертекстовых ссылок документа (document.links[]).

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

for(i=0;i<document.links.length;i++) document.write(document.links[i].href+"<BR>");

Список ссылок:

http://www.intuit.ru/help/index.html http://www.intuit.ru/help/terms.html http://www.intuit.ru/help/shop.html

Вставим в документ контейнер MAP:

<MAP NAME=test> <AREA SHAPE=rect COORDS="0,0,0,0" HREF="javascript:window.alert('Area_Link_1');void(0);"> <AREA SHAPE=rect COORDS="0,0,0,0" HREF="javascript:window.alert('Area_Link_2');void(0);"> </MAP>

И снова распечатаем массив ссылок:

links[0]:http://www.intuit.ru/help/index.html links[1]:http://www.intuit.ru/help/terms.html links[2]:http://www.intuit.ru/help/shop.html links[3]:javascript:window.alert('Area_Link_1');void(0); links[4]:javascript:window.alert('Area_Link_2');void(0);

Две новые ссылки — это ссылки из контейнера MAP, который не отображается, но ссылки из него попадают в массив встроенных ссылок. При этом, как в нашем случае, они могут попасть между обычными гипертекстовыми ссылками, если контейнер MAP расположить внутри текста документа. На данной странице он помещен перед контейнером SCRIPT, в котором мы распечатываем массив встроенных ссылок.



Объект URL


Объект класса URL обладает свойствами, которые определены схемой URL. В качестве примера рассмотрим ссылку на применение атрибута SRC в контейнере IMG:

http://intuit.ru/help/index.html Значения свойств

href:

http://intuit.ru/help/index.html

protocol:

http:

hostname:

intuit.ru

host:

intuit.ru:80

port:

80

pathname:

help/index.html

search:

 

hash:

 

Обращение к свойству объекта класса URL выглядит как:

имя_объект_класса_URL.свойство

Например, так:

document.links[0].href document.location.host document.links[2].hash

Свойства объекта URL дают программисту возможность менять только часть URL – объекта (гипертекстовой ссылки, например). Наиболее интересно это выглядит в объекте Location , когда при изменении свойства происходит перезагрузка документа. Однако и при работе с обычными гипертекстовыми ссылками такая технология более предпочтительна, чем изменение всего URL целиком.

Здесь следует заметить, что чаще всего все-таки меняют весь URL. Это связано с тем, что такое действие более понятно с точки зрения HTML-разметки. Ведь у контейнера A нет атрибута PROTOCOL, но зато есть атрибут HREF.



Обработка событий Mouseover и Mouseout


Эти два события из всех событий, которые обрабатываются на страницах Web, используются чаще всего. Именно они позволяют обесцвечивать и проявлять картинки, а также менять содержание поля status. Первое событие генерируется браузером, если курсор мыши указывает на гипертекстовую ссылку, а второе — когда он покидает гипертекстовую ссылку. Рассмотрим пример с записной книжкой, но только для проявления меню второго уровня будем использовать обработчик события onMouseover:

<table CELLSPACING=1 CELLPADDING=0 ALIGN=center> <TR> <TD> <A HREF="javascript:void(0);" onMouseover="line2(0);return true;"> <IMG SRC=addrpho.gif BORDER=0></A></TD> ... </TR> </TABLE>

В качестве обработчика события мы вызываем функцию line2(), которая идентична line1() из предыдущего примера. В примере IMG перенесен на новую строку для наглядности. На самом деле так поступать не следует — при интерпретации HTML-парсером могут появиться неучтенные пропуски, которые не предусмотрены автором страницы. (открыть)


Рис. 17.2. 

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

<A HREF="javascript:void(0);" onMouseover="document.pic1.src='image2.gif'; return true;" onMouseout="document.pic1.src='image.gif'; return true;"> <IMG NAME=pic1 src=image.gif BORDER=0></A>

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



Обработка события click


Вообще говоря, обработчик события click в современном JavaScript не нужен. Можно прекрасно обойтись URL-схемой javascript, которая была специально придумана для перехвата события гипертекстового перехода. Обработчик onClick следует рассматривать как реликт, доставшийся нам в наследство от предыдущих версий языка, который поддерживается в версиях Netscape Navigator и Internet Explorer.

Основная задача обработчика данного события — перехват события гипертекстового перехода. Если функция обработки данного события возвращает значение true, то переход происходит, при значении false — не происходит:

Отменим переход в начало страницы описания события обработчика onClick:

<A HREF=#click onClick= "window.alert('Нет перехода на #click'); return false;">onClick</A>

А теперь дадим пользователю право выбора перехода в начало страницы посредством окна подтверждения:

<A HREF=#top onClick= "return window.confirm( 'Перейти в начало страницы?');"> переход в начало страницы</A>

Обратите внимание на место применения функции window.confirm() — аргумент команды return. Логика проста: функция возвращает значение true или false, и именно оно подставляется в качестве аргумента. Если просто написать функцию без return, то ничего работать не будет.

Можно ли вообще обойтись одним обработчиком onClick без использования атрибута HREF? Видимо, нет. Первое, что необходимо браузеру — это определение типа контейнера A. Если в нем есть только атрибут NAME, то это якорь, если присутствует атрибут HREF — ссылка. Это два разных объекта. Они имеют различные составляющие, в том числе и обработчики событий. В контексте текущего раздела нам нужна именно ссылка, т.е. контейнер A с атрибутом HREF. Проверим наше предположение:

<A ID=red onClick="window.alert("тест");return false;"> Нет атрибута HREF </A>

Текст "Нет атрибута HREF" — это якорь. Обработчик на нем не работает, так как на него нельзя указать мышью.

<A HREF="" id=red onClick="window.alert('URL:'+this.href); return false;"> Нет атрибута HREF </A>

Теперь мы указали пустую ссылку (см. поле статуса). Содержание окна — это база URL.



Схема URL- "javascript:..."


Для программирования гипертекстовых переходов в спецификацию универсального идентификатора ресурсов (URL) разработчики JavaScript ввели отдельную схему по аналогии со схемами http, ftp и т.п. — javascript. Эта схема URL упоминается в разделе "Размещение JavaScript-кода" в контексте передачи управления JavaScript-интерпретатору от HTML-парсера. Кроме того, о программировании гипертекстового перехода рассказано в разделе "Обработка события click". Теперь мы рассмотрим более общий случай обработки события гипертекстового перехода при выборе гипертекстовой ссылки.

Схема URL javascript в общем виде выглядит следующим образом:

<A HREF="javascript:...;">...</A> <FORM ACTION="javascript:...">

Одним словом, в любом месте, где мы используем URL, вместо любой из стандартных схем можно применить схему javascript. Единственное исключение составляет контейнер IMG. URL в нем используется в атрибуте SRC. Принять определенное значение SRC может при помощи либо назначения в IMG, либо обращения к свойству IMG. По большому счету, применение JavaScript в SRC может только проинициализировать картинку. Дальнейшее ее изменение описано в разделе "Программируем графику". Рассмотрим пример простой гипертекстовой ссылки:

<A HREF="javascript:window.alert( 'window.alert() изменяет HREF');void(0);"> Заменили обычную ссылку </A>

Можно выполнить аналогичную операцию, но над картинкой:

var flag=0; function ichange() { if(flag==0) { document.i1.src="image1.gif"; flag=1; } else { document.i1.src="image2.gif"; flag=0; } } ... <A HREF="javascript:ichange();void(0);"> <IMG NAME=i1 SRC=image2.gif BORDER=0> </A>

Попробуем теперь выполнить JavaScript-код применительно к контейнеру FORM:

<FORM NAME=f action="javascript:window.alert( document.f.fi0.value);void(0);" METHOD=post>Введите текст для отображения в окне и нажмите ввод: <INPUT NAME=fi0 SIZE=20 MAXLENGTH=20> </FORM>



Замена атрибута HREF


В разделе "Программируем ссылки" мы перечислили свойства объекта класса Link. Теперь покажем, как при помощи JavaScript-кода можно ими управлять. Рассмотрим меню типа "записная книжка": (открыть)


Рис. 17.1. 

Конечно, это не настоящая "записная книжка". Поле формы заполняется только при выборе гипертекстовой ссылки, расположенной над этим полем. Единственная цель данного примера — показать, как изменяется значение атрибута HREF (оно отображается в поле status окна браузера). Изменение производится посредством вызова функции:

Пример 17.1.

(html, txt)

В данном случае мы работаем с тремя элементами массива встроенных гипертекстовых ссылок: all['lo0'], all['lo1'] и all['lo2']. У каждого из них при вызове функции со значением аргумента a, равным 0, 1 и 2, соответственно, изменяем значение свойства href. Это свойство мы меняем целиком. URL можно менять и частично.



Break


Оператор break позволяет досрочно покинуть тело цикла. Распечатаем только title документа:

for(v in window.document) if(v=="title") { document.write(v+":"+eval('document.'+v)+" "); break; }

Результат исполнения:

title:Web-engineering (Introduction to the JavaScript. Operators.).

В пример распечатки свойств объекта   document мы вставили break при просмотре свойства title и получили искомый результат.



Continue


Того же результата, что и при использовании break, можно было бы достичь при помощи оператора continue:

for(v in window.document) { if(v!="title") continue; document.write(v+":"+eval('document.'+v)); break; }

Результат исполнения:

title:Web-engineering (Introduction to the JavaScript. Operators.)

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



For


Оператор for — это еще один оператор цикла. В общем случае он имеет вид:

for (инициализация переменных цикла; условие; модификация переменных цикла) оператор;

Оператор в теле цикла может быть блоком. Рассмотрим типичный пример использования этого оператора:

for(i=0;i<document.links.length;i++) { document.write(document.links[i].href+"<BR>"); } http://intuit.ru/help/index.html http://intuit.ru/help/shop.html#choice http://intuit.ru/help/payment.html

Подобные примеры разбросаны по всем разделам курса.



For ... in


Данный оператор позволяет "пробежаться" по свойствам объекта. Рассмотрим пример:

for(v in window.document) { document.write(v+"<BR>"); }

Все свойства текущего объекта "документ":

Пример 18.4.

(html, txt)

Данный документ состоит из нескольких разделов. Обращение "window.document" позволяет обратиться к общему документу, а не к отдельно взятому разделу. Поэтому распечатанные свойства — это свойства всего документа.



Функции


Язык программирования не может обойтись без механизма многократного использования кода программы. Такой механизм обеспечивается процедурами или функциями. В JavaScript функция выступает в качестве одного из основных типов данных. Одновременно с этим в JavaScript определен объект   Function.

В общем случае любой объект JavaScript определяется через функцию. Для создания объекта используется конструктор, который в свою очередь вводится через Function. Таким образом, с функциями в JavaScript связаны следующие ключевые вопросы:

функция — тип данных;

функция — объект; конструкторы объектов.

Именно эти вопросы мы и рассмотрим в данном разделе.



Функция — объект


У любого типа данных JavaScript существует объектовая "обертка" — Wrapper, которая позволяет применять методы типов данных к переменным и литералам, а также получать значения их свойств. Например, длина строки символов определяется свойством length. Аналогичная "обертка" есть и у функций — объект   Function.

Например, увидеть значение функции можно не только при помощи метода valueOf(), но и используя метод toString():

function f_name(x,y) { return x-y; } document.write(f_name.toString()+"<br>");

Результат распечатки:

function f_name(x,y) { return x-y; }

Свойства функции доступны для программиста только тогда, когда они вызываются внутри функции. При этом обычно программисты имеют дело с массивом аргументов функции (arguments[]), его длиной (length), именем функции, вызвавшей данную функцию (caller)и прототипом (prototype).

Рассмотрим пример использования списка аргументов функции и его длину:

function my_sort() { a = new Array(my_sort.arguments.length); for(i=0;i<my_sort.arguments.length;i++) a[i] = my_sort.arguments[i]; b = a.sort(); return b; } b = my_sort(9,5,7,3,2) for(i=0;i<b.length;i++) document.write("b["+i+"]="+b[i]+"<br>");

Результат исполнения:

b[0]=2 b[1]=3 b[2]=5 b[3]=7 b[4]=9

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

function slave() { document.write(slave.caller+""); return slave.caller; } function master1() { slave(); } function master2() { slave(); } ... master1(); master2();

Результат исполнения двух последних строк:

function master1() { slave(); } function master2() { slave(); }

Еще одним свойством объекта   Function является prototype, но это общее свойство всех объектов, поэтому и обсуждать его мы будем в контексте типа данных   Object. Упомянем только о конструкторе объекта   Function:

f = new Function(arg_1,...,arg_n, body)

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



Функция — тип данных


Определяют функцию при помощи ключевого слова function:

function f_name(arg1,arg2,...) { /* function body */ }

Здесь следует обратить внимание на следующие моменты. Во-первых, function определяет переменную f_name. Эта переменная имеет тип "function":

document.write("Тип переменной f_name:"+ typeof(f_name));

Тип переменной f_name: function. Во-вторых, этой переменной присваивается значение:

document.write("Значение i:"+i.valueOf()); document.write("Значение f_name:"+ f_name.valueOf());

Значение переменной f_name: 10. Значение переменной f_name:function f_name(a) { if(a>=0) return true; else return false; }. В данном случае метод valueOf() применяется как к числовой переменной i, так и к f_name. По этой причине функции можно назначить синоним путем присваивания ее значения другой переменной:

function f_name(a) { if(a>=0) return true; else return false; } document.write("Значение переменной f_name:"+ f_name(1)+""); b = f_name; document.write("Значение переменной b:"+ b(1)+""); Значение переменной f_name:true Значение переменной b:true

Очевидно, что если функцию можно присвоить переменной, то ее можно передать и в качестве аргумента другой функции. Все это усиливается при использовании функции eval(), которая позволяет реализовать отложенное исполнение JavaScript-кода. Отложенное исполнение — это возможность изменения программы по ходу ее исполнения. Типичным использованием eval() является сокращение кода за счет генерации однотипных строк:

for(i=0;i<5;i++) { eval("document.write('test"+i+"<br>')"); }

Результат исполнения кода:

test0 test1 test2 test3 test4

При непосредственном кодировании пришлось бы написать пять строк кода. Данный подход использовался в разделе "Изменение картинки" для инициирования массивов картинок, имена которых построены по принципу индексации ячеек таблицы.



If


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

if (логическое выражение) оператор1; [else оператор2;]

Логическое выражение — это выражение, которое принимает значение true или false. Если оно равно true, то оператор 1 исполняется. В квадратных скобках необязательная составляющая оператора if — альтернатива основной ветви вычислений:

if (navigator.appName=="Netscape") { window.location.href= "http://intuit.ru/netscape.htm"; } else { window.location.href= "http://intuit.ru/explorer.htm"; }

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



Код во внешнем файле


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

<HTML> <HEAD> ... <SCRIPT LANGUAGE=JavaScript SRC="../css/jsc.pgm"> </SCRIPT> ... </HEAD> <BODY onLoad="jump();"> ... </BODY> </HTML>

Контейнер SCRIPT определяет внешний файл размещения скриптов. Функция jump() расположена именно в этом файле. Она анализирует ссылку на данный документ, и если в ней есть компонент hash(#), то она продергивает файл до якоря, указанного в hash. Чтобы в этом убедиться, перейдите по любой внутренней ссылке, например, из меню разбивки раздела на подразделы, а после этого перезагрузите документ по Ctrl+R. Сначала документ будет загружен, а потом прокручен до указанного якоря.



Литералы


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

числовой литерал: 10 числовой литерал: 2.310 числовой литерал: 2.3e+2 строковый литерал: 'Это строковый литерал' строковый литерал: "Это строковый литерал"

Литералы используются в операциях присваивания значений переменным или в операциях сравнения:

var a=10; var str = 'Строка'; if(x=='test') window.alert(x);

Два варианта строковых литералов необходимы для того, чтобы использовать вложенные строковые литералы. Вообще говоря, есть подозрение, что равноправие "..." и '...' мнимое. Если внимательно посмотреть на реализацию страниц программирования гипертекстовых ссылок (href.htm, path.htm и mouse.htm), можно заметить, что вместо прямого переназначения гипертекстовой ссылки литералом типа '...' там используется косвенное переназначение через функцию литералом "...":

... function line(a) { ... window.document.main.document.links[4].href= "javascript:data(0);void(0);"; ... } ... <A HREF="javascript:line(0);void(0);"> <IMG SRC=image.gif BORDER=0> </A>

вместо:

<A HREF="javascript: window.document.main.document.links[4].href= 'javascript:data(0);void(0);';void(0);"> <IMG SRC=image.gif BORDER=0> </A>

Это связано с особенностями реализации Netscape. Дело в том, что прямое переназначение неправильно отображает кириллицу в win32, а вот косвенное работает. Похоже, что "..." разрешает анализ информации внутри строкового литерала JavaScript-интерпретатором, а '...' — нет.

Если быть более точным, то следует сказать, что строка — это объект. У этого объекта существует великое множество методов. Строчный литерал и строчный объект — далеко не одно и то же. При применении к строчным литералам методов строчных объектов происходит преобразование первых в последние.



Массивы


Массивы делятся на встроенные (document.links[], document.images[],...) и определяемые пользователем (автором документа). Встроенные массивы мы подробно обсуждаем в разделах "Программируем картинки", "Программируем формы" и "Программируем гипертекстовые переходы". Поэтому подробно остановимся на массивах, определяемых пользователем. Для массивов задано несколько методов:

join();

reverse();

sort();

и свойство length, которое позволяет получить число элементов массива. Это свойство активно используется в примерах данного раздела. В частности, при обсуждении метода join().

Для определения массива пользователя существует специальный конструктор:

a = new Array(); b = new Array(10); c = new Array(10,"Это значение");

Пример использования:

<SCRIPT> c = new Array(30,"Это значение"); </SCRIPT> <FORM><INPUT SIZE=& {c[0];}; value=& {c[1];}; onFocus="this.blur();"> </FORM>

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

Для работы с массивами в JavaScript применяются методы join(), reverse(), sort(). Кроме того, массивы обладают свойством длины, length.



Метод join()


Метод join() позволяет объединить элементы массива в одну строку. Он является обратной функцией методу split(), который применяется к объектам типа STRING. Рассмотрим пример преобразования локального URL в URL схемы http:

window.location: http://intuit.ru/help/index.html

Выполнили:

b = window.location.href.split('/');

Получили массив b:

b[0]=http: b[1]= b[2]=intuit.ru b[3]=help b[4]=index.html

Заменили схему и вставили "host:port":

for(i=0;i<b.length;i++) { if(b[i]=="file:") b[i]="http:/"; if(b[i]=="c%7C") b[i]="remote.host.domain:80"; }

Получили массив b:

b[0]=http: b[1]= b[2]=intuit.ru b[3]=help b[4]=index.html

Слили элементы массива b:

l=b.join("/");

Получили в результате:

http://intuit.ru/help/index.html

Другой пример использования метода join() — замена символа в строке:

str = "document.img1.src= 'http://images/imagе1.gif';" document.write(str);

Исходная строка:

document.img1.src='http://images/imagе1.gif';

Заменяем в строке все единицы на двойки:

b = str.split('1'); str = b.join('2');

Получаем следующий результат:

document.img2.src='http://images/imagе2.gif';

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



Метод reverse()


Метод reverse() применяется для изменения на противоположный порядка элементов массива внутри массива. Предположим, массив натуральных чисел упорядочен по возрастанию:

a = new Array(1,2,3,4,5);

Упорядочим его по убыванию:

a.reverse(); a[0]=5 a[1]=4 a[2]=3 a[3]=2 a[4]=1

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



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


Object — это объект и, следовательно, у него могут быть методы. Таких методов мы рассмотрим три: toString(), valueOf() и assign().

Метод toString() осуществляет преобразование объекта в строку символов. Он используется в JavaScript-программах повсеместно, но неявно. Например, при выводе числа или строковых объектов. Интересно применение toString() к функциям:

document.write(prot.toString()+"<BR>");

Результат исполнения:

function prot() { a = this.src.split(':'); protocol = a[0]+":"; return protocol; }

Здесь мы используем функцию   prot() из примера с прототипом. Если распечатать таким же образом объект   Image, то получим следующее:

картинка:[object]

Таким образом, далеко не всегда метод toString() возвращает строковый эквивалент содержания объекта. Он может просто вернуть его тип. Internet Ехplorer при этом возвращает "Object", в то время как Netscape Navigator — "object Image".

Аналогично ведет себя и метод valueOf(). Этот метод позволяет получить значение объекта. В большинстве случаев он работает подобно методу toString(), особенно если нужно выводить значение на страницу:

document.write(prot.valueOf()+"<BR>");

Результат исполнения:

function prot() { a = this.src.split(':'); protocol = a[0]+":"; return protocol; }

Как видим, результат тот же, что и в методе toString().

В отличие от двух предыдущих методов, assign() позволяет не прочитать, а переназначить свойства и методы объекта. Данный метод используется в контексте присваивания объекту некоторого значения:

object = value; <=> object.assign(value);

Рассмотрим пример:

c = new Image(); c.src = "image1.gif"; b =new Image(); b.src = "image2.gif"; Image.prototype.assign = new Function("a","this.src = a.src;"); ... <A HREF='javascript:window.document.main. document.i2.assign(c); void(0);'>Покрасить картинку</A>

В данном примере мы применяем метод assign() не внутри JavaScript-кода, а в обычной HTML-разметке. При этом перед использованием метода мы переопределили его своей функцией. Пользователей Internet Explorer следует предупредить, что здесь мы их немножко обманываем, так как переназначить прототип Image в Internet Explorer мы в данном случае не можем.



Модель безопасности


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

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

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

По умолчанию к защищенным в JavaScript данным относятся:

ОбъектСвойства
Documentcookie, domain, forms[], lastModified, links[], location, referer, title, URL
Formaction
document.forms [].elements[]checked, defaultChecked, defaultValue, name, selectedIndex, toString, value
Historycurrent, next, previous, toString(), all array elements
Location, Link, Areahash, host, hostname, href, pathname, port, protocol, search, toString()
OptiondefaultSelected, selected, text, value
WindowdefaultStatus, status

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

В настоящее время известны три модели защиты: запрет на доступ (Navigator 2.0), taint model (Navigator 3.0), защита через Java (Navigator 4.0). Применение моделей и соответствующие приемы программирования — это отдельный сложный вопрос, требующий знаний и навыков программирования на языке Java, поэтому в рамках данного курса мы его рассматривать не будем.

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



Невидимый фрейм


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

<FRAMESET COLS="100%,*"> <FRAME NAME=left SRC=hcfl.htm> <FRAME NAME=right SRC=hcfl.htm> </FRAMESET>

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

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



Невидимый код


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

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

Мы будем рассматривать возможность использования скрытого кода без выдачи вердиктов о преимуществе того или иного подхода. Рассмотрим несколько вариантов:

невидимый фрейм; код во внешнем файле;

обмен данными посредством встроенной графики.

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



Объекты


Объект — это главный тип данных JavaScript. Любой другой тип данных имеет объектовую "обертку" — Wrapper. Это означает, что прежде чем можно будет получить доступ к значению переменной того или иного типа, происходит конвертирование переменной в объект, и только после этого выполняются действия над значением. Тип данных   Object сам определяет объекты.

В данном разделе мы остановимся на трех основных моментах:

понятие объекта;

прототип объекта; методы объекта   Object.

Мы не будем очень подробно вникать во все эти моменты, так как при программировании на стороне браузера чаще всего обходятся встроенными средствами JavaScript. Но поскольку все эти средства — объекты, нам нужно понимать, с чем мы имеем дело.



Обмен данными посредством встроенной графики


Данный прием основан на двух идеях: возможности подкачки графического образа без перезагрузки страницы и возможности подкачки этого графического образа не через указание URL графического файла, а через CGI-скрипт, который возвращает Content-type: image/... или осуществляет перенаправление.

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

... function change_image(x) { s = "http://intuit.ru/cgi-bin/image_script?"+ document.cookie; document.x.src= s; ... <a HREF="javascript:change_image(i); void(0);"> <IMG NAME=i SRC=image1.gif> </A>

Эта безобидная последовательность операторов JavaScript позволит нам узнать получил ли клиент cookie. "Волшебные ключики" могут не поддерживаться по разным причинам. В данном случае программа передает на сервер выставленные им "ключики" в качестве параметра скрипта под видом изменения картинки.



Операторы языка


В этом разделе будут рассмотрены операторы JavaScript. Основное внимание при этом мы уделим операторам декларирования и управления потоком вычислений. Без них не может быть написана ни одна JavaScript-программа.

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

var;

{...};

if;

while;

for;

for ... in;

break;

continue;

return.

Сразу оговоримся, что этот список неполный.



Понятие объекта


Сначала рассмотрим пример произвольного, определенного пользователем объекта, потом выясним, что же это такое:

function Rectangle(a,b,c,d) { this.x0 = a; this.y0 = b; this.x1 = c; this.y1 = d; this.area = new Function( "return Math.abs(this.x0-this.x1)* Math.abs(this.y0-this.y1)"); this.perimeter = new Function( "return (Math.abs(this.x0-this.x1) + Math.abs(this.y0-this.y1))*2"); } c = new Rectangle(0,0,100,100); document.write(c.area());

Результат исполнения:

10000

Этот же пример использовался в разделе "Функции" для иллюстрации применения конструктора функции. Здесь мы рассмотрим его в более общем контексте.

Функция   rectangle() — это конструктор объекта класса Rectangle, который определяется пользователем. Конструктор позволяет создать реальный объект данного класса. Ведь функция — это не более чем описание некоторых действий. Для того чтобы эти действия были выполнены, необходимо передать функции управление. В нашем примере это делается при помощи оператора new . Он вызывает функцию и тем самым генерирует реальный объект.

Создается четыре переменных: x0, y0, x1, y1 — это свойства объекта   Rectangle. К ним можно получить доступ только в контексте объекта данного класса, например:

up_left_x = c.x0; up_left_y = c.y0;

Кроме свойств мы определили внутри конструктора два объекта типа Function(), применив встроенные конструкторы языка JavaScript, — area и perimeter. Это методы объекта данного класса. Вызвать эти функции можно только в контексте объекта класса Rectangle:

sq = c.area(); length = c.perimeter();

Таким образом, объект — это совокупность свойств и методов, доступ к которым можно получить, только создав при помощи конструктора объект данного класса и использовав его контекст.

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



Закрыть окно'


wid = window.open("","test","width=200,height=100,statusbar"); wid.document.open(); wid.document.write("<HTML><HEAD>"); wid.document.write("<SCRIPT>var t;</SCRIPT>"); wid.document.write("</HEAD><BODY>"); wid.document.write("<CENTER>Новое окно<BR>"); wid.document.write("<FORM>"); wid.document.write("<INPUT TYPE=button VALUE=' Закрыть окно' onClick=window.close();></FORM>"); wid.document.write("</CENTER></BODY></HTML>"); wid.document.close(); ... <A HREF="javascript:wid.t=window.prompt("Новое состояние:");wid.defaultStatus=t;wid.focus();void(0);>...</A>
Пример 18.1.
Закрыть окно

var flag= 0; var cid=null; function clock() { flag=1; d = new Date(); window.document.main.document.f0.fi1.value= d.getHours()+":"+d.getMinutes()+":"+d.getSeconds(); cid = setTimeout("clock();",10000); } function stop() { if(cid!=null) { clearTimeout(cid); cid=null; flag=0; } } function wo() { cid = window.open("","test","width=400,height=100"); cid.document.open(); cid.document.write("<HTML><HEAD></HEAD><BODY><CENTER>"); cid.document.write("<FORM><INPUT TYPE=button onClick='window.close();' value='Закрыть окно'></FORM></CENTER>"); cid.document.write("</BODY></HTML>"); cid.document.close(); cid.focus(); } ... <FORM NAME=f0> <INPUT NAME=fi1 SIZE=8 MAXLENGTH=8> <INPUT TYPE=button VALUE="Часы(start/stop)" onClick="if(flag==0)clock();else stop();"> <INPUT TYPE=button VALUE="Окно" onClick="wo();"> </FORM>
Пример 18.2.
Закрыть окно

function prot() { a = this.src.split(':'); protocol = a[0]+":"; return protocol; } function host() { a = this.src.split(':'); path = a[1].split('/'); return path[2]; } function pathname() { a = this.src.split(':'); path = a[1].split('/'); b = new array(); for(i=3;i<path.length;i++) b[i-3]="path[i];<br" c = b.join('/'); return "/"+c; } Image.prototype.protocol = prot; Image.prototype.host = host; Image.prototype.pathname = pathname; ... document.write("<IMG NAME=i1 SRC='image1.gif'><BR>"); document.write(document.i1.src+"<BR>"); document.write(document.i1.protocol()+"<BR>"); document.write(document.i1.host()+"<BR>"); document.write(document.i1.pathname()+"<BR>");
Пример 18.3.
Закрыть окно

namespaces:[object] lastModified:07/16/2002 21:22:53 onmousedown:null URLUnencoded:http://intuit.ru/help/index.html fileCreatedDate:07/16/2002 onbeforeeditfocus:null bgColor:#ffffff oncontextmenu:null onrowexit:null embeds:[object] scripts:[object] mimeType:HTML Document alinkColor:#0000ff onmousemove:null onselectstart:null oncontrolselect:null body:[object] protocol:HyperText Transfer Protocol onkeypress:null onrowenter:null vlinkColor:#800080 URL:http://intuit.ru/help/index.html onreadystatechange:null applets:[object] domain:intuit.ru fileModifiedDate:07/16/2002 onmouseover:null dir: media: defaultCharset:windows-1251 plugins:[object] ondragstart:null oncellchange:null cookie:hotlog=1; hotlog=1; b=b documentElement:[object] ondatasetcomplete:null nameProp:Web-engineering (Introduction to the JavaScript. Operators.) referrer:http://intuit.ru/help/index.html onrowsdelete:null onerrorupdate:null onselectionchange:null ondblclick:null onkeyup:null location:http://intuit.ru/help/index.html forms:[object] title:Web-engineering (Introduction to the JavaScript. Operators.) onrowsinserted:null onmouseup:null onkeydown:null fgColor:#000080 ondatasetchanged:null onmouseout:null parentWindow:[object] fileUpdatedDate:01/01/1601 onpropertychange:null onstop:null onhelp:null linkColor:#0000ff images:[object] readyState:interactive frames:[object] all:[object] onclick:null childNodes:[object] anchors:[object] selection:[object] onbeforeupdate:null security: This type of document does not have a security certificate. fileSize:15911 ondataavailable:null styleSheets:[object] activeElement:null links:[object] onafterupdate:null sea001:[object]onafterupdate:null down:[object]onafterupdate:null
Пример 18.4.
Закрыть окно

Прототип


Обычно мы имеем дело со встроенными объектами JavaScript. Собственно, все, что изложено в других разделах курса — это обращение к свойствам и методам встроенных объектов. В этом смысле интересно свойство объектов, которое носит название prototype. Прототип — это другое название конструктора объекта конкретного класса. Например, если мы хотим добавить метод к объекту класса String:

String.prototype.out = new Function("a", "a.write(this)"); ... "Привет!".out(document);

Результат исполнения:

Привет!

Для объявления нового метода для объектов класса String мы применили конструктор Function. Есть один существенный нюанс: новыми методами и свойствами будут обладать только те объекты, которые порождаются после изменения прототипа объекта. Все встроенные объекты создаются до того, как JavaScript-программа получит управление, что существенно ограничивает применение свойства prototype.

Тем не менее покажем, как можно добавить метод к встроенному в JavaScript классу объектов. Примером будет служить встроенный поименованный Image. Задача состоит в том, чтобы разобрать URL картинки таким же образом, как и URL объекта класса Link:

Пример 18.3.

(html, txt)

Основная идея заключается в том, чтобы переопределить конструктор раньше, чем он будет использован. HTML-парсер разбирает HTML и создает встроенные объекты раньше, чем запускается JavaScript-интерпретатор. Из этого следует, что объект на странице нужно создать через JavaScript-код. В этом случае сначала происходит переопределение объекта   Image, а уже после этого создается встроенный объект данного класса. При работе с Internet Explorer все иначе. Если на свойство prototype у строкового объекта он не "ругается", то для Image такое свойство уже не определено.



Скрытая передача данных из форм


Обмен данными в Web-технологии подробно рассматривается в другой главе — "CGI и формы". Программирование элементов форм обсуждается в разделе "Программируем формы". В этом разделе мы рассмотрим вопрос о возможности передачи скрытых от пользователя данных.

Рассмотрим следующий пример. Нажмите на кнопку "Подписка" и посмотрите на строку location своего браузера. Вы обнаружите там два поля, которых нет в заполняемой вами форме: h1 и h2. Это уже неприятно, хотя сама информация в них отнюдь не криминальная (location.href и document.referer). Это означает, что в тексте страницы есть вызов функции со строками типа:

<SCRIPT> document.f.h1.value = window.location.href; document.f.h2.value = window.document.referer; </SCRIPT>

Теперь посмотрим другой пример. Если начать вводить данные в левом фрейме окна примера, то, как только вы переходите от поля к полю, в правом фрейме заполняются соответствующие поля. Cкрипт из правого фрейма читает данные из полей левого фрейма. В кодах это будет выглядеть примерно так:

function ask() { document.forms[0].elements[0].value= window.top.frames[0].document.forms[0 ].elements[0].value; document.forms[0].elements[1].value= window.top.frames[0].document.forms[0 ].elements[1].value; document.forms[0].elements[2].value= window.top.frames[0].document.forms[0 ].elements[2].value; document.forms[0].elements[3].value= window.top.frames[0].document.forms[0 ].elements[3].value; setTimeout("ask();",100); } ... <BODY onLoad="ask();" BGCOLOR=lightyellow TEXT=navy>

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

Еще один пример — отправка данных по событию без наличия какой-либо формы в документе вообще.

<FORM NAME=hf ACTION="javascript:window.alert('Готово'); void(0);" METHOD=post> <INPUT NAME=hfi TYPE=hidden> </FORM> <SCRIPT> document.hf.hfi.value = location.href; </SCRIPT> <A HREF="javascript:window.alert('Внимание'); void(0);" onClick="document.hf.submit();"> Нажми на ссылку </A>

Результат исполнения:

Нажми на ссылку

Согласно примеру при нажатии на гипертекстовую ссылку произойдет не только выдача сообщения, которое в этой ссылке указано, но и событие Submit для формы. В итоге вы получите два окна предупреждения. Но второе окно вы не заказывали!

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



Типы и структуры данных


Как и любой другой язык программирования, JavaScript поддерживает встроенные типы и структуры данных. Все их многообразие подразделяется на:

литералы и переменные; массивы, функции и объекты.

При этом все они делятся на встроенные и определяемые программистом. Функции и объекты рассматриваются в разделах "Функции" и "Объекты". Поэтому здесь мы остановимся на литералах, переменных и массивах.



Управление фокусом


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

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

окнах; фреймах; полях формы.

Следует сразу заметить, что фреймы — это тоже объекты класса Window, и многие решения, разработанные для окон, справедливы и для фреймов.



Управление фокусом во фреймах


Фрейм — это такое же окно, как и само окно браузера. Точнее — это объект того же класса. К нему применимы те же методы, что и к обычному объекту "окно":

var flag=1; function clock() { if(flag==0) { d=new Date(); s=d.getHours()+':'+d.getMinutes()+':'+ d.getSeconds(); window.document.forms[0].elements[0].value=s; } setTimeout('clock();',100); } window.onblur = new Function('this.flag = 1;'); window.onfocus = new Function('this.flag = 0;'); window.onload = clock;

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



Управляем фокусом в окнах


Для управления фокусом у объекта класса "окно" существует два метода: focus() и blur(). Первый передает фокус в окно, в то время как второй фокус из окна убирает. Рассмотрим простой пример:

function hide_window() { wid=window.open("","test", "width=400,height=200"); wid.opener.focus(); wid.document.open(); ... wid.document.close(); }

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

Для того чтобы этого не происходило, нужно после открытия передавать фокус на новое окно:

function visible_window() { wid=window.open("","test", "width=400,height=200"); wid.focus(); wid.document.open(); ... wid.document.close(); }

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

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

Для реализации такого сценария достаточно использовать метод окна onblur(). Его можно также задать в контейнере BODY в качестве обработчика события onBlur, но в этом случае он видим пользователю. Мы воспользуемся этим методом "в лоб":

window.onblur = new Function("window.defaultStatus = 'Background started...';"); window.onfocus = new Function("window.defaultStatus = 'Document:Done';");

Обратите внимание на поле статуса браузера. Оно демонстрирует возможность выполнения функции в фоновом режиме. Кроме того, onblur() в этом виде не отрабатывает в Internet Explorer. Причина кроется в прототипе объекта и возможности его переназначения программистом.

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



Var


Оператор var служит для объявления переменной. При этом переменная может принимать значения любого из разрешенных типов данных. На практике довольно часто обходятся без явного использования var. Переменная соответствующего типа создается путем простого присваивания:

var a; var a=10; var a = new Array(); var a = new Image();

Все перечисленные выше примеры использования var верны и могут быть применены в JavaScript-программе. Область действия переменной определяется блоком (составным оператором), в котором используется переменная. Максимальная область действия переменной — страница.



While


Оператор while определяет цикл. Определяется он в общем случае следующим образом:

While (логическое выражение) оператор;

Оператор, в том числе и составной, — тело цикла. Тело исполняется до тех пор, пока верно логическое условие:

while (flag==0) { id=setTimeout ("test();",500); }

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