По ту сторону Веб-страницы

Сайт следит за вами: JavaScript

Очень часто на динамических сайтах нам не хватает динамики.

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

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

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

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

А может, удастся реализовать стандартные характеристики интер­фейсов операционных систем, когда объекты можно перетаскивать по рабочему пространству мышкой?

Наконец, не кажется ли вам, что перезагрузка страницы, нуж­ная всего лишь для того, чтобы отправить письмо разработчику, — это уже каменный век, и после щелчка по кнопке «Отправить» посетитель должен оставаться на той же странице, где и был, просто ему должно

Сайт следит за вами: JavaScript

Выдаваться сообщение, что письмо на самом деле отправлено? И пере­загрузка страницы по времени для обновления строк чата — это тоже не очень удобно.

Кроме того, хочется свежих веяний. Кто сказал, что переместить­ся на следующую страницу можно, только щелкнув по ссылке «Далее»? А не логичнее ли просто перетащить страницу влево, чтобы увидеть то, что справа? И почему, кстати, вообще сайт должен располагаться на раз­ных страницах, если на одну страницу можно динамически подгружать разную информацию.

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

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

Активные сценарии — терминология, принятая компанией Microsoft. Дело в том, что есть два известных языка активных сцена­риев, выполняющих сходные или даже идентичные задачи: JavaScript и VBScript. Однако второй из них поддерживается исключительно бра­узером Internet Explorer, тогда как второй является кроссбраузерным (для современных браузеров) и кроссплатформенным. Это значит, что JavaScript можно использовать в большинстве браузеров, тогда как сфе­ра применения VBScript существенно уже. Когда Internet Explorer зани­мал лидирующее положение на рынке браузеров, использовать VBScript имело смысл; сейчас же все большую популярность приобретают аль­тернативные браузеры, где из активных языков есть только поддержка JavaScript.

Несмотря на богатую функциональность, JavaScript не считает­ся языком программирования — в первую очередь потому, что он не по­зволяет писать отдельные программы, а способен только встраиваться в виде сценариев и фрагментов. В качестве серверного языка он исполь­зуется редко: иногда в качестве языка сценариев в составе технологии ASP (уступая пальму первенства языку VBScript). С другой стороны, среди остальных языков активных сценариев он признанный лидер как представитель клиентских технологий. Основные его возможности можно разделить на несколько групп:

1. Взаимодействие со структурой документа. В полной мере (с неко­торыми оговорками) такая возможность реализована в браузерах Internet Explorer версий 5 и позднейших, Firefox, Netscape с вер­сии 6, Opera с версии 7, в Safari и других современных браузерах.

JS

4

Программирование

Речь идет в первую очередь о работе с DOM — объектной моделью документа, когда с помощью активных сценариев можно не толь­ко изменять текстовое и иное содержимое отдельных элементов, но также добавлять и удалять сами объекты. При этом объекты могут быть не только элементами веб-страниц, но и «абстрактны­ми» объектами. Кроме того, с помощью DOM и языка JavaScript можно обращаться к свойствам объектов — считывать (проверять) и изменять их. В функциональных возможностях языка в версиях браузеров, чья популярность предшествовала периоду поддержки DOM, было хорошо реализовано обращение к формам и их ком­понентам, а также динамическое изменение содержимого неко­торых тэгов. Это, пожалуй, самая важная возможность языка.

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

3. Реакция на действия посетителя страницы и иные события. Лю­бое движение мышью, нажатие и отпускание кнопки мыши или клавиши на клавиатуре, изменение размеров окна, загрузка и уход со страницы могут быть отслежены с помощью сценариев на JavaScript. К событию можно «привязать» функцию, вызов како­го-либо другого действия.

4. Работа с браузером. С помощью JavaScript можно работать не только с событиями, происходящими в пределах окна браузера, но и с самим браузером: например, открывать новые окна, со­ставляя «на лету» набор панелей, которые будут показаны в но­вом окне браузера; доставлять посетителю диалоговые окна и окна сообщений; влиять на некоторые компоненты окна браузе­ра. Поскольку определенная часть таких эффектов работает толь­ко в Internet Explorer, а в книге делается упор на кроссбраузерный подход, то рассмотрены будут только те сценарии, которые будут хорошо работать в большинстве современных популярных брау­зеров.

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

Сценарии на языке JavaScript могут занимать длинные файлы (в этом случае говорят о целых библиотеках), а могут состоять и из од­ной строчки — все зависит от конкретной задачи.

326

Сайт следит за вами: JavaScript

Существует три способа включения активных сценариев на языке JavaScript на веб-страницах.

Во-первых, это способ отдельных файлов. Создается текстовый файл, ему присваивается произвольное имя (допустим, scriptl) и рас­ширение. js (а не. txt или какое-то иное, потому что именно с этим расширением файлы сценариев можно связывать с веб-страницами). А в нужном месте кода веб-страницы включение происходит следую­щей директивой:

<script language="JavaScript" src="script1.js"> </script>

Если наступят времена, когда браузеры будут поддерживать язык HTML версии 5 в его XHTML-разновидности, то такие директивы бу­дут звучать лаконичнее:

<script src="script1.js"/>

Однако сейчас такая форма записи пока не работает. Точнее, ра­ботает в отдельных браузерах, но некорректно. Например, в Opera 9 такая конструкция вызывает многочисленные ошибки отображения остального содержимого страницы, а в Firefox вызывает задержку по­явления на веб-странице содержимого, которое следует после такого вызова сценария. В Internet Explorer такой вызов игнорируется и не позволяет выводить на экран содержимое страницы, которое следовало в коде после вызова.

Во-вторых, работает метод прямой записи сценария в коде веб­страницы. В этом случае код нужно оформить тэгами <script> ... </script>:

<script language="JavaScript">

// Текст сценария

// Двойным слэшем в начале строки оформляются

// однострочные комментарии

</script>

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

Наконец, третий способ включения сценариев не требует тэга <script> вообще. Этот способ связан только с обработкой событий. В нужный тэг включается обработчик, в качестве значения (содержимо­го) которого выступает сценарий. Приведем простой пример. Допустим,

Во избежание торопливых действий со стороны посетителя страницы на время загрузки страницы наверху страницы располагается предупреж­дение: «Идет загрузка». После загрузки страницы это предупреждение убирается. Сначала мы должны написать само предупреждение:

<div id="preload а1е^">Идет 3arpy3Ka</div>

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

<style>

#preload alert { background-color:#CC0000; color:white; font-weight:bold; padding:5px; position:absolute; left:30px; top:30px; z-Index:100; width:17 5px; text-align:center;

}

</style>

Наконец, чтобы убрать этот блок со страницы, по окончании за­грузки документа в браузер, в тэге <body> пишем небольшой сценарий:

<body onLoad="document. getElementById('preload alert'). style. display='none'">

Это, пожалуй, все. Разберем сценарий подробнее. Обработ­чик события load (загрузка) называется onLoad — он помещается в тэг в виде параметра. Значением этого параметра является сценарий. В данном случае сценарий присваивает блоку «preload_alert» через CSS такое значение свойства display, при котором блок становится неви­димым и не занимает места. Обращение к свойству идет поступенчато, сверху вниз. Сначала сценарий обращается к документу; затем — к оп­ределенному блоку (нужному нам) внутри этого документа, но обра­щается посредством нахождения в документе этого элемента по его идентификатору id (document. getElementById('идентификатор элемента') — работает во всех современных браузерах, не работало в Internet Explorer и Netscape четвертых версий). Обратившись к эле-

Сайт следит за вами: JavaScript

Менту документа, сценарий читает его стили (style), после чего обра­щается к свойству display стиля элемента документа («Вот дом, ко­торый построил Джек...»). Наконец, обратившись к свойству, сценарий присваивает (привычный знак равенства, как и в PHP, является опера­тором присваивания, а в качестве знака равенства выступает двойной знак равенства «==») ему значение «none» — оно обязательно должно быть заключено в кавычки.

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

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

Со свойствами мы уже познакомились, однако в качестве свойств могут выступать не только стили CSS, но и параметры тэгов. Напри­мер, чтобы изменить изображение наведением на нее мыши, нужно с помощью сценария изменить содержимое параметра src тэга <img>, используя обработчики событий наведение (mouseover) и уведения (mouseout) мыши с изображения:

<img src="1.jpg" alt="" onMouseOver="this. src='2.jpg'" onMouseOut="this. src='1.jpg'">

Изначально должны быть подготовлены два изображения: 1.jpg и 2.jpg. В качестве изначального изображения в документе загружает­ся первое. Поскольку обработчики события вставлены в тэг, с которым эти события и происходят, можно использовать слово-указатель this [англ. «это(т)»] — он указывает на текущий элемент страницы. Если бы событие происходило не при наведении на само изображение, то при­шлось бы использовать доступ по идентификатору. Традиционно в тэге изображения использовался не идентификатор id, а параметр name — «имя». Поэтому нужно было написать тэг:

<img name="picture1" src="1.jpg" alt="">

И обращаться к нему так (допустим, изменение изображения происхо­дит при щелчке на кнопке):

<button

OnClick="document. picture. src='2.jpg'">

Нажмите<button>

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

<button onClick="document. images['picture']. src='2.jpg'">Нажмите<button>

И даже эта:

<button onClick="document. images. picture. src='2.jpg'">Нажмите<button>

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

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

<a href="" onClick="document. images. picture. src='2.jpg'; return false">Нажмите</a>

Видно, что появились слова «return false» после точки с запя­той в сценарии, обрабатываемом событием. Они предотвращают пере­ход по ссылке, возвращая (return) действию ссылки ложное (false) значение, тогда как сценарий выполняется нормально.

Кроме свойств существуют еще и методы.

Метод — это та же функция, только зарезервированная языком. Например, при показе динамически появляющейся на странице по­исковой формы (вы уже можете это сделать) логично было бы сделать форму еще более услужливой и в строку запроса уже поставить мигаю­щий курсор, чтобы посетителю осталось только набрать с клавиатуры поисковый запрос и нажать клавишу «Enter» («Return»). Допустим, у нас есть форма, имя (name) которой мы дали «myform», а внутри нее поле для ввода поискового запроса, которая называется «search_item». Кроме того, что это название поля передаст в принимающий сценарий содержимое поискового запроса в переменную $search_item, оно выполнит еще одно полезное действие. После вызова самой формы мы напишем в сценарии:

Сайт следит за вами: JavaScript

<script>

// Предыдущий фрагмент сценария document. myform. search item. focus()

</script>

Метод focus() поставит курсор в указанное сценарием поле. Поскольку формы (даже если форма на странице только одна) при за­грузке страницы также создают массив, то данный сценарий мог бы быть записан и так:

<script>

// Предыдущий фрагмент сценария document. forms['myform'].search item. focus() </script>

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

Обратите внимание: в конце строк в сценариях JavaScript мож­но ставить точку с запятой (как в PHP), а можно и не ставить, если это не препятствует корректному выполнению сценария. А препятствовать это может лишь тогда, когда сценарий из нескольких команд записан в одну строку. Такие сценарии часто помещаются в тэги при обработке событий.

В качестве примера можно привести такой: в строке запроса поис­ковой формы светло-серыми буквами написаны слова «Поиск по базе»; нужно, чтобы при щелчке по строке эти слова убирались, а при наборе других слов они уже были черного цвета, но при потере фокуса даже эти слова становились бы светло-серыми.

<form method="post" action="">

<input type="text" name="fld good" value=" Поиск по базе" style="color:#DDDDDD" onFocus="this. style. color='black'; if(this. value==' Поиск по базе') {this. value='';}" onBlur="this. style. color='#DDDDDD'; if(this. value==false) {this. value=' Поиск по базе';}"> <input type="submit" value="Найти">

</form>

Переведем на русский язык. По событию приобретения строкой запроса фокуса (onFocus) CSS-параметр цвет данного (this) элемента становится черным, после чего идет проверка: если значение (value) текстового поля равно « Поиск по базе» (именно так, с пробелом в начале), то это значение изменяется на пустую строку ( '' ). По собы­тию потери фокуса (onBlur) текст снова становится серым, а значе-

Ние текстового поля, если оно пустое, становится таким: « Поиск по базе». Если же строка не пустая, а в нее введен поисковый запрос, то условие не выполняется, и замены текста не происходит. Разные коман­ды разделяются точкой с запятой, что и требовалось доказать.

Работа с формами очень часто возлагается как раз на JavaScript. Дело в том, что часто посетители веб-страниц, заполняющие какие - либо формы, забывают заполнить некоторые поля. Проверить, пустое ли поле, серверным сценарием очень просто ^^@$переменная_ из_имени_поля==false) ), но зачем заставлять посетителя ждать, если проверить можно сразу, не загружая странице-обработчик?

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

<form name="letter" method="post" action="/mail/" onSubmit="return checkFeedback()">

<р>Ваш e-mail:

<br><input type="text" name="youremail" value="" style="width:300px"></p>

<р>Тема письма:

<br><input type="text" name="yoursubj" style="width:300px" value=""></p>

<р>Текст письма:

<br><textarea name="yourletter" rows="8" cols="25" style="width:300px">

</textarea></p>

<p><input type="submit" value="OTnpabMTb"></p>

</form>

Обратите внимание на имена формы и всех трех обязательных полей, которые должен заполнить посетитель (отправитель пись­ма). Кроме того, в открывающем тэге формы есть такой фрагмент: onSubmit="return checkFeedback()". Он сообщает о том, что по событию отправки (например, нажатию кнопки «Отправить») воз­вращается результат выполнения функции checkFeedback() — я на­писал ее, чтобы она проверяла, все ли поля заполнены. Напишем чуть выше такой сценарий, состоящий из функции checkFeedback() :

<script language="JavaScript">

Function checkFeedback()

Сайт следит за вами: JavaScript

{

If(document. letter. youremail. value==false)

{

А1егМ"Напишите адрес вашей электронной почты, пожалуйста!");

Document. letter. youremail. focus(); return false;

}

Else if(document. letter. yoursubj. value==false)

{

If(confirm("Bbi не создали тему сообщения!

Оставить без темы?"))

{

Document. letter. yoursubj. value="Без темы

(с сайта)";

}

Else

{

Document. letter. yoursubj. focus();

}

Return false;

}

Else if(document. letter. yourletter. value==false)

{

А1егМ"Будьте так добры, напишите хотя бы пару строк, не отправляйте пустым!");

Document. letter. yourletter. focus(); return false;

}

}

</script>

Сначала проверяется поле для ввода электронного адреса. Если оно пустое или его значение составляют пробельные символы (if(document. letter. youremail. value==false) ), то посетите­лю выдается предупреждение (а1егМ"Текст предупреждения")), в поле ставится курсор (document. letter. youremail. focus()), а отправка отменяется (return false). То же самое происходит и с двумя остальными полями (по очереди), только проверка поля для ввода темы снабжена дополнительной функциональностью: если поле пустое, то посетителю выдается не просто предупреждение (alert()), а диа­логовое окно соглашения (confirm()) с кнопками «Да» и «Нет». Если

Посетитель соглашается оставить письмо без темы, то в поле для ввода темы помещаются слова «Без темы (с сайта)», если не соглашается (else), то в поле ввода темы просто ставится курсор.

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

Обращение к элементам не всегда производится через иден­тификационную запись или имя. Достаточно просто самому создать массив из элементов определенного типа и обращаться к ним по оче­реди. В этом случае обращение производится по имени тэга, но пос­редством выбора элементов из массива. Массив создается функцией getElementsByTagName() , в качестве аргумента в которую переда­ется имя тэга. Например, иногда возникает необходимость обратиться ко всему содержимому элемента <body>, то есть считать все содержи­мое страницы, чтобы обработать его сценарием. Применяются два вида записи, первая из которых полная:

Body elem = document. getElementsByTagName("body")

Body content = body elem[0].innerHTML

Второй вид — более краткий:

Body content = document. getElementsByTagName("body") [0].innerHTML

Различия важны. В первом случае сначала создается перемен­ная body_elem, значением которой становится массив всех элементов с именем тэга <body> — то, что элемент всего один (и к тому же по про­граммистской традиции носит номер «0»), сейчас несущественно, пос­кольку такой метод может быть применен к любой коллекции элементов. И только после этого создается переменная body_content, значением которого является содержимое (innerHTML) нулевого (по-русски — пер­вого) элемента массива body_elem. Во втором случае промежуточной переменной не создается, а поскольку запись document. getElement sByTagName ( "body" ) инициализирует массив элементов <body>, то к ней сразу можно обращаться, выбирая ее элементы по номерам. Пер­вый вариант (с разделением кода на фрагменты) может пригодиться, ког­да с массивом элементов нужно сделать еще что-то впоследствии. Если же преследуется только одна задача, то логичнее написать сценарий в сокращенной форме. В данном случае они оба работоспособны и в ко­нечном итоге записывают в переменную body_content содержимое тэга <body>. (Правда, с этим тэгом связана одна особенность. Все сов­ременные браузеры для настольных компьютеров и ноутбуков более или менее лояльно относятся к ошибкам верстальщика в разметке страницы.

Сайт следит за вами: JavaScript

Иногда они проявляют чрезмерное усердие и оказывают медвежьи ус­луги. Например, в Internet Explorer, даже если расположить вышепри­веденный сценарий после закрывающего </body>, браузер почитает, что кодер ошибся, и включит в содержимое переменной body content и сам сценарий, что, конечно, никому не нужно.)

С помощью инструкции innerHTML можно, разумеется, не толь­ко считывать содержимое элементов, но и изменять его:

<H1>Hello</H1>

<button onClick="document. getElementsByTagName ('h1')[0].innerHTML='Привет'"> Перевести </button>

Неважно, в каком регистре написан тэг <h1> — таким методом можно обратиться к его содержимому и присвоить новое. При нажатии (onClick) на кнопку (<button>) в данном сценарии происходит по­добная замена содержимого.

Такая методика (с использованием innerHTML) очень часто ис­пользуется для динамического изменения содержимого целых текс­товых блоков, абзацев и фрагментов текста. Часто требуется создавать динамически меняющиеся ссылки. Если ссылка звучит как «Показать настройки», а при щелчке по ней показывается блок настроек, то ссыл­ка должна быть заменена на «Убрать настройки», потому что они уже показаны. После скрытия настроек ссылка должна приобрести перво­начальный вид.

<a href="" onClick="return false"><span onClick="this. innerHTML=(this. innerHTML=='noKa3aTb настройки') ? 'Убрать настройки' : 'Показать настройки'">Показать настройки<^рап></а>

Разберем конструкцию подробнее, поскольку она включает проверку условия, сокращенную форму условного ветвления и замену содержимого. Для простоты я убрал из ссылки обращение к функции, поскольку сейчас это не играет существенной роли. Кроме ссылки, сло­ва «Показать настройки» заключены в тэг <span>...</span> — именно содержимое этого блока будет меняться (это обеспечит корректную ра­боту во всех браузерах, поддерживающих JavaScript). Сокращенная за­пись условного ветвления имеет такой формат:

Переменная или свойство объекта объект =

(условие) ? "результат, если условие истинно" : "результат, если условие ложно"

Или, чтобы выразить это абстрактно, можно записать так:

Abc = (bcd == 3) ? "result 1” : "result 2”

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

If(bcd == 3)

{

Abc = "result 1”

}

Else

{

Abc = "result 2"

}

Первая форма явно компактнее, но она не позволяет исполь­зовать множественное ветвление и оператор else if (аналогичный elseif в PHP и elif в SSI). Результат в данном случае один и тот же: если переменная bcd (ее обязательно нужно инициализировать ранее, чтобы не возникло ошибки) равна трем, переменная abc будет содер­жать строку «result 1», если же не равна, — строку «result 2».

В данном случае результатом проверки условия будет присвоение значения не переменной, а свойству innerHTML (внутренний HTML) ко­дового слова this (оно обозначает текущий <span>). Условием является проверка на соответствие this. innerHTML строке «Показать настрой­ки» (соответствие, или равенство, обозначается знаком ==). Если соот­ветствие правильно, в качестве содержимому <span>^ назначается стро­ка «Убрать настройки», в противном случае возвращается на место строка «Показать настройки». Заменять таким образом можно содержимое любо­го блока, содержащего текст, например, обращаясь к нему по идентифи­катору. В качестве примера можно привести такую ситуацию: существует блок для подсказок. При наведении на ссылки с непонятными словами в этот блок записывается содержимое подсказки, хранящееся в перемен­ной, элементе массива или где-то еще. При отведении курсора мышки в блок записывается пустая строка, чтобы подсказка не оставалась:

<div id="info"></div>

<a href="ccbmKa" onMouseOver="document. getElementById('info').innerHTML='TeKCT подсказки, соответствующий тематике ссылки'" onMouseOut^'document. getElementByld^info'). innerHTML=' '">

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

Сайт следит за вами: JavaScript

Функция определяется так же, как и в PHP: после кодового слова function приводится название функции, при необходимости в скобках приводятся аргументы (параметры, передаваемые в функцию). После этого в фигурных скобках описывается функция:

Function infotext(text)

{

Document. getElementById("info").innerHTML = text }

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

<a href="ccbmKa" onMouseOver="infotext('TeKCT подсказки, соответствующий тематике ссылки')" onMouse Out="infotext(,,)">

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

Обратите внимание, что переменные в JavaScript не предваряются знаком доллара, как в PHP, но при инициализации переменной можно предварить имя переменной кодовым словом var:

Var abc = "";

Но это необязательно, как и кавычки в концах строк.

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

Function opCl(elem id)

{

Document. getElementById(elem id).style. display = (document. getElementById(elem id).style. display=="block") ? "none" : "block";

}

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

<a href="" опС^ск="орС1('идентификатор открываемого элемента'); return false"> Текст ссылки </a>

Хорошим правилом является оставлять элементам ту функци­ональность, на которую они рассчитаны. Например, ссылка является средством перехода на другую страницу, и использовать ее для активи­зации сценария, по логике вещей, нежелательно. Дело в том, что при щелчке по таким ссылкам не происходит никакого перехода, хотя в ста­тусной строке или всплывающей подсказке появляется адрес. Компро­миссным вариантом может быть создание специального стиля для ссы­лок, отвечающих только за активизацию сценария. Идеологии HTML это противоречит, но внешние отличия бросятся в глаза. Оптимальным вариантом является использование фрагмента <span> с назначенным классом, описанным в стилевой таблице. Как дополнительный плюс — не требуется усложнения «ссылки» словами «return false»:

<span class="activator"

ОпС1іск="орС1('идентификатор открываемого

Элемента')">Текст «ссылки»<^рап>

Сам же класс для таких «ссылок» следует описать так, чтобы он максимально походил на обычную ссылку (то есть вызывал рефлектор­ное желание нажать), но имел отличия. Например, сейчас принято де­лать прерывистое подчеркивание при сохранении остальных атрибутов ссылок:

.activator { color:blue;

Border-bottom:1px dashed blue; cursor:pointer;

}

Наконец, желательно снабжать такие псевдоссылки всплываю­щими подписями (title=""), чтобы посетитель, подведя мышь к под­черкнутому тексту и увидев характерный «ссылочный» курсор в виде перста указующего, не впал в панику, не обнаружив адреса ссылки.

Определенным минусом такого подхода является то, что пользо­ватель, отключив JavaScript в браузере (таких пользователей, по некото­рым данным, около 10%), не сможет воспользоваться псевдоссылками. С одной стороны, он сам же и ограничивает себя в просмотре сайта, и, отключив JavaScript, не сможет воспользоваться в принципе никакими эффектами и удобствами, которые реализованы на сайте с помощью ак­тивных сценариев; с другой же — не хочется сужать его возможности. Например, он не может воспользоваться формой для входа на сайт (пос­ле заполнения полей логина и пароля), потому что она, предположим, выезжает из-за границы документа — а это, скорее всего, реализовано именно на JavaScript — но можно предоставить ему доступ к странице

Сайт следит за вами: JavaScript

Login. php, на которой те же поля доступны без всяких сценариев. Тут как раз пригодится компромиссный вариант, но несколько модернизи­рованный:

<a href="login. php" class="pseudolink" onClick="opCl('login form'); return false" ^^е="Форма для ввода логина и пароля">

Войти на сайт</а>

Здесь собрано все вместе: и ссылка, и сценарий, открывающий форму с id="login form" и свойством style="display:block", и указание класса, модифицирующего вид ссылки, и всплывающее пояснение. При включенных активных сценариях форма будет откры­ваться на данной странице (при условии, что она существует) по щелчку на этой ссылке, а при отключенном JavaScript не будут работать не толь­ко сценарии, но и инструкция return false, которая запрещает пе­реход по ссылке, а значит, загрузится страница для тех пользователей, что отключают сценарии. Наконец-то все довольны.

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

Например, простая функция alert() позволяет активировать окошко предупреждения. Оно не является элементом веб-страницы, и браузер нельзя закрыть, пока не будет нажата кнопка «ОК» на этом окошке. Этим пользуются для создания простейших взломов, напри­мер, гостевых книг, где любой посетитель сайта может оставить свое со­общение. Если веб-мастер не позаботился о предварительной обработ­ке сообщений перед их помещением на сервер, то в сообщении можно оставить вредоносный код. Например, такой:

<script language="JavaScript"> var i = 1 while(i==1) {

Alert("y веб-мастера кривые руки") }

</script>

Суть сценария простая. Сначала инициализируется переменная i, которой присваивается значение «1». Затем начинается цикл while, который, как известно, повторяется, пока условие, заключенное в скоб­ках, истинно. Поскольку никакой другой сценарий не поменяет значе­ние переменной (во время выполнения этого кода не будут выполняться другие сценарии), условие вечно будет истинным, а значит, постоянно будет выскакивать окошко с обидной надписью. Пока окошко актив-

Но, нельзя закрыть браузер, нажать ссылку и уйти со страницы иным способом, а также зайти в меню, чтобы отключить активные сценарии. Единственный выход — принудительно завершение работы браузера по Ctrl+Alt+Delete, а это завершит работу не только открытого окна, но и всех остальных окон этого браузера, что неприятно, если их было открыто больше десятка. И если Opera после аварийного завершения работы программы восстанавливает все окна, то другие браузеры тако­го не умеют. И такие неприятности причинил сценарий в три строчки! Остерегайтесь таких глупых взломов, используйте серверную проверку и замену скобок тэгов на подстановки &lt; и &gt;.

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

Кроме этого, JavaScript позволяет управлять самим окном браузе­ра. Есть функции для записи своего текста в статусной строке (window. status='TeKCT'), есть функции для динамического изменения окна браузера и т. п., но ими не стоит злоупотреблять, чтобы пользователь не терял контроль над теми интерфейсными элементами, над которыми он должен иметь контроль.

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

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

<script language="JavaScript">

Function owind(obj)

{

Left one = (screen. width-660)/2

Window. open(obj, "inf", config='width=660, height= 34 0,top=150,left='+left one+',toolbar=0,scrollbars=1')

}

<script>

Как следует из параметров функции, новое окно будет фикси­рованной ширины (width=660) и высоты (height=34 0), откроется на высоте 150 пикселей от верхней границы экрана или от меню брау-

Сайт следит за вами: JavaScript

Зера (в разных браузерах top=150 расценивается по-разному), панели инструментов у окна не будет (toolbar=0), а полосы прокрутки будут видны (scrollbars = 1). Поскольку в функции window. open нет па­раметра выравнивания по центру, приходится прибегать к хитрости. Бе­рется объект screen (экран), из его ширины (screen. width) вычи­тается ширина открываемого окна. Получается ширина пространства, которое остается по бокам нового окна. Эта ширина делится пополам, и получается нужное число — количество пикселей, которое будет от­ступом от левого края экрана. Для этих манипуляций пришлось ввес­ти переменную left one, которая в настройках вставляется не прос­то так, а конкатенируется с помощью плюсов (строка конфигурации, заключенная в кавычки, разрывается, чтобы можно было вставить не текст, а переменную).

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

Window. open("адрес страницы", "имя окна", сопйд='параметры открываемого окна')

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

<a href="project. html" target=" blank" onClick="owind('project. html'); return false">

О проекте</а>

В данном случае ссылка откроет новое окно в любом случае: с JavaScript или без него. Но в первом случае размер окна будет проконт­ролирован, а во втором — нет.

Выше был упомянут цикл while — его синтаксис понятен без объяснений. Он служит для повторения каких-либо операций, пока действует заданное условие. Как только условие прекращает действо­вать, прекращается и действие цикла. Приведем пример:

<script language="JavaScript"> now = new Date() seconds = now. getSeconds() while(seconds<30)

{

А1егМ"Щелкать в течение 30 секунд")

}

</script>

Сценарий представляет собой щадящую модификацию ранее приведенного сценария. Сначала вводится переменная (now), которая вызывает из памяти компьютера информацию о времени и дате с по­мощью функции Date() — строго говоря, мы инициализируем объект now, который содержит информацию о времени и дате. Новый объект инициализируется с помощью кодового слова new. (Например, чтобы инициализировать массив, нужно написать: имя_переменной = new Array() .)

В отличие от while, цикл for манипулирует числами. С его по­мощью очень удобно перебирать пронумерованные данные, например, элементы массива или переменные, содержащие в имени числа. При этом JavaScript позволяет изначально не знать длину массива, а опери­ровать свойством length, которое считает элементы.

Например, стоит такая задача: найти в документе все элементы <span> и сообщить им название класса, при этом выделить только один <span>, выявленный по идентификатору, и сообщить ему название дру­гого стиля. Таким образом, получится, что HTML-разметка в документе присутствует, а CSS — нет, а только назначается по сценарию.

<script language="JavaScript"> function span colorize(elem)

{

Spcol = document. getElementsByTagName("span") for(i=0; i<spcol. length; i++)

{

Spcol[i].className="spanlink"

}

Document. getElementById(elem).className = "spanunlink"

}

</script>

В переменную spcol заносится динамически генерируемый функцией getElementsByTagName() массив, состоящий из элемен­тов <span>. Количество этих элементов не важно: оно подсчитывается конструкцией spcol. length. Синтаксис параметров цикла таков:

1. i=0 обозначает, что нумерация начата с нуля. В цикле перемен­ная i будет обозначать номер элемента, увеличивающийся на еди­ницу при каждом новом прохождении цикла: во второй проход переменная i будет равна единице. Переменную можно назвать как угодно; i — это просто традиция.

2. i<spcol. length определяет последнее число, при котором бу­дет выполняться цикл. Длина массива всегда на единицу больше,

Сайт следит за вами: JavaScript

Чем номер его последнего элемента, потому что нумерация начи­нается с нуля. Например, если элементов четыре, то номер пос­леднего элемента — 3, а первого — 0. Поэтому на русский язык это можно перевести так: последнее число для прохождения цик­ла — то, которое на единицу меньше длины массива spcol; или так: цикл выполняется до тех пор, пока i меньше длины массива spcol.

3. Если бы последнего условия (i++) не было, то число, скрытое под переменной i, не менялось бы. Двойной плюс обозначает ин­кремент, то есть увеличение числа i на единицу. Если бы тут был двойной минус, то происходило бы уменьшение числа на едини­цу (декремент), но при этом первое число должно быть больше второго, иначе цикл не будет выполняться в принципе.

Далее при каждом проходе цикла берется очередной элемент мас­сива (spcol[i] — его номер скрыт под переменной i), а его свойс­тво — «имя класса» (className) — изменяется на «spanlink» (естес­твенно, имя класса произвольное, «spanlink» приведено в качестве примера). Таким образом, при завершении цикла у каждого элемен­та <span> имя класса теперь «spanlink». И после окончания цик­ла с помощью одной строчки document. getElementByld(elem). className = "spanunlink" элементу, идентификатор которого передан в функцию с помощью переменной elem, присваивается имя класса «spanunlink» — строго говоря, этому элементу присваивается сначала имя класса «spanlink» (в цикле), а потом «spanunlink» (от­дельной инструкцией), однако последние присвоение имеет, конечно, больший приоритет перед первым.

Использовать в цикле можно и статические массивы, и явно вы­водить номера тоже можно:

<script language="JavaScript">

Fruits = new Аггау("яблоко", "груша", "персик", "манго", "слива", "гранат")

For(f=0; f<fruits. length; f++)

{

Fnum = f+1

Document. write("<p>"+fnum+". "+fruits[i]+"</p>")

}

</script>

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

Обратите внимание на конструкцию document. write (). Она тоже является средством вывода данных на экран, но, в отличие

От свойства innerHTML, выводит данные не в тот блок, который ука­зан сценарию, а прямо в том месте, где располагается сценарий. Так, если сценарий расположен сразу после <body>, то вывод будет напе­чатан в начале страницы, поэтому пользуйтесь методом write ( ) с ос­торожностью.

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

Оператор for...in выполняет заданные действия для каждого свойства объекта или для каждого элемента массива. Он имеет вид:

For (переменная in выражение) оператор

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

<script language="JavaScript">

Var ob = {"а" : "Архангельск", "б" : "Баку", "в" :

"Воронеж"};

For (var key in ob)

Document. write(key + ": " + ob[key] + "<BR>");

</script>

На экране появится текст

А: Архангельск б: Баку в: Воронеж

Объекты, соответствующие ассоциативным массивам в PHP, со­здаются просто:

Var cat = {name : "Мурка", age : "3 года", color :

"серая"} ;

Обратиться к элементу объекта можно, например, так:

Alert("Имя кошки — "+cat. name)

Возможен и другой вариант:

Function Browser(name, version) {

Сайт следит за вами: JavaScript

This. name = name; this. version = version;

}

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

Var myBrowser = new Browser("Microsoft Internet Explorer", "5.5");

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

<script language="JavaScript"> sls = new Array(

"Деепричастный обормот",

"Киберквазиэкзистенциальная футуристическая псевдосущность",

"Кидальго (бесчестный испанский рыцарь, из словаря)",

"Не все так просто, как на самом деле",

"А есть еще язык бурушаски",

"Все страньше и страньше",

"После нас хоть потом",

"Шумерский букварь весом 34 кг",

"Новости будут к старости"

)

Now = new Date();

Num sls = (now. getSeconds())%sls. length; document. write(sls[num sls]);

</script>

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

Sls = new Array()

Sls[] = "Деепричастный обормот"

Sls[] = "Киберквазиэкзистенциальная футуристическая псевдосущность"

Sls[] = "Кидальго (бесчестный испанский рыцарь, из словаря)"

Sls[] = "Не все так просто, как на самом деле"

// Тут, допустим, комментарий

Sls[] = "А есть еще язык бурушаски"

Sls[] = "Все страньше и страньше"

Sls[] = "После нас хоть потом"

Sls[] = "Шумерский букварь весом 34 кг"

Sls[] = "Новости будут к старости"

Дальше начинается самое интересное. Чтобы реализовать псев­дослучайный выбор числа, возьмем из памяти компьютера (уже извес­тным способом) текущее количество секунд. Как вы уже догадались, полученное число можно использовать в качестве индекса для того, чтобы взять какой-то элемент из массива. Но число секунд в минуте явно больше длины массива, поэтому с помощью конструкции (now. getSeconds())%sls. length ограничиваем число секунд, которые могут применяться для этой цели, числом, соответствующим длине массива. Полученное число записывается в переменную num_sls, ко­торая и используется как индекс.

Операция a % b, строго говоря, возвращает остаток по модулю, то есть целый остаток от деления левого операнда на правый (причем плавающие числа перед операцией округляются до целых). Поскольку такой остаток (например, при делении 52 на 7 он будет равен трем, 55 на 7 — шести) всегда как минимум на единицу меньше второго операн­да (в данном случае длины массива), он как раз подходит в качестве чи­сел — случайных индексов для выбора элементов из массива.

Кроме секунд, объект Date() предоставляет доступ и к другим единицам измерения времени:

GetSeconds() — секунды getMinutes() — минуты getHours() — часы getDay() — день недели getDate() — число getMonth() — месяц

GetYear() — год (две цифры, в Opera обрабатывается неправильно)

GetFullYear() — год (4 цифры)

Каждая функция на выводе дает число, причем в случае с меся­цами и днями недели нумерация, конечно, начинается с нуля: это крест программистов, который они должны нести.

Сайт следит за вами: JavaScript

Старайтесь не просто использовать эти числа, а стараться, чтобы вывод был красивым. Сравните, что удобнее: 12.06.2007 или «12 июня 2007 года»? А ведь такого результата добиться совсем просто:

<script language="JavaScript"> abc = new Date() chislo = abc. getDate() mesiac = abc. getMonth() god = abc. getFullYear()

Mesiaca = new Аггау("января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря")

Document. write(chislo + mesiaca[mesiac] + god + " года")

</script>

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

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

Concat()

Объединяет два массива в один новый и возвращает его

Join(разделитель)

Объединяет все элементы массива в текстовую строку

Pop()

Удаляет последний элемент массива

Push(элемент 1, элемент 2, ...)

Добавляет элементы в конец массива

Reverse()

Изменяет порядок элементов массива на противоположный

Shift()

Удаляет первый элемент массива и возвращает его

Slice(начало, конец)

Извлекает часть массива и возвращает новый массив. Начало и конец — числа либо числовые выражения

4

Программирование

Sort()

Сортирует элементы массива

ToString()

Преобразует массив в строку, при этом разделителями между бывшими элементами массива является запятая

Unshift()

Добавляет элементы в начало массива

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

^агА^число)

Возвращает символ, находящийся в данной позиции строки. На­пример, вызов string. charAt(2) возвратит символ «а», если пере­менная string будет содержать в себе слово «красный».

IndexOf(подстрока)

Возвращает позицию первого вхождения заданной подстроки. Вызов document. write(string. indexOf("a") ) возвратит число 2.

LastIndexOf(подстрока)

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

Та^Мрегулярное выражение)

Сопоставляет строку с регулярным выражением (о регулярных выражениях — чуть позже).

Гер1асе(регулярное выражение, подстрока)

Сопоставляет строку с регулярным выражением и заменяет най­денную подстроку новой подстрокой. Действует как функция «Найти и заменить» в Windows-приложениях, но исключительно с использова­нием регулярных выражений.

Search(регулярное выражение)

Ищет сопоставление строки с регулярным выражением. Возвра­щает позицию первого вхождения подстроки.

Slice(начало, конец)

Извлекает часть строки и возвращает новую строку. В качест­ве аргументов в функцию передаются числа, обозначающие позиции относительно начала строки. Вызов document. write("ABCDEF".

348

Сайт следит за вами: JavaScript

Slice(2,-1)) выведет на экран обозревателя строку CDE. Отрица­тельные аргументы трактуются как смещения от конца строки.

Split(разделитель)

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

Substr(позиция [, длина])

Возвращает подстроку, заданную позицией и длиной. Вызов "abcdef".substr(1, 3) возвратит «bcd», поскольку чтение начина­ется с символа с индексом «1» (это символ «b»), после которого извлека­ется строка длиной 3 символа.

Substring(начало [,конец])

Возвращает подстроку, заданную начальной и конечной позици­ями. Если конечная позиция не задана, то возвращается подстрока, на­чиная с позиции начало и до конца исходной строки.

ToLowerCase()

Преобразует все буквы строки в строчные.

ToUpperCase()

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

Все эти функции обращаются к строкам в таком формате: стро- ка. функция(). В качестве строки удобнее всего использовать пере­менные.

Регулярные выражения используются в самых разных языках, и JavaScript не исключение. Их смысл в том, чтобы производить опера­ции с тем текстом, который заранее неизвестен. Чаще всего с ними ис­пользуется функция replace(). Сама замена может иметь два формата записи. Во-первых, в одну строку:

String = string. replace(/&/g,"&amp;")

Где string — это обычная строка (ее содержимым может быть и один символ, и нуль символов, и текст, и содержимое текстового поля, и все содержимое документа...). В качестве искомой строки в косые скобки заключен амперсанд, в качестве параметра — глобальный по­иск (g), который означает, что поиск заменит все вхождения подстроки, а в качестве результирующей строки дано мнемоническое представ­ление спецсимвола амперсанда. Все знаки «&» заменятся в документе на «&amp; ».

Во-вторых, используется такая запись, когда сложное регулярное выражение в первой записывается в переменную, а вторая строка про­изводит замену:

Para = "<p>"

Thispattern = new RegExp(para+"rn"+para, "g")

String = string. replace(thispattern, para)

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

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

Dashvar = "—"

Thispattern = new RegExp("([A ])"+dashvar, "g")

String = string. replace(thispattern,

"$1 "+dashvar)

Данный пример ищет отрезки текста, где перед тире забыли пос­тавить пробел, и расставляет эти пробелы. Регулярное выражение [А ] говорит о том, что ищется любой символ, кроме (А) пробела, оно заклю­чено в скобки, чтобы этот символ, каким бы он ни был, можно было вер­нуть на место в результирующей строке в виде сочетания $1 (это не пе­ременная, а так называемый «карман», куда было записано содержимое найденной регулярным выражением подстроки). Всего может быть до девяти таких последовательностей, заключенных в круглые скобки, и их содержимое возвращается с помощью последовательностей от $1 до $9. Последовательность [А ] + отвечала бы за любое количество не­пробельных символов больше нуля, а [А ]* — за любое количество непробельных символов, включая нуль. Любой символ обозначается точкой. Чтобы использовать в регулярном выражении точку, ее надо эк­ранировать обратным слэшем:

String = string. replace(/T.K./g,"T.&nbsp;K.")

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

Хороший и лаконичный справочник по регулярным выражениям располагается по адресу: Http://Wdh.Suncloud.Ru/Js05.Htm.

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

Сайт следит за вами: JavaScript

Различными тэгами. Естественно, в коде не происходит никаких изме­нений — после действия сценария они отображаются на веб-странице. Среди таких функций — link() , bold() , italic() и другие (упо­мянутые делают текст ссылкой, полужирным и курсивным соответс­твенно), однако они используются только в сочетании с конструкцией document. write() , что снижает ценность подобных функций почти до нуля. То есть подобная конструкция работать будет:

<script language="JavaScript">

Document. write("Мой TeKCT".link("Http://www. ya. ru"))

</script>

А такие — не будут:

<p onMouseOver="this. innerHTML. link('Http://www. уа. ги')">Мой текст</р>

<p onMouseOver="this. link('Http://www. уа. ги')">Мой текст</р>

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

В JavaScript можно работать не только со строками, но и с чис­лами:

<script language="JavaScript">

Function convert(inches) { cm = inches * 2.54; meters = inches / 39.37;

}

</script>

Данная функция преобразует дюймы (inches) в сантиметры и метры, инициализирую соответствующие переменные. Сначала нуж­но вызвать функцию с определенным числом дюймов, а потом печа­тать полученные значения вызовами document. write() или object. innerHTML.

В JavaScript употребляются следующие математические опера­торы:

A < b

Меньше

4

Программирование

А > Ь Больше

А <= Ь

Не больше; меньше или равно

А => Ь

Не меньше; больше или равно

А == Ь Равно

А!= Ь Не равно

А === Ь

Тождественно (равно с учетом типа данных)

А !== Ь

Не тождественно (не равно с четом типа данных)

Эти операторы чаще всего используются при сравнении, на­пример, в условных операторах. Есть и обычные арифметические операции:

А + Ь Сложение

А - Ь Вычитание

А * Ь Умножение

А / Ь Деление

А % Ь

Остаток по модулю (возвращает целый остаток от деления левого операнда на правый. Плавающие числа перед операцией округляются до целых)

++

Инкремент [увеличивает значение переменной на 1. Если ис­пользуется как префикс (++а), возвращает значение операнда после

352

Сайт следит за вами: JavaScript

Увеличения его на 1. Если используется как постфикс (a++), возвраща­ет значение операнда перед увеличением его на 1]

Декремент [уменьшает значение переменной на 1. Если исполь­зуется как префикс (--a), возвращает значение операнда после умень­шения его на 1. Если используется как постфикс (a--), возвращает зна­чение операнда перед уменьшением его на 1]

Смена знака

Обратите внимание, что JavaScript (в отличие, например, от Python) лояльно относится к пробелам. То есть «a+b» и «а + b» для языка JavaScript — одно и то же.

В JavaScript есть три логических оператора:

А && b

Логическое «и» (оператор возвращает true, если оба операнда истинны. Если первый операнд ложен, то возвращает false, не вычис­ляя значение второго операнда)

А || b

Логическое «или» (возвращает true, если хотя бы один операнд истинен. Если первый операнд истинен, то возвращает true, не вычис­ляя значение второго операнда)

Логическое «не» (возвращает true, если операнд ложен)

В JavaScript есть операторы присваивания. С оператором a=b мы уже несколько раз имели дело, но есть и сложные операторы, которые аналогичны операции присваивания, совмещенной с арифметически­ми операциями:

A += b аналогично a = a + b a -= b аналогично a = a - b a *= b аналогично a = a * b a /= b аналогично a = a / b a %= b аналогично a = a % b

Помимо этого, в JavaScript существует встроенный объект Math, который используется для доступа к различным операциям с числами. Например, Math. round ( число ) возвращает результат округления чис­ла, переданного в функцию, Math. sin( число ) возвращает синус и т. п.

353

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

Условный оператор формата var = test? valuel : value2 уже рассматривался в одном из примеров. Он позволяет присваивать значение не только переменной, но и свойствам объектов.

Оператор «запятая» позволяет группировать действия. Например, в цикле for:

<script language="JavaScript">

Abc = new Array("nepBbrn", "Второй", "Третий") for(i=0,j=1; i<abc. length; i++,j++)

{

Document. write("<p>"+j+". "+abc[i]+"</p>")

}

</script>

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

Оператор delete может удалять свойство объекта или элемент из массива, а также переменную (если она не была назначена с исполь­зованием кодового слова var):

Langs = new Array("HTML", "CSS", "JavaScript", "Java")

Delete langs[2]

Оператор in возвращает «истину», если левый операнд является свойством правого операнда (или левый операнд является элементом массива, указанного в качестве правого операнда).

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

<p id="special">123</p>

Можно традиционно изменить его свойства:

<script language="JavaScript"> document. getElementById("special").style. color="red"

Сайт следит за вами: JavaScript

Document. getElementById("special").style.

FontWeight="bold"

Document. getElementById("special").style.

BorderBottomWidth="1px"

Document. getElementById("special").style.

BorderBottomStyle="dashed"

Document. getElementById("special").style.

BorderBottomColor="red"

</script>

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

Но код можно написать лаконичнее:

<script language="JavaScript">

With(document. getElementById("special").style)

{

Color="red"

FontWeight="bold"

BorderBottomWidth="1px"

BorderBottomStyle="dashed"

BorderBottomColor="red"

}

</script>

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

Пример 1. Печатающийся текст

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

<div id="info"></div>

<script language="JavaScript">

Text = "Какой-то очень длинный текст, в данном случае приводимый в качестве тестового, чтобы можно было сполна насладиться эффектом." i = 0

Function printIt()

{

I++

If(i <= text. length)

{

Document. getElementById("info").innerHTML = text. substr(0,i)

}

SetTimeout("printIt()",25)

}

PrintIt()

</script>

Пояснения. Из переменной text берется сначала первый сим­вол, при втором выполнении функции — первый и второй и т. п.; после такого извлечения в пустой блок с идентификатором «info» записыва­ется получившийся отрезок текста. Выполнение функции повторяется благодаря функции setTimeout(функция, задержка) , которая вы­зывает переданную ей функцию еще раз с задержкой в 25 миллисекунд. Поскольку вызов setTimeout() помещен внутрь функции (более того, внутрь условия), он будет вызываться снова и снова, пока условие (i меньше или равно длине массива) не перестанет быть истинным. Все это создает иллюзию печатающегося текста, хотя фактически содержи­мое блока перезаписывается сценарием со все увеличивающимся коли­чеством символов.

Пример 2. Блок, выезжающий из-за границы экрана

Задача: при нажатии на ссылку или кнопку из-за боковой грани­цы веб-страницы выезжает блок с каким-то содержимым и останавли­вается в определенном месте.

<div id="searcher" style="position:absolute; left:-350px; top:245px; width:300px; background- color:#9CEB91; border-width:1px; border-style: solid; border-color:#FDECB8 black black #FDECB8; padding:10px">

Содержимое блока поисковой системы </div>

<script language="JavaScript"> sta = -350 re = 1

Function load search()

{

If(sta<=255)

Сайт следит за вами: JavaScript

{

Sta = sta+10

Document. getElementById("searcher").style. left =

Sta

SetTimeout("load search()",10)

}

Else

{

Re = 2 }

}

Function load search back()

{

If(sta>=-350)

{

Sta = sta-10

Document. getElementById("searcher").style. left =

Sta

SetTimeout("load search back()",10)

}

Else

{

Re = 1 }

}

</script>

<p><a href="/search/" onClick="if(sta<=55)

{load search();} else {load search back();} return false">Поиск</a></p>

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

Пример 3. Системное сообщение на сайте

Задача: при нажатии на кнопку или ссылку вся веб-страница за­темняется, элементы становятся едва видны, а на этом фоне четко вид­но системное сообщение.

<script language="JavaScript"> function opCl(dvname)

357

{

Document. getElementById(dvname).style. display = (document. getElementById(dvname).style. display=="block") ? "none" : "block";

}

</script>

<button onClick="opCl('darkener')></button>

<div id="darkener" style="display:none; position: absolute; left:0px; top:0px; width:100%; height:100%; background-color:#777777; text-align:center; filter: progid:DXImageTransform. Microsoft. Alpha(opacity=90); - moz-opacity: 0.9; - khtml-opacity: 0.9; opacity: 0.9; color:white">

Содержимое системного сообщения </div>

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

Пример 4. Переход подсветки по пунктам меню

Задача: сделать эффект плавного подсвечивания пунктов меню определенным цветом, причем чтобы это подсвечивание «двигалось» по меню: сначала был подсвечен первый пункт меню, затем второй и т. п., вне зависимости от количества пунктов. Соседние пункты меню долж­ны иметь переходный цвет. При наведении на пункт меню оно меняется (как обычно, псевдоклассом a:hover), что не мешает движению динами­ческого подсвечивания. Сами же пункты меню располагаются в виде «лесенки» сверху вниз справа налево, уступами.

<style type="text/css"> body, td, div, p, pre { font-size:10pt;

Font-family:Arial, Helvetica, Sans-Serif;

}

A:link, a:visited, a:active, .pseudolink { color:#4 9587 9;

}

.cat1:link, .cat1:visited, .cat1:active { text-decoration:none; font-size:110%;

Сайт следит за вами: JavaScript

Color:#4 9587 9;

}

.cat2:link, .cat2:visited, .cat2:active {

Text-decoration:none;

Font-size:110%;

Color:#CC0000;

}

A:hover, .pseudohover { color:#355DB5;

}

.cat3:link, .cat3:visited, .cat3:active {

Text-decoration:none;

Font-size:110%;

Color:#683456;

}

A:hover, .pseudohover { color:#355DB5;

}

.cat1:hover, .cat2:hover, .cat3:hover { font-size:12 0%; color:#CC0000;

Border-left:3px solid #B8C2CE; border-bottom:1px solid #B8C2CE;

}

</style>

<script language="JavaScript"> items = new Агму^Тазораспределительные станции", "Подогреватели газа", "Газорегуляторные пункты блочные", "Газорегуляторные пункты шкафные", "Газорегуляторные пункты НОРД", "Газорегуляторные пункты с узлом учета", "Пункты учета газа автономные", "Регуляторы давления газа", "Предохранительно-сбросные клапаны", "Предохранительно-запорные клапаны",

"Клапаны электромагнитные", "Клапаны предохранительные полуавтоматические", "Краны шаровые газовые",

"Фильтры газовые", "Блочно-модульные котельные", "Подогреватели нефти");

Itemsl = new Array(); itemsll = new Array();

For(i=0; i<items. length; i++)

{

Zi = 102+i

Itemsl[i] = "div"+i;

Itemsll[i] = "lin"+i; posit = i+1; leftstop = 430-(i*11); topstop = 20+(i*20); document. write("<div id='div"+i+"' style='height:25px; width:400px; position:absolute; left:"+leftstop+"; top:"+topstop+"; padding:1px; z - Index:"+zi+"'><A id='lin"+i+"' HREF="/?catalogue/ "+posit+"/" class="cat1">"+items[i]+"</A></div>"); // конец цикла }

Ii=0

Function change()

{

If(ii<itemsl. length)

{

Iipre = ii-1;

If(iipre < 0) {iipre=itemsl. length-1;} iipost = ii+1;

If(iipost >= itemsl. length) {iipost=0;} for(j=0; j<itemsl. length; j++)

{

Document. getElementById(itemsll[j]).className =

"cat1"

}

Document. getElementById(itemsll[ii]).className = "cat2"

Document. getElementById(itemsll[iipre]).className = "cat3"

Document. getElementById(itemsll[iipost]). className = "cat3" ii++

If(ii==itemsl. length)

{

Ii = 0;

}

}

SetTimeout("change()",800)

}

Change()

</script>

Сайт следит за вами: JavaScript

Пояснения. Меню генерируется динамически, поэтому его пун­кты содержатся в массиве. (На самом деле стоит предусмотреть, что JavaScript в браузере может быть отключен, и создать на этот случай статическое меню, заключив его в тэги <noscript>...</noscript>.) Циклом также генерируются имена блоков <div> и идентификацион­ные названия ссылок, чтобы и к тем, и к другим можно было впослед­ствии обращаться с помощью getElementById(). В этом же цикле динамически вычисляются координаты для жестко позиционирован­ных блоков, заключающих пункты меню. Поскольку стиль псевдоклас­са hover (видный при наведении мыши) такой, что ссылка становится крупнее, нужно проследить, чтобы она не перекрывалась соседними ссылками. За это отвечает CSS.

Функция change(), описываемая в сценарии, отвечает не толь­ко за применение нового имени класса к очередной ссылке, но и за применение имени особого «промежуточного» класса к соседним ссылкам, причем, если «красная», то есть наиболее ярко подсвеченная ссылка оказывается на краю меню (первым или последним элементом), то соседними считаются не только второй или предпоследний пункты меню соответственно, но и крайние с противоположного конца меню: для первого пункта меню соседними считаются второй и последний. Для движения цвета по элементам используется небольшая задержка, реализованная с помощью функции setTimeout(), которая нахо­дится в теле функции, а значит, будет выполняться, пока истинно ус­ловие — значение числовой переменной меньше, чем длина массива. Чтобы это условие всегда оставалось истинным, при достижении пос­леднего допустимого значения числовая переменная обнуляется, и все начинается сначала.

Пример 5. Расписание

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

<script language="JavaScript">

Daysname = new Аггау("Воскресенье","Понедельник", "Вторник","Среда","Четверг","Пятница","Суббота") days = new Array() days[0] = 'Выходной' // Воскр.

Days[1] = 'Математика — 2 пары <БИ>Прикладное домоводство' // Понед.

Days[2] = 'Общая типология <БИ>Кибернетика <БИ>История татарской культуры' // Вт.

Days[3] = 'История вьетнамского языка <БИ>Рисование <БИ>Трехмерное моделирование' // Ср.

Days[4] = 'Традиционный буддизм <БК>Углубленное сисадминство' // Чт.

Days[5] = 'Литературоведение <ВК>Шрифты и визуальные коммуникации' // Птн. days[6] = 'Выходной' // Суб.

Now = new Date() thisday = now. getDay() tomorrow = now. getDay()+1 if(tomorrow>6) tomorrow=0

Document. write("<P><B>"+daysname[thisday]+"

(сегодня)</В><ВИ>")

Document. write(days[thisday] + "</P>")

Document. write("<P><B>"+daysname[tomorrow]+"

(завтра)</В><ВИ>")

Document. write(days[tomorrow]+ "</P>")

</script>

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

Пример 6. Перетаскивание объектов по веб-странице

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

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

Для этого достаточно сделать следующее: создать файл со сце­нарием (например, drag. js — от английского drag «тащить») и сделать на него ссылку в коде страницы, где будет происходить перетаскива­ние: <script language="JavaScript" src="/scripts/drag. js"></script>; в каждом тэге блока, который будет перемещаться мышкой по странице, сделать указание на класс, определенный в гло­бальной таблице, идентифицировать этот блок с помощью параметра id, указать первоначальные координаты и инициализировать выполне­ние скрипта:

Сайт следит за вами: JavaScript

<div class="draggable" id="block1" style="left:17 5px; top:100px" onmousedown="StartDrag( event, this, PutBack)">nepeTac^BaeMbra блок</div>

Класс «draggable» следует определить заранее:

<style>

.draggable

{

Cursor: pointer; z-index: 2; width: 180px; padding: 5px; position: absolute; background-color: white; border:1px solid #C6C9DD }

</style>

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

Наконец, содержание файла «/scripts/drag. js» таково:

//---------------------------------------------------------------------------

/*

Author — Andrew Shitov (Ash@design. ru) | 2005­2006

Additions — Erlang (Www. erlang. com. ru) | 2006 May Original drag mechanics was written by Mike Hall (Http://www. brainjar. com/dhtml/drag/) in 2001.

*/

Var isMSIE = document. attachEvent!= null; var isGecko = !document. attachEvent && document. addEventListener;

Var Draggingltem = new Object();

Function StartDrag (event, this, afteraction)

{

Draggingltem. This = this; Draggingltem. AfterAction = afteraction;

Var position = new Object(); if (isMSIE)

{

Position. x = window. event. clientX + document.

DocumentElement. scrollLeft + document. body. scrollLeft;

Position. y = window. event. clientY + document. documentElement. scrollTop + document. body. scrollTop;

}

If (isGecko)

{

Position. x = event. clientX + window. scrollX; position. y = event. clientY + window. scrollY;

}

Draggingltem. cursorStartX = position. x; Draggingltem. cursorStartY = position. y;

Draggingltem. StartLeft = parseInt (Draggingltem. This. style. left);

Draggingltem. StartTop = parseInt (Draggingltem. This. style. top);

If (isNaN (Draggingltem. StartLeft)) Draggingltem. StartLeft = 0;

If (isNaN (Draggingltem. StartTop)) Draggingltem. StartTop = 0;

If (isMSIE)

{

Document. attachEvent ("onmousemove",

ProceedDrag);

Document. attachEvent ("onmouseup", StopDrag); window. event. cancelBubble = true; window. event. returnValue = false;

}

If (isGecko)

{

Document. addEventListener ("mousemove", ProceedDrag, true);

Document. addEventListener ("mouseup", StopDrag, true);

Event. preventDefault();

}

}

Function ProceedDrag (event)

{

Document. getElementById("restorer").style.

Display="block";

Сайт следит за вами: JavaScript

Var position = new Object(); if (isMSIE) {

Position. x = window. event. clientX + document. documentElement. scrollLeft + document. body. scrollLeft;

Position. y = window. event. clientY + document. documentElement. scrollTop + document. body. scrollTop;

}

If (isGecko)

{

Position. x = event. clientX + window. scrollX; position. y = event. clientY + window. scrollY;

}

Var nextX = DraggingItem. StartLeft + position. x

- DraggingItem. cursorStartX;

If (nextX < 9) nextX = 9;

DraggingItem. This. style. left = nextX + "px"; if (nextX > 800) nextX = 800;

DraggingItem. This. style. left = nextX + "px";

Var nextY = DraggingItem. StartTop + position. y

- DraggingItem. cursorStartY;

If (nextY < 12) nextY = 12;

DraggingItem. This. style. top = nextY + "px";

If (nextY > 421) nextY = 421; DraggingItem. This. style. top = nextY + "px";

If (isMSIE)

{

Window. event. cancelBubble = true; window. event. returnValue = false;

}

If (isGecko) event. preventDefault();

}

Function StopDrag (event)

{

If (isMSIE)

{

Document. detachEvent ("onmousemove",

ProceedDrag);

Document. detachEvent ("onmouseup", StopDrag);

}

If (isGecko)

{

Document. removeEventListener ("mousemove", ProceedDrag, true);

Document. removeEventListener ("mouseup",

StopDrag, true);

}

If (DraggingItem. AfterAction) DraggingItem. AfterAction (DraggingItem. This);

}

Zeta = 100

Function PutBack (item)

{

Zeta = zeta+1 item. style. zIndex = zeta;

}

//--------------------------------------------------------------------------

Резюме

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

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

Сайт располагается на одной странице

4.5

подпись: 4.5Меню там, где можно обойтись простым иерархическим, — еще не все браузеры в состоянии отобразить действующие выпадающие меню, пос­троенные только на CSS (имеются в виду не списки <select>, а динами­чески формирующиеся блоки ссылок). Однако и ударяться в обратную крайность — делать только статические страницы — тоже не стоит.

По ту сторону Веб-страницы

Словарь

Ботки. Обычно расширения состоят из трех букв (exe, gif, php, mov, bmp, eps, swf, asp, m3u, avi, rtf, txt, zip, cpp), но встречаются также двухбуквенные (js, ai) и четырехбуквенные (html, …

Справочник для внутреннего использования

Навигация есть признание того, что твоя страница далека от иде­ала. Ибо если бы она была близка к нему, зачем бы потребовалось покидать ее? А если ее не требуется покидать, зачем …

Алфавит от Google

Есть такая тестирующаяся поисковая подсистема от Google (Http://Www.Google.Com/Webhp?Complete=1&Hl=En), в которой по введенным первым буквам предлагаются наиболее часто за­прашиваемые слова. Я собрал все первые (наиболее рейтинговые) слова на каждую букву русского …

Как с нами связаться:

Украина:
г.Александрия
тел./факс +38 05235  77193 Бухгалтерия

+38 050 457 13 30 — Рашид - продажи новинок
e-mail: msd@msd.com.ua
Схема проезда к производственному офису:
Схема проезда к МСД

Партнеры МСД

Контакты для заказов оборудования:

Внимание! На этом сайте большинство материалов - техническая литература в помощь предпринимателю. Так же большинство производственного оборудования сегодня не актуально. Уточнить можно по почте: Эл. почта: msd@msd.com.ua

+38 050 512 1194 Александр
- телефон для консультаций и заказов спец.оборудования, дробилок, уловителей, дражираторов, гереторных насосов и инженерных решений.