0% нашли этот документ полезным (0 голосов)
15 просмотров199 страниц

JavaScript. Как Писать Программы

Загружено:

Bakhshi Mehdiyev
Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
0% нашли этот документ полезным (0 голосов)
15 просмотров199 страниц

JavaScript. Как Писать Программы

Загружено:

Bakhshi Mehdiyev
Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
Вы находитесь на странице: 1/ 199

В. В.

ЯНЦЕВ

JAVASCRIPT
КАК ПИСАТЬ
ПРОГРАММЫ
Учебное пособие

•САНКТПЕТЕРБУРГ•МОСКВА•КРАСНОДАР•
•2022•
УДК 004
ББК 32.973я73

Я 65 Янцев В. В. JavaScript. Как писать программы : учебное пособие


для вузов / В. В. Янцев. — СанктПетербург : Лань, 2022. — 200 с. :
ил. — Текст : непосредственный.

ISBN 9785811485598

В книге рассматриваются все этапы написания сценариев на JavaScript:


от появления идеи до финальных испытаний. Читатели узнают, как про
исходят: подготовка среды разработки на персональном компьютере; фор
мирование алгоритма выполнения проекта; освоение приемов написания
качественных сценариев; работа с переменными, массивами, операторами,
регулярными выражениями, функциями; тестирование, отладка и стан
дартизация кода. Особое внимание уделено методам, приемам и навыкам,
которые помогут разработчику упростить создание интернетпроектов.
Кроме того, подробно разобраны примеры нескольких готовых сценариев.
Рекомендовано в качестве дополнительной литературы для студентов
вузов, обучающихся по направлению «Информатика и вычислительная
техника».

УДК 004
ББК 32.973я73

Обложка
П. И. ПОЛЯКОВА

© Издательство «Лань», 2022


© В. В. Янцев, 2022
© Издательство «Лань»,
художественное оформление, 2022
Оглавление

1. Введение ......................................................................................................................................... 5
1.1. О чем эта книга .................................................................................................................. 5
1.2. Особенности изложения материала ................................................................................. 6
1.3. Оформление сценариев ..................................................................................................... 7
1.4. Скромное напутствие ........................................................................................................ 8
1.5. Благодарности .................................................................................................................. 10
2. Программное обеспечение .......................................................................................................... 11
2.1. Установка сервера Apache .............................................................................................. 12
2.2. Установка PHP 7 .............................................................................................................. 16
2.3. Установка Python 3 .......................................................................................................... 18
2.4. Установка Perl 5 ............................................................................................................... 23
2.5. Установка Ruby 3 ............................................................................................................. 29
2.6. Устанавливать ли базу данных? ..................................................................................... 34
2.7. Редакторы кода ................................................................................................................ 37
3. Подготовительные работы .......................................................................................................... 48
3.1. Алгоритм действий.......................................................................................................... 48
3.2. Шаблон страницы ............................................................................................................ 49
3.3. Добавляем элементы ....................................................................................................... 51
3.4. Таблицы стилей ............................................................................................................... 54
3.5. Размещаем сценарий ....................................................................................................... 56
4. Некоторые особенности программирования ............................................................................. 57
4.1. События ............................................................................................................................ 57
4.2. Обработчики событий ..................................................................................................... 59
4.3. Остановка, удаление обработчика ................................................................................. 64
4.4. Глобальные и локальные переменные ........................................................................... 67
4.5. let или var (или const)? ..................................................................................................... 70
4.6. Меньше или больше переменных? ................................................................................ 74
4.7. Массивы............................................................................................................................ 76
4.8. Операторы ........................................................................................................................ 80
4.9. Условные операторы ....................................................................................................... 83
4.10. Операторы циклов ......................................................................................................... 88
4.11. Функции.......................................................................................................................... 92
4.12. Технология Ajax ............................................................................................................ 99
4.13. Подсказки и проверка данных .................................................................................... 105
4.14. Регулярные выражения ............................................................................................... 108
4.15. Обработка строк........................................................................................................... 113
4.16. Комментарии ................................................................................................................ 115
5. Тестирование сценариев ............................................................................................................ 119
5.1. Оптимизация кода ......................................................................................................... 120
5.2. Валидаторы .................................................................................................................... 125
5.3. Браузеры ......................................................................................................................... 129
5.4. Логические ошибки ....................................................................................................... 130
5.5. Кстати ............................................................................................................................. 140

3
6. Примеры сценариев ................................................................................................................... 143
6.1. Выбор картинки ............................................................................................................. 143
6.2. Увеличиваем рисунки ................................................................................................... 149
6.3. Галерея ............................................................................................................................ 156
6.4. Слайдер ........................................................................................................................... 161
6.5. Круговорот изображений .............................................................................................. 168
6.6. Пасьянс из картинок ...................................................................................................... 173
6.7. Проявление и «растворение»........................................................................................ 178
6.8. Рисунки по номерам ...................................................................................................... 183
6.9. Три галереи..................................................................................................................... 188
7. Подведем итоги .......................................................................................................................... 195
8. Об авторе..................................................................................................................................... 197

4
1. Введение

Прежде чем купить какую-либо книгу по программированию, потенци-


альному читателю важно знать, для кого она написана. На этот вопрос я отвечу
так: данная книга — для тех, кто уже изучил азы JavaScript и готов приступить
к самостоятельному написанию сценариев для web-страниц. Поэтому советую
перед ее прочтением ознакомиться с литературой, посвященной основам языка.
Например, можете начать с книги А. В. Дикова «Web-программирование на
JavaScript» (издательство «Лань», Санкт-Петербург, 2021). Или с любой другой,
где рассказано о переменных, операторах, управляющих инструкциях, циклах и
функциях.

1.1. О чем эта книга

Чтобы у посетителей остались хорошие впечатления о вашем сайте, он


должен быть функциональным, интерактивным и привлекательным. Достигает-
ся такой результат не только за счет красивого дизайна, но и, в первую очередь,
за счет гармоничного и умелого внедрения сценариев на JavaScript.
Книг о синтаксисе языка программирования JavaScript много. Я бы даже
сказал: очень много. Конечно, каждая из них по-своему хороша, интересна и
полезна, но в конечном счете все они об одном и том же.
Есть ряд книг, в которых излагаются не только основы языка, но и даются
примеры сценариев. Они могут объяснить начинающему разработчику, каким
образом создаются реальные проекты.
И все же в литературе по JavaScript есть некоторый изъян. По одной важ-
ной теме не написано почти ничего. Я программирую с 2003 г., прочитал мно-
жество изданий, посвященных JavaScript, и могу судить об этом не понаслышке.
Много ли вы видели книг, в которых рассказывалось бы непосредственно
о методах, технике и последовательности создания программ? Где излагались
бы разные подходы, варианты и приемы написания скриптов? Из которых чи-
татель узнал бы о секретах, хитростях и тонкостях «производства» сценариев
на JavaScript? Познакомился бы с рекомендациями по оптимизации, настройке
и отладке проектов? Повторюсь: таких книг почти нет.
Частично я попытался решить проблему двумя предыдущими книгами:
«JavaScript. Готовые программы» и «JavaScript. Обработка событий на приме-
рах» (обе книги выпустило издательство «Лань», Санкт-Петербург, 2021). Од-

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

Сомнительные практики
Не спешите завершать свое образование в программировании на
JavaScript прочтением 2–3 книг. Язык этот предоставляет разработчику
настолько богатые и разнообразные возможности, что изучать их можно
очень долго, а совершенствовать свои знания всегда полезно, особенно,
если вы хотите стать настоящим профессионалом.

1.2. Особенности изложения материала

Допустим вы решили создать свой первый сайт, в котором будет код на


JavaScript. Что и в какой последовательности делать? Именно об этом и расска-
зывается в книге. В ней 5 основных глав.
Глава 2 «Программное обеспечение». Здесь идет речь о том, как подгото-
вить среду разработки на вашем персональном компьютере. Короткое содержа-
ние главы:
1) вам необходимо создать на компьютере локальный хостинг, чтобы
проверять сценарии в действии. Для этого понадобится http-сервер. Подробные
инструкции по его установке приведены в начале главы;
2) если ваши HTML-страницы должны взаимодействовать с серверными
программами, необходимо установить интерпретатор какого-либо языка, при-
годного для написания серверных скриптов. Я предложил вам 4 разных языка с
инструкциями по установке их интерпретаторов на ваш ПК;
3) необходимо понять, насколько сложной будет первая разработка, будут
ли присутствовать в проекте скрытые данные и, соответственно, необходимо ли
установить базу данных. Даются примеры наиболее популярных БД;
4) в конце главы рассказано о нескольких редакторах кода, которые удоб-
но и целесообразно использовать при написании HTML-страниц, таблиц стилей,
сценариев на JavaScript и серверных программ.
Глава 3. Довольно короткая. Здесь рассматриваются основные приемы
создания шаблонов страниц, принципы компоновки элементов, добавление
таблиц стилей и сценариев на JavaScript в документы.
Глава 4. Самая большая. Основные положения данной книги изложены
именно в ней. Вы узнаете:
 на какие события наиболее часто откликаются сценарии;
 что такое обработчики событий и какими способами их можно зареги-
стрировать;
 как остановить или удалить обработчик;

6
 в чем различия между глобальными и локальными переменными;
 какой вариант объявления переменных лучше выбрать и чем эти вари-
анты отличаются;
 что лучше — больше переменных или, наоборот, меньше;
 массивы, операторы, условия, циклы — их особенности;
 какие бывают функции и как передавать в них параметры;
 о пользе технологии Ajax;
 как помочь клиенту правильно заполнить форму;
 в чем польза регулярных выражений;
 о способах и приемах добавления комментариев к вашему коду.
Глава 5 посвящена «шлифовке» сценариев. Мы последовательно раз-
берем:
1) что такое оптимизация кода. На конкретных примерах увидим, какими
способами можно реализовать этот процесс;
2) что такое валидаторы, какими они бывают и как помогают корректиро-
вать разметку, таблицы стилей и сценарии, чтобы «удержать» их в рамках стан-
дартов;
3) как и в каких браузерах проверять работоспособность ваших программ;
4) логические ошибки (как они возникают и как их находить). Продемон-
стрируем данный процесс на конкретном примере, специально написанном для
этой книги.
Глава 6 содержит примеры законченных программ для работы с изобра-
жениями. То есть, мы используем полученные знания на практике.

1.3. Оформление сценариев

Хочу рассказать вам о принципах, по которым примеры из книги создава-


лись, отбирались и оформлялись.
Главных принципа три.
Первый. Все примеры написаны по самым современным стандартам, су-
ществующим в web-программировании.
Второй. Чтобы сделать примеры кода в этой книге максимально просты-
ми и понятными, из них удалены все лишние элементы. Оставлено только са-
мое необходимое.
Третий. Все примеры кода проверены в HTML-валидаторе Консорциума
Всемирной паутины и JavaScript-валидаторе (я выбрал самый строгий валида-
тор из тех, что обнаружил в сети). Подробнее о валидаторах вы узнаете в гла-
ве 6 (раздел 6.2).
На момент написания книги отклонений от стандартов HTML5, CSS3 и
JavaScript в примерах не было.
Также все примеры были проверены в наиболее популярных браузерах —
Microsoft Edge, Google Chrome, Mozilla Firefox, Opera и Яндекс.Браузер. В пе-
речисленных браузерах все примеры работали корректно.

7
Кроме того, сценарии имеют особое типографское оформление в соответ-
ствии с их размещением в тексте.
Если сценарий выделен в отдельный блок, он оформлен моноширинным
шрифтом, например так:
let i=0;
function func()
{
i++;
alert ("Количество кликов: "+i);
}

Если фрагменты сценария внедрены непосредственно в текст, то в этом


случае части кода выделены полужирным шрифтом, например так:
{once: true}

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


да на вторую строку (из-за недостатка ширины страницы). В реальном
сценарии код записывается одной строкой. Запомните правило: все пере-
носы строк кода существуют только в их типографском воспроизведении.
Если вы в дальнейшем столкнетесь с подобной ситуацией, учитывайте
данный аспект.
Еще один принципиальный момент. Не все программисты внимательно
следят за изменениями в стандартах HTML5. Особенно это касается написания
тега <script>. Почти во всех, даже современных, книгах авторы по-прежнему
указывают его неправильно. Обратите внимание! В данном теге тип type не
указываем! Этому правилу уже несколько лет, но многие разработчики до сих
пор не в курсе. Валидатор Консорциума Всемирной паутины воспринимает
указание типа в теге <script> как ошибку и выдает предупреждение! Поэтому
включение кода в страницу правильно оформлять так:
<script>
...
</script>

И последнее дополнение к разговору о сценариях — вы можете скачать


zip-архив со всеми примерами кода. Он находится по адресу https://testjs.
ru/kpp/kpp.zip. Сценарии, которые есть в архиве, обозначены рисунком .

1.4. Скромное напутствие

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


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

8
Однако предположу, что скорее всего вы решили научиться программи-
рованию, чтобы превратить этот увлекательный процесс в будущую профессию.
А это уже совсем иная ситуация. Все, что вы делаете в качестве начинающего
кодера, должно укладываться в определенные рамки, к которым необходимо
приучать себя с первых шагов.
Знакомясь с приемами написания сайтов и сценариев, примите во внима-
ние несколько простых советов.
1. Во многих IT-компаниях существуют внутренние корпоративные пра-
вила оформления программ. Будьте готовы к тому, что в некоторых случаях
ваш стиль написания кода придется немного подкорректировать. В разных сту-
диях разные требования и соответствовать сразу всем невозможно. Отнеситесь
к этому с пониманием. Ваша задача — создавать программы, с которыми легко
и приятно иметь дело не только вам, но и другому специалисту, если ему при-
дется в дальнейшем использовать ваши разработки.
2. Не хотите трудиться в коллективе, а предпочитаете быть фрилансером?
Но даже в этом случае не все так просто. Есть немало заказчиков, которые не
примут готовый «продукт» и не помашут вам рукой на прощание, пока доско-
нально не проверят, что именно вы им сделали. Так что, чем строже вы оцени-
ваете свою работу, тем больше вероятность написать качественный сценарий с
первого раза и не тратить время на исправления и переделки.
3. Хорошим тоном в web-программировании считается подход, при кото-
ром вы отделяете разметку страницы от стилей и кода JavaScript. Более того, в
корпоративной среде принято все сценарии и таблицы стилей размещать в от-
дельных файлах. В этой книге мы не станем придерживаться данного правила,
но только по одной причине — чтобы упростить изложение.
4. Отступы, пробелы, расположение скобок. В некоторых случаях это
важно, а в других — нет. Поясню. Одни разработчики выделяют блоки кода че-
тырьмя пробелами слева, другие используют три и даже два:

if(a<5)
b=100; // Отступ в 4 пробела

if(a<5)
b=100; // Отступ в 3 пробела
if(a<5)
b=100; // Отступ в 2 пробела

Замечу, что практика использования четырех пробелов наиболее распро-


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

function func() { // Скобка в одной строке с именем функции


...
}

9
function func()
{ // Скобка ниже имени функции
...
}

Первый вариант более распространен, но второй делает программу более


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

1.5. Благодарности

Мне нравится писать книги по программированию на JavaScript. Этот


язык очень интересен. И не только своим изяществом, синтаксисом и мощью.
Нравится, что JavaScript позволяет создавать потрясающие сценарии, которые
делают web-страницы отзывчивыми к действиям посетителей. Нравится, что
язык предоставляет разработчику поистине неограниченные возможности для
реализации его идей и задумок.
Словом, я пишу книги про замечательный язык программирования. Эти
книги дошли до читателей благодаря неоценимой помощи многих людей, чьи
имена вы не увидите на обложке. Поэтому я хотел бы особо отметить их.
Большое спасибо:
 заведующей редакцией литературы по информационным технологиям и
системам связи издательства «Лань» Ольге Евгеньевне Гайнутдиновой — это
уже моя третья книга, которую подготовила Ольга Евгеньевна;
 ответственному редактору издательства «Лань» Наталье Александровне
Кривилёвой — за подготовку макетов моих книг и терпеливое внесение ис-
правлений во время этого процесса;
 моему давнему другу Василию Регентову за многолетнее предоставле-
ние «площадки» для моих компьютерных экспериментов и терпеливое решение
возникающих — иногда непростых — проблем;
 братьям Василию и Дмитрию Зиновьевым, в далеком 2003 г. давшим
мне путевку в профессию программиста.

10
2. Программное обеспечение

Если вы хотите заниматься программированием на серьезном уровне, вам


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

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

Если вы хотите тестировать только HTML-страницы с внедренными в них


сценариями на JavaScript, то можно ограничиться установкой на свой компью-
тер http-сервера. Если планируете создавать страницы не просто загружаемые с
сервера, а взаимодействующие с другими серверными скриптами (например, с
программами обработки данных из форм), надо, кроме того, установить интер-
претатор какого-либо языка, например PHP, Python, Perl или Ruby (любой из
них на ваш выбор).
Рекомендую начать с сервера Apache и одного из интерпретаторов. Опи-
сание технологии их установки — в следующих разделах.

11
Обратите внимание: все процедуры описаны для компьютера с опе-
рационной системой Windows 10.
Итак, приступим к созданию локального хостинга. А начнем мы с уста-
новки сервера Apache.

2.1. Установка сервера Apache

Первым делом выясните разрядность вашей операционной системы. Для


этого щелкните на кнопке «Пуск», а затем на кнопке «Параметры» (рис. 2.1.1).
В открывшемся окне выберите пункт меню «Система». Откроется следующая
вкладка, в которой необходимо кликнуть на строку «О системе». После этого
на вкладке «Характеристики устройства» посмотрите строку «Тип системы»
(рис. 2.1.2).
Скачайте и установите на свой компьютер Visual C ++ Redistributable. Ад-
рес компилятора https://aka.ms/vs/16/re/release/VC_redist.x64.exe (для 64-раз-
рядной системы) или https://aka.ms/vs/16/release/VC_redist.x86.exe (для 32-раз-
рядной системы).
Теперь скачайте на рабочий стол компьютера с сайта https://
www.apachelounge.com/download/ zip-архив, соответствующий разрядности
вашей ОС (рис. 2.1.3). Для 64-разрядной системы — архив с пометкой Win64,
для 32-разрядной — с пометкой Win32. Распакуйте архив. Скопируйте папку
Apache24 (только ее) непосредственно на диск C, чтобы ее адрес был
C:\Apache24 (рис. 2.1.4).
Откройте приложение «Командная строка» от имени администратора. Для
этого нажмите кнопку «Пуск», в меню выберите «Служебные — Windows»,
найдите пункт «Командная строка» и щелкните на нем правой (правой!) кноп-
кой мыши. В выпадающем списке выберите «Дополнительно», а затем «Запуск
от имени администратора» (рис. 2.1.5). Откроется окно программы.

Рис. 2.1.1. Кнопка «Параметры» в меню

12
Рис. 2.1.2. Разрядность или тип операционной системы

Рис. 2.1.3. Сайт с zip-архивом сервера

13
Рис. 2.1.4. Копируем папку Apache24 на диск C

Рис. 2.1.5. Запуск командной строки

14
Теперь надо инсталлировать, а затем запустить сервер. Для этого в окне
командной строки сразу после
C:\WINDOWS\System32>

наберите
C:\Apache24\bin\httpd.exe -k install

Должно получиться
C:\WINDOWS\System32>C:\Apache24\bin\httpd.exe -k install

Нажмите «Enter». Когда процесс инсталляции закончится (рис. 2.1.6), в


окне программы вновь появится строка
C:\WINDOWS\System32>

Рис. 2.1.6. Инсталляция сервера из приложения «Командная строка»

Закройте окно командной строки и перезагрузите компьютер.


ВНИМАНИЕ! Может открыться окно брандмауэра с запросом на
разрешение работы «Apache». Разрешите доступ во всех сетях.
Нам надо убедиться, что сервер заработал. Для этого откройте ваш брау-
зер и введите в строке адреса http://localhost/. Если появилось сообщение
«It works!», значит, все в порядке — сервер функционирует как положено (рис.
2.1.7).

Рис. 2.1.7. Сервер подтверждает, что он работает

15
Осталось добавить, что файлы ваших проектов необходимо помещать в
папку htdocs по адресу C:\Apache24\htdocs, а просматривать готовые страницы
сайтов в браузере по адресу http://localhost/ или http://localhost/
имя_страницы.html, например http://localhost/primer.html или http://
localhost/test/primer.html.

2.2. Установка PHP 7

Скачайте на рабочий стол компьютера с сайта https://windows.php.net/


download/ zip-архив PHP 7, соответствующий разрядности вашей ОС. Для
64-разрядной системы — архив с пометкой x64, для 32-разрядной — с помет-
кой x86. Обратите внимание: так как мы будем устанавливать PHP в качестве
модуля сервера Apache, скачивать надо дистрибутив Thread Safe (рис. 2.2.1).
Распакуйте архив. Переименуйте распакованную папку на php и переме-
стите ее непосредственно на диск C, чтобы ее адрес был C:\php (рис. 2.2.2).
В папке C:\Apache24\conf с помощью текстового редактора «Блокнот»
откройте файл httpd.conf (рис. 2.2.3).

Рис. 2.2.1. Сайт с zip-архивом PHP

16
Рис. 2.2.2. Перемещаем папку php на диск C

Рис. 2.2.3. Файл httpd.conf

Найдите в файле httpd.conf строку


DirectoryIndex index.html

и добавьте к ней
index.php

Должно получиться
DirectoryIndex index.html index.php

Теперь в самый конец файла httpd.conf добавьте 2 строки:


AddHandler application/x-httpd-php .php
LoadModule php7_module "C:/php/php7apache2_4.dll"

17

Powered by TCPDF (www.tcpdf.org)


Сохраните изменения, закройте файл и перезагрузите компьютер.
Убедимся, что PHP заработал. Для этого в папке C:\Apache24\htdocs со-
здайте текстовый файл и запишите в него следующий код (файл ph1.php в пап-
ке «Глава2» zip-архива):

<?php
echo "Good !";

Сохраните этот файл под именем, например ph1.php. Откройте ваш брау-
зер и введите в строке адреса http://localhost/ph1.php. Если появилось сообще-
ние «Good !», значит, все в порядке — PHP работает (рис. 2.2.4).

Рис. 2.2.4. Проверяем работу PHP

PHP-файлы ваших проектов необходимо помещать в папку htdocs по ад-


ресу C:\Apache24\htdocs, а просматривать готовые страницы сайтов в браузере
по адресу http://localhost/ (для файла index.php) или http://localhost/
имя_страницы.php, например http://localhost/primer.php или http://localhost/
test/primer.php.

2.3. Установка Python 3

Скачайте на рабочий стол компьютера с сайта https://www.python.org/


downloads/windows/ установочный файл, соответствующий разрядности вашей
ОС (рис. 2.3.1). Запустите его.
Выберите вариант установки с настраиваемыми параметрами. Для этого
нажмите ссылку «Customize installation» (рис. 2.3.2). В открывшемся окне ниче-
го не меняйте, просто нажмите кнопку «Next» (рис. 2.3.3).
Основные настройки выполняются в следующем окне. Сперва поставьте
«галочку» напротив пункта «Install for all users», чтобы интерпретатор был до-
ступен всем пользователям. В строке «Customize install location» вручную вве-
дите адрес папки для интерпретатора: C:\Python. После этого нажмите кнопку
«Install» (рис. 2.3.4).
Дождитесь окончания процесса (рис. 2.3.5). Нажмите кнопку «Close» (рис.
2.3.6).

18
Рис. 2.3.1. Сайт с файлами интерпретатора Python

Рис. 2.3.2. Начало установки интерпретатора Python

19
Рис. 2.3.3. Здесь просто жмем «Next»

Рис. 2.3.4. Ставим «галочку» напротив пункта «Install for all users»,
меняем имя папки, в которую будет установлен интерпретатор,
и начинаем инсталляцию

20
Рис. 2.3.5. Процесс установки файлов интерпретатора Python

Рис. 2.3.6. Завершение установки

21
Рис. 2.3.7. Файл httpd.conf

В папке C:\Apache24\conf с помощью текстового редактора «Блокнот»


откройте файл httpd.conf (рис. 2.3.7), найдите в нем строку
DirectoryIndex index.html

и добавьте к ней
index.py

Должно получиться
DirectoryIndex index.html index.py

Теперь найдите строку


Options Indexes FollowSymLinks

и добавьте в конце нее


ExecCGI

Должно получиться
Options Indexes FollowSymLinks ExecCGI

Дальше найдите строку


#AddHandler cgi-script .cgi

удалите в начале нее знак # и добавьте в конце строки


.py
22
Должно получиться

AddHandler cgi-script .cgi .py

Сохраните изменения, закройте файл и перезагрузите компьютер.


Теперь убедимся, что Python заработал. Для этого в папке C:\Apache24\
htdocs создайте текстовый файл и запишите в него следующий код (файл
py1.py в папке «Глава2» zip-архива):

#! C:/Python/python
print ("Content-type: text/html\n\n")
print ("YES !")

Сохраните этот файл под именем py1.py. Откройте ваш браузер и введите
в строке адреса http://localhost/py1.py. Если появилось сообщение «YES !»,
значит, все в порядке (рис. 2.3.8).

Рис. 2.3.8. Проверяем работу Python

Осталось добавить, что файлы ваших проектов необходимо помещать в


папку htdocs по адресу C:\Apache24\htdocs, а просматривать готовые страницы
сайтов в браузере по адресу http://localhost/ (для файла index.py) или
http://localhost/имя_страницы.py, например http://localhost/primer.py или
http://localhost/test/primer.py. Путь к интерпретатору (первая строка) во всех
python-файлах на локальном хостинге должен записываться так:

#! C:/Python/python

2.4. Установка Perl 5

Скачайте на рабочий стол компьютера с сайта http://strawberryperl.com/


установочный файл, соответствующий разрядности вашей ОС (рис. 2.4.1). За-
пустите его.
Нажмите кнопку «Next» (рис. 2.4.2), после чего согласитесь с условиями
лицензии, снова нажмите «Next» (рис. 2.4.3).
Вам будет предложено установить интерпретатор в папку C:\Strawberry\.
Вручную перепишите ее название на C:\Perl\ (рис. 2.4.4).
Опять нажмите «Next», а затем «Install» (рис. 2.4.5). Дождитесь окончания
процесса (рис. 2.4.6).

23
Рис. 2.4.1. Сайт с файлами интерпретатора Perl

Рис. 2.4.2. Начало установки интерпретатора Perl

24
Рис. 2.4.3. Соглашаемся с условиями лицензии

Рис. 2.4.4. Меняем имя папки, в которую будет установлен интерпретатор

25
Рис. 2.4.5. Начинаем инсталляцию

Рис. 2.4.6. Процесс установки файлов интерпретатора Perl

26
Снимите галочку в строке «Read README file.» и нажмите «Finish» (рис.
2.4.7). Установка завершена.

Рис. 2.4.7. Завершение установки

Рис. 2.4.8. Файл httpd.conf

27
В папке C:\Apache24\conf с помощью текстового редактора «Блокнот»
откройте файл httpd.conf (рис. 2.4.8), найдите в нем строку
DirectoryIndex index.html

и добавьте к ней
index.pl

Должно получиться
DirectoryIndex index.html index.pl

Теперь найдите строку


Options Indexes FollowSymLinks

и добавьте в конце нее

ExecCGI

Должно получиться

Options Indexes FollowSymLinks ExecCGI

И последнее дополнение. Найдите строку

#AddHandler cgi-script .cgi

удалите в начале нее знак # и добавьте в конце строки

.pl

Должно получиться
AddHandler cgi-script .cgi .pl

Сохраните изменения, закройте файл и перезагрузите компьютер.


Нам надо убедиться, что Perl заработал. Для этого в папке C:\Apache24\
htdocs создайте текстовый файл и запишите в него следующий код (файл pe1.pl
в папке «Глава2» zip-архива):

#! C:/Perl/perl/bin/perl
print "Content-type: text/html\n\n";
print "FINE !";

Сохраните этот файл под именем pe1.pl. Откройте ваш браузер и введите
в строке адреса http://localhost/pe1.pl. Если появилось сообщение «FINE !»,
значит, все в порядке (рис. 2.4.9).

28
Рис. 2.4.9. Проверяем работу Perl

Осталось добавить, что файлы ваших проектов необходимо помещать в


папку htdocs по адресу C:\Apache24\htdocs, а просматривать готовые страницы
сайтов — в браузере по адресу http://localhost/ (для файла index.pl) или
http://localhost/имя_страницы.pl, например http://localhost/primer.pl или
http://localhost/test/primer.pl. Путь к интерпретатору (первая строка) во всех
perl-файлах на локальном хостинге должен записываться так:
#! C:/Perl/perl/bin/perl

2.5. Установка Ruby 3


Скачайте на рабочий стол компьютера с сайта https://rubyinstaller.org/
downloads/ из раздела «WITHOUT DEVKIT» установочный файл, соответ-
ствующий разрядности вашей ОС. Для 64-разрядной системы — архив с помет-
кой x64, для 32-разрядной — с пометкой x86 (рис. 2.5.1). Запустите установоч-
ный файл.

Рис. 2.5.1. Сайт с файлами интерпретатора Ruby

29
Рис. 2.5.2. Соглашаемся с условиями лицензии

Рис. 2.5.3. Меняем имя папки, в которую будет установлен интерпретатор

30
Рис. 2.5.4. В этом окне оставляем все без изменения

Рис. 2.5.5. Процесс установки интерпретатора Ruby

31
Рис. 2.5.6. Завершение установки

Рис. 2.5.7. Файл httpd.conf

Согласитесь с условиями лицензии и нажмите «Next» (рис. 2.5.2).


В открывшейся вкладке вручную перепишите адрес папки с интерпрета-
тором на C:\Ruby (рис. 2.5.3). Нажмите «Install». В следующем окне оставьте
все без изменений и нажмите кнопку «Next» (рис. 2.5.4).
Дождитесь окончания процесса установки (рис. 2.5.5).

32
На последней вкладке снимите «галочку» напротив строки «Run 'ridc
insnall' to set up MSYS2 and development toolchain» (рис. 2.5.6). Завершите оста-
новку нажатием кнопки «Finish».
В папке C:\Apache24\conf с помощью текстового редактора «Блокнот»
откройте файл httpd.conf (рис. 2.5.7), найдите в нем строку

DirectoryIndex index.html

и добавьте к ней

index.rb

Должно получиться

DirectoryIndex index.html index.rb

Теперь найдите строку

Options Indexes FollowSymLinks

и добавьте в конце нее

ExecCGI

Должно получиться

Options Indexes FollowSymLinks ExecCGI

И последнее дополнение. Найдите строку

#AddHandler cgi-script .cgi

удалите в начале нее знак # и добавьте в конце строки


.rb

Должно получиться
AddHandler cgi-script .cgi .rb

Сохраните изменения, закройте файл и перезагрузите компьютер.


Нам надо убедиться, что Ruby заработал. Для этого в папке
C:\Apache24\htdocs создайте текстовый файл и запишите в него следующий
код (файл ru1.rb в папке «Глава2» zip-архива):

#! C:/Ruby/bin/ruby
puts "Content-type: text/html\n\n"
puts "OK !"

33
Сохраните этот файл под именем ru1.rb. Откройте ваш браузер и введите
в строке адреса http://localhost/ru1.rb. Если появилось сообщение «OK !», зна-
чит, все в порядке (рис. 2.5.8).

Рис. 2.5.8. Проверяем работу Ruby

Осталось добавить, что файлы ваших проектов необходимо помещать в


папку htdocs по адресу C:\Apache24\htdocs, а просматривать готовые страницы
сайтов — в браузере по адресу http://localhost/ (для файла index.rb) или
http://localhost/имя_страницы.rb, например http://localhost/primer.rb или
http://localhost/test/primer.rb. Путь к интерпретатору (первая строка) во всех
ruby-файлах на локальном хостинге должен записываться так:

#! C:/Ruby/bin/ruby

2.6. Устанавливать ли базу данных?

В основе общепринятого подхода к созданию большинства сайтов лежит


принцип хранения всех данных в базе, например популярной MySQL или дру-
гих. Причем этот подход никак не зависит от типа хранимой информации. А зря.
Один из критериев, по которым можно разделить сайты, — это наличие или от-
сутствие секретной информации: личных данных, логинов, паролей. Например,
интернет-магазин скрывает множество конфиденциальной информации. А сайт
поддержки моей книги «JavaScript. Готовые программы» вообще не имеет ни
грамма секретов. Зададимся вопросом: нужна ли сайту поддержки база данных?
Ведь вся его информация открыта и доступна любому посетителю, а значит, нет
никакого смысла что-то скрывать. Так не проще ли на таком ресурсе хранить
все данные в простых текстовых файлах? Ответ очевиден: конечно, проще!
К чему я веду? По моему мнению, начинающим программистам (подчер-
киваю — именно начинающим) в сайтах с открытой информацией для ее хра-
нения лучше использовать текстовые файлы. Такая система хранения проще, не
требует написания sql-запросов, не нуждается в подключении к БД, а значит,
быстрее разрабатывается и внедряется. Подчеркну: для начинающего — это
очень важный момент. В первых проектах неопытных программистов всегда
ждет много сложностей: необходимо написать качественные страницы, доба-
вить к ним таблицы стилей, «прикрутить» сценарии на JavaScript, создать пару
серверных программ, которые будут взаимодействовать с отдельными страни-
цами. Поэтому на первых порах не стоит усложнять себе жизнь — на началь-
ном этапе освоения профессии web-разработчика делайте простые сайты с хра-

34

Powered by TCPDF (www.tcpdf.org)


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

Рис. 2.6.1. Сайт СУБД MySQL

Вы все-таки решили установить на своем компьютере базу данных? Могу


посоветовать вам три наиболее популярных из них.
1. СУБД (система управления базами данных (БД)) — MySQL. Старейшая
база данных, применяющаяся в web-проектах с 1995 г. Фактически в свое время
задала стандарты для баз данных, используемых при создании сайтов. Разраба-
тывается и поддерживается корпорацией Oracle. Обладает множеством досто-
инств, функционирует на серверах практически всех хостинг-провайдеров. Но
есть и один существенный недостаток — в последние годы данная БД совер-
шенствуется и обновляется довольно медленно. Сайт, где можно скачать дан-
ную БД, — https://www.mysql.com/ (рис. 2.6.1).
2. Система управления базами данных PostgreSQL. На сегодняшний день
один из лидеров по популярности среди разработчиков интернет-проектов. Со-
здавалась как открытый проект в Калифорнийском университете в Беркли. Ра-

35
ботает во всех основных операционных системах, в том числе Windows. Для
этой ОС существуют автоматические установщики. Необходимо отметить, что
последние версии СУБД доступны только для 64-разрядной ОС Windows, а для
32-разрядной можно скачать версии не выше 10.16. Сайт БД — https://
www.postgresql.org/ (рис. 2.6.2).

Рис. 2.6.2. Сайт СУБД PostgreSQL

Рис. 2.6.3. Сайт СУБД SQLite

36
3. СУБД SQLite. Это библиотека на языке C, которая реализует пол-
нофункциональный механизм базы данных SQL. Файлы, созданные SQLite, яв-
ляются кроссплатформенными и обратно совместимыми. При этом разработчи-
ки обещают сохранить формат файлов в таком виде до 2050 г. Также разработ-
чики утверждают, что их СУБД — самая часто используемая в мире. Впрочем,
это заявление расходится со многими исследованиями по определению попу-
лярности баз данных. Сайт БД — https://www.sqlite.org/ (рис. 2.6.3).

2.7. Редакторы кода

Полноценную среду разработки невозможно представить без установки


на ваш компьютер настоящего текстового редактора, специально предназна-
ченного для создания программ.
Конечно, весь код можно писать в обычном «Блокноте». Но гораздо луч-
ше и рациональнее пользоваться специализированным редактором. Такие при-
ложения намного удобнее: они подсвечивают код, предлагают синтаксические
подсказки, позволяют менять кодировку документов, сохраняют файлы в раз-
ных форматах.
В нашем случае необходим редактор, который позволит:
 выполнять качественную разметку;
 создавать файлы с таблицами стилей;
 писать сценарии на JavaScript;
 делать серверные программы обработки данных на одном из языков,
про интерпретаторы которых мы говорили в разделах 2.2–2.5.
Этому принципу удовлетворяют многие редакторы. Рассмотрим несколь-
ко из них. Обращаю ваше внимание: процесс установки одного редактора и его
возможности я опишу подробно, а об остальных расскажу более сжато.

Notepad++
Скачать его можно по адресу https://notepad-plus-plus.org/downloads/
(рис. 2.7.1).
Устанавливается редактор следующим образом. Запустите скачанный
файл. В первую очередь появится окно выбора языка программы. Оставляем
русский (рис. 2.7.2) и нажимаем кнопку «ОК».
В следующем окне нажимаем кнопку «Далее» (рис. 2.7.3). Затем нажати-
ем кнопки «Принимаю» соглашаемся с условиями лицензии (рис. 2.7.4).
Теперь нам необходимо выбрать папку для установки программы. По
умолчанию предлагается C:\Program Files\Notepad++. Думаю, такой вариант
установки подходит всем, поэтому данную папку оставляем без изменений (рис.
2.7.5).
Дальше будет окно с выбором компонентов, которые можно установить
вместе с программой. Этот раздел предназначен, в первую очередь, для опыт-
ных программистов, каковыми мы пока не являемся. Поэтому здесь ничего не
меняем, жмем кнопку «Далее» (рис. 2.7.6).

37
Рис. 2.7.1. Сайт с файлами редактора Notepad++

Рис. 2.7.2. Выбираем язык ПО

38
Рис. 2.7.3. Нажимаем кнопку «Далее»

Рис. 2.7.4. Соглашаемся с условиями лицензии

39
Рис. 2.7.5. Выбираем папку для установки программы

Рис. 2.7.6. Здесь ничего не меняем, жмем кнопку «Далее»

40
На последнем этапе, непосредственно перед началом установки, на вклад-
ке появится пункт «Create Shortcut on Desktop» (рис. 2.7.7). То есть нам предла-
гают создать ярлык программы на рабочем столе. Делать это или нет — зависит
от вашего желания. Замечу, что после установки редактора ссылка на него до-
бавится в контекстное меню. Достаточно будет навести указатель мыши на
файл, щелкнуть правой клавишей и в списке возможных действий вы увидите
строку «Edit with Notepad++». Кликните по ней — и файл будет открыт в редак-
торе.
Разобравшись с вопросом, необходим ли ярлык на рабочем столе или нет,
нажмите кнопку «Установить». Дождитесь окончания процесса (рис. 2.7.8).
После завершения установки вам нужно будет принять еще одно решение:
сразу запустить программу или отложить это дело на потом. Для этого оставьте
или снимите «галочку» в пункте «Запустить Notepad++» (рис. 2.7.9). Теперь
нажмите кнопку «Готово». Поздравляю — отныне на вашем компьютере уста-
новлен профессиональный текстовый редактор!
Познакомимся с ним поближе. Как выглядит редактор, вы можете видеть
на рисунке 2.7.10.
Notepad++ позволяет:
 выполнять поиск по файлу в разных направлениях;
 производить замену по шаблону;

Рис. 2.7.7. Нажимаем кнопку «Установить»

41
Рис. 2.7.8. Дождитесь окончания процесса

Рис. 2.7.9. Завершение установки

42
Рис. 2.7.10. Редактор Notepad++ с открытым в нем файлом

 устанавливать кодировки, в том числе необходимую для наших сайтов


UTF-8;
 менять страницы, форматируя их по стандартам разных операционных
систем — Windows, Unix или macOS;
 подсвечивать код, выделяя разные по назначению фрагменты, операто-
ры, функции, переменные, комментарии и т. д.;
 выбирать синтаксис документа (благодаря чему меняются варианты
подсветки кода), например HTML, CSS, JavaScript, а также PHP, Python, Perl и
Ruby;
 сохранять файлы с разным расширением;
 менять стили оформления окна программы — вариантов достаточно,
найдется любой по желанию;
 выполнять еще многие и многие операции.
Как известно, на вкус и цвет товарищей нет, но лично мне Notepad++ ка-
жется наилучшим вариантом для разработчика. Остальные редакторы, на мой
взгляд, менее удобны и функциональны.

Visual Studio Code


Страница редактора — https://code.visualstudio.com/Download.
Разработан в 2015 г. компанией Microsoft. Создатели позиционируют его,
в первую очередь, как «легкий» редактор для web-проектов.

43
Скачивать лучше вариант «System Installer» (рис. 2.7.11). Эта версия про-
граммы позволяет установить ее для всех пользователей вашего компьютера.
Выбирайте файл для скачивания в соответствии с разрядностью вашей опера-
ционной системы. О том, как ее определить, мы говорили в разделе 2.1 данной
главы.

Рис. 2.7.11. Страница загрузки редактора Visual Studio Code

Рис. 2.7.12. Редактор Visual Studio Code с набранным в нем сценарием на JavaScript

44
Редактор (рис. 2.7.12) поставляется с английским языком интерфейса по
умолчанию. Однако в программу встроена функция русификации. Правда,
Visual Studio Code снабжена довольно причудливым механизмом поиска в ее
собственных недрах инструкции по установке русского языка. Но вы можете
найти многочисленные описания процесса, введя в поисковой системе, напри-
мер Яндекс, следующий запрос: «visual studio code русский язык».
К достоинствам программы можно отнести наличие автоматической про-
верки кода на его соответствие синтаксису.
Кроме того, Visual Studio Code позволяет менять стили оформления окна
программы. По умолчанию включена тема с черным интерфейсом.
В редактор встроена поддержка всех необходимых нам синтаксисов:
HTML, CSS, JavaScript, а также PHP, Python, Perl и Ruby.
Для расширения функциональности Visual Studio Code существует мно-
жество плагинов.

Atom
Страница редактора — https://atom.io/. Вам сразу будет предложен файл,
соответствующий вашей операционной системе и ее разрядности (рис. 2.7.13).

Рис. 2.7.13. Сайт редактора Atom

Программное обеспечение устанавливается очень просто. От пользовате-


ля требуется лишь запустить скачанный файл. Все остальные действия, в том
числе и первый запуск, будут выполнены в автоматическом режиме.
Как и предыдущий редактор, Atom (рис. 2.7.14) поставляется с англий-
ским языком интерфейса. Описывать русификацию этого ПО мы тоже не ста-
нем. Вы можете найти многочисленные инструкции по ее реализации, введя в
поисковой системе следующий запрос: «Atom русский язык».

45
Рис. 2.7.14. Редактора Atom с введенным кодом

Как и в Visual Studio Code, в редактор Atom встроена автоматическая про-


верка кода. По умолчанию включена тема с черным интерфейсом.
Первая версия редактора вышла зимой 2014 г.

Sublime Text
Страница редактора — https://www.sublimetext.com/3.
Скачивая программу (рис. 2.7.15), выбирайте файл в соответствии с разрядно-
стью вашей операционной системы. О том, как ее определить, рассказано в раз-
деле 2.1 данной главы.

Рис. 2.7.15. Страница для выбора файла загрузки редактора Sublime Text

46
Процесс инсталляции довольно несложный. Можно оставить все настрой-
ки, предложенные установщиком, без изменений.
Как выглядит окно редактора, показано на рисунке 2.7.16.

Рис. 2.7.16. Окно редактора Sublime Text

И уже по традиции, как и в двух предыдущих случаях, сообщаю вам, что:


 по умолчанию включена тема с темным интерфейсом;
 редактор поставляется с английским языком интерфейса, но в Интернете
есть много описаний процесса русификации, надо только набрать в поисковой
системе запрос «Sublime Text русский язык», чтобы найти необходимую ин-
формацию.
Надеюсь, что приведенные описания помогут вам при выборе редактора
кода.

47
3. Подготовительные работы

Итак, вы приступаете к написанию первых сценариев для вашего будуще-


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

3.1. Алгоритм действий

Жизнь web-программиста очень облегчает наличие в его голове или на


бумаге четко продуманного алгоритма создания проекта. Особенно если этот
проект выполняется в одиночку.
Рискну предположить, что свои первые сайты начинающие кодеры пол-
ностью делают самостоятельно. В отличие от web-студии, где каждый специа-
лист отвечает за свой участок, у разработчика-индивидуала должны быть под
контролем все участки сразу. А это создает определенные трудности — не имея
опыта, сложно правильно распределить усилия и рабочее время.
Попробую рассказать вам о рациональном плане выполнения того или
иного проекта.
На мой взгляд, наиболее оптимальным выглядит следующий порядок
действий:
1) определите, какой сайт вы хотите создать;
2) составьте перечень будущих страниц и список материалов (контента),
которые необходимо разместить на сайте;
3) решите, как будут выглядеть страницы и разработайте самостоятельно
или с чьей-то помощью дизайн ресурса;
4) сверстайте все страницы, наполните их содержимым, пропишите в от-
дельных файлах стили;
5) внедрите в необходимые страницы код JavaScript или файлы со сцена-
риями;
6) проверьте, как смотрится и работает ваш сайт в разных браузерах.
Примерно так.
Давайте рассмотрим этапы 4–6 более подробно.

48
3.2. Шаблон страницы

Консорциум Всемирной Паутины — World Wide Web Consortium, или


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

<!DOCTYPE html>

Во-вторых, необходимо объявить начало разметки:

<html>

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

<html lang="ru">

Теперь можно разместить контейнер для заголовочного блока:

<head>
...
</head>

Внутрь контейнера вы можете добавить различные метатеги — их можно


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

<meta charset="utf-8">

Для поисковых систем очень важно, чтобы в заголовочной части были


обязательно указаны теги description и keywords. Тег description содержит
описание страницы, а тег keywords — список ключевых слов для текущего до-
кумента. «Операцию» добавления тегов можно выполнить следующим образом:

<head>
...
<meta name="description" content="описание страницы">
<meta name="keywords" content="ключевые слова">
</head>

Естественно, список возможных метатегов не ограничивается этими тре-


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

<meta name="viewport" content="width=device-width,


initial-scale=1">

49
Впрочем, разных полезных метатегов немало — copyright, robots и дру-
гие. О них лучше посмотреть информацию на сайте Консорциума Всемирной
Паутины: https://www.w3.org/.
Наконец, в заголовочной части документа непременно разместите тег title:

<head>
...
<title>Заголовок страницы</title>
</head>

Он необходим для текста, который является заголовком или названием


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

<head>
<meta charset="utf-8">
<meta name="description" content="описание страницы">
<meta name="keywords" content="ключевые слова">
<title>Заголовок страницы</title>
</head>

Теперь добавим тело документа. Поскольку страница у нас еще пустая,


это будет выглядеть совсем просто:

<body>

</body>

Когда все начальные компоненты готовы, можно собрать шаблон в еди-


ное целое (файл Шаблон.html в папке «Глава3» zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="description" content="описание страницы">
<meta name="keywords" content="ключевые слова">
<title>Заголовок страницы</title>
</head>

<body>

</body>
</html>

Обратите внимание: завершает описание документа закрывающий тег


</html>.
Шаблон готов. Можно добавлять на страницу необходимые элементы:
текст, рисунки, таблицы, ссылки и т. д.

50
3.3. Добавляем элементы

Этот процесс описать сложнее всего, ведь вариантов размещения элемен-


тов бесконечное множество. И все-таки сделаем акцент на одном моменте: для
начала необходимо решить вопрос с выбором метода позиционирования эле-
ментов. Тут тоже большое разнообразие способов. Рассмотрим всего несколько
самых простых вариантов из огромного количества возможных приемов.
Например, можно поместить в тело документа контейнер <div> </div> и
уже внутри него позиционировать отдельные элементы страницы, а также сам
контейнер относительно границ окна браузера:

<div>
элемент 1
элемент 2
элемент 3
и так далее
</div>

Расположение элементов и контейнера устанавливается в настройках таб-


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

<div id=”d1”>
элемент 1
элемент 2
</div>

<div id=”d2”>
элемент 3
элемент 4
элемент 5
</div>

<div id=”d3”>
элемент 6
элемент 7
</div>

и так далее

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


размещаются необходимым образом внутри контейнеров, а затем уже «расстав-
ляются» сами контейнеры (порядок действий может быть обратным). Располо-
жение элементов внутри контейнеров и самих контейнеров также устанавлива-
ется настройками таблицы стилей. Примеры того, как можно позиционировать
различные блоки, показаны на рисунке 3.3.1.

51

Powered by TCPDF (www.tcpdf.org)


Рис. 3.3.1. Простое и сложное позиционирование блоков

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


тейнеры, а ячейки таблицы. Примерно так:

<table>
<tr>
<td>элемент 1</td>
<td>элемент 2
элемент 3</td>
</tr>

<tr>
<td>элемент 4
элемент 5
элемент 6</td>
<td>элемент 7</td>
</tr>
и так далее
</table>

52
Внутри ячеек элементы могут позиционироваться с применением настро-
ек стилей, а могут просто располагаться один за другим. Кроме того, применяя
атрибуты colspan и rowspan, вы способны получить более разнообразные вари-
анты расположения ячеек, чем это показано в структуре таблицы выше. Приме-
ры того, как по-разному можно располагать ячейки в таблице, изображены на
рисунке 3.3.2.

Рис. 3.3.2. Простые и сложные варианты размещения элементов


в ячейках таблицы

Добавлю, что эти три способа можно использовать как при верстке доста-
точно простых страниц, так и при компоновке сложных документов.
К сожалению, способ размещения элементов в сегментах таблицы имеет
существенный недостаток. Это «жесткий» каркас таблицы, в результате чего
отсутствует возможность изменить расположение ячеек при уменьшении раз-
меров окна браузера. На рисунке 3.3.3 показано, как меняется структура доку-
мента, созданного на основе блоков <div> </div> при просмотре страницы в
браузере компьютера и в браузере мобильного устройства. Если на большом
экране блоки располагаются горизонтально, то на маленьком они перестроятся
53
в вертикальную последовательность. Но такой результат невозможно получить,
если применять табличную компоновку документа.
Наконец, есть еще один способ последовательного расположения элемен-
тов, рассчитанный на самый простой случай:
<body>
элемент 1
элемент 2
элемент 3
и так далее
</body>

Здесь элементы не позиционируются, а просто следуют один за другим.


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

Рис. 3.3.3. Расположение блоков при просмотре страницы


в браузере компьютера и браузере мобильного устройства

3.4. Таблицы стилей


Итак, мы добавили в тело документа необходимое содержимое. Настало
время:
 правильно разместить элементы;
 оформить их внешний вид и придать необходимые размеры.
Сделать это нам помогут таблицы стилей. Рассказывать о них я не ста-
ну — тема большая и многообразная, лучше прочитайте об этом специальную
книгу, например «Клиентские технологии веб-дизайна. HTML5 и CSS»
А. В. Дикова (издательство «Лань», Санкт-Петербург, 2019) или любую другую,
посвященную данной теме. Я расскажу вам только о способах внедрения таб-
лиц стилей в документ.

54
Сомнительные практики
Мы не рассматриваем ситуации, когда стили указываются непо-
средственно в элементе разметки, например, так: <button style="...">.
Считается хорошим тоном отделять стили от разметки, а показанный ва-
риант не соответствует этому принципу.

Способ первый — размещение таблицы в заголовочной части документа.


Для этого внутри блока head вставьте теги style:

<head>
...
<style>
</style>
</head>

И уже в контейнере <style> </style> запишите все необходимые настрой-


ки. Например, таким образом:

<style>
body {background: #FFFFFF; color: #000000;}
a {text-decoration: none; color: #0000CC;}
...
</style>

Нужно признать, что данный способ имеет один серьезный недостаток:


если настроек много, файл страницы получится очень большим и работать с
ним программисту станет неудобно. Поэтому предпочтительней другой вари-
ант — размещение таблиц стилей во внешних файлах. Подключить такие фай-
лы к текущему документу очень просто. Для этого также существует 2 способа.
Первый — с использованием директивы @import. В этом случае подклю-
чение необходимо выполнить внутри блока head таким образом:
<style>
@import url(css.css);
</style>

где css.css — адрес файла с таблицей стилей. Обратите внимание: любой внеш-
ний файл с таблицей стилей должен иметь расширение .css. Естественно, что
внутри такого файла теги <style> </style> не указываются — описание стилей
начинается прямо с первой строки.
Второй способ — с применением элемента разметки link. В этом случае
файл таблицы стилей привязывается к документу внутри блока head так:

<link rel="stylesheet" href="css.css">

55
Какой вариант избрать — с директивой @import или элементом link —
решать вам. Замечу, что второй вариант применяется разработчиками гораздо
чаще, чем первый.

3.5. Размещаем сценарий

Ситуация с размещением сценариев на JavaScript очень похожа на то, что


мы делали с таблицами стилей. Есть два подхода к внедрению кода скриптов в
страницу.
Первый способ — размещение кода в заголовочной части документа.
С этой целью внутри блока head вставьте теги script:

<head>
...
<script>

</script>
</head>

Теперь в контейнере <script> </script> запишите все необходимые пере-


менные и функции. Вот пример такой записи:

<script>
window. addEventListener("load", function()
{
let w=screen.width;
let h=screen.height;
document.getElementById("si").innerHTML=w+" x "+h;
});
</script>

Так же как и в случае с таблицами стилей, данный способ имеет анало-


гичный недостаток: если код довольно объемный, файл страницы получится
очень большим и работать с ним станет неудобно. Поэтому предпочтительней
другой вариант — размещение сценариев на JavaScript во внешних файлах.
Подключают такие программы к текущему документу следующим образом.
Необходимо внутри блока head разместить теги <script> </script>, при этом в
открывающем теге указать адрес необходимого файла:

<script src="js.js"></script>

где js.js — адрес файла со сценарием. Обратите внимание: любой внешний


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

56
4. Некоторые особенности программирования

Эта глава не является учебником по программированию на JavaScript.


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

4.1. События

События на страницах сайта происходят почти непрерывно. Загружаете


ли вы документ, нажимаете кнопку мыши, прокручиваете страницу или просто
перемещаете указатель — все это для браузера события. Их принято идентифи-
цировать по типу (иногда говорят не тип события, а имя события).
Рассмотрим некоторые события, которые обрабатываются наиболее часто.
Событие load происходит при загрузке страницы в браузер. Оно генери-
руется сразу после того, как html-разметка и рисунки (картинки, изображения,
фото) полностью отображаются на экране компьютера. Такое же событие про-
исходит и при получении ответа от сервера, на котором стоит программа, вы-
зываемая с применением технологии Ajax.
Событие click возникает при щелчке кнопкой мыши на элементе разметки
страницы. Это могут быть ссылки, кнопки, изображения, текстовые блоки, слои

57
(событие click, как, впрочем, и некоторые другие, может также происходить
даже на визуально пустом месте документа).
Событие mouseover происходит, когда указатель мыши перемещается
внутрь границ элемента.
Событие mouseout противоположно предыдущему и возникает, когда
указатель мыши покидает границы элемента.
Событие mousemove генерируется в продолжение всего времени, пока
указатель мыши перемещается над элементом.
Событие mousedown происходит при нажатии кнопки мыши, когда ее
указатель находится над элементом.
Событие mouseup возникает при отпускании кнопки мыши (при этом
указатель должен находиться над элементом, для которого генерируется со-
бытие).
Событие dragstart происходит, когда посетитель начинает перетаскивать
элемент по странице.
Событие dragover возникает в процессе перетаскивания элемента по
странице.
Событие drop происходит в момент отпускания перетаскиваемого эле-
мента в точке назначения.
Событие resize возникает при изменении размеров окна браузера.
Событие focus случается при получении элементом фокуса (например,
когда курсор будет установлен в текстовом поле).
Событие blur происходит, когда элемент теряет фокус.
Событие submit сопровождает отправку формы, например нажатием
кнопки. Обычно программисты к данному событию привязывают проверку
данных, введенных пользователем.
Событие change происходит на элементах <select>, <input> и <textarea>,
когда меняются их значения.
Событие scroll возникает при прокручивании страницы в окне браузера
или во фрейме.
Событие paste происходит в момент вставки содержимого буфера обмена
в элемент документа в позицию курсора (например, в однострочное или много-
строчное текстовые поля).
Событие keydown происходит в момент нажатия любой кнопки на кла-
виатуре.
Событие keyup возникает в момент отпускания после нажатия клавиши.
Событие keypress — это «симбиоз» двух предыдущих событий (клавиша
нажата и отпущена).
Событие select происходит в текстовых полях документа при выделении в
них какого-либо фрагмента или всего текста в целом.
Событие mouseenter очень похоже на mouseover. Отличие в том, что со-
бытие mouseenter не всплывающее и не отменяемое (из этого понятно, что
mouseover всплывающее и отменяемое).

58
Событие mouseleave, соответственно, похоже на mouseout. Разница такая
же, как и в случае, описанном в предыдущем определении. Событие mouseleave
не всплывающее и не отменяемое.
Разработчик сайта заранее выбирает, какие события на странице должны
вызвать программный отклик, а какие — нет.

4.2. Обработчики событий

Обработчик события — это некоторая функция, выполняющая опреде-


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

<head>
...
...
...
<script>
регистрация обработчика, например, для клика на изображении

программный код, обрабатывающий щелчок на изображении


</script>
</head>

Почему? Дело в том, что анализатор браузера при загрузке документа


считывает его «сверху вниз». Но у нас обработчик объявлен в заголовочной ча-
сти, то есть еще до того, как построена объектная модель документа. В резуль-
тате браузер еще не видит изображение, а мы уже пытаемся привязать к кар-
тинке какое-то событие. В итоге обработчик не будет зарегистрирован.
Чтобы избежать таких неприятностей, я советую поступать следующим
образом. Регистрировать сначала обработчик уровня окна браузера для события
load, а уже внутри него записывать остальные обработчики. Сделать это можно
следующим образом:

59
<head>
...
...
...
<script>
регистрация обработчика события load уровня окна
{
регистрация обработчика для клика на изображении
}

программный код, обрабатывающий щелчок на изображении


</script>
</head>

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

Традиционные способы регистрации обработчиков


(считаются устаревшими)

Сразу оговорюсь, что данные способы все еще входят в стандарты, рабо-
тают и долго будут работать во всех браузерах, проходят любую валидацию (об
этом — в главе 5, раздел 5.2). Однако среди программистов считаются уста-
ревшими.
Почему же я все-таки решил посвятить им часть данной темы? Дело в том,
что эти способы все еще применяются в некоторых программах и упоминаются
в литературе по JavaScript.
Чтобы вызвать обработчик в ответ на какое-либо событие, надо добавить
к имени события префикс (приставку) on, например onload, onclick, onfocus,
onsubmit и т. д. (так формируется имя свойства обработчика). А затем указать
анонимную или именованную функцию, которая будет «откликаться» на то или
иное событие.
Обычно это делается так:

window.onload=func;

document.getElementById("im").onclick=func;

или так

window.onload=function()
{
...
};
document.getElementById("im").onclick=function()
{
...
};

60
Сомнительные практики
Мы не рассматриваем ситуации, когда вызов обработчика события
помещается непосредственно в элемент разметки, например, так:
<button onclick="func()">. Считается хорошим тоном отделять про-
граммный код от разметки, а размещение вызова функции внутри тега
элемента не соответствует этому принципу.

Регистрация обработчиков методом addEventlistener


(современный способ)

Метод принимает три аргумента. Два из них обязательные. Первый — тип


события. Второй — функция, «реагирующая» на событие, или имя внешней
функции. Третий аргумент — булево значение, которое допустимо не указы-
вать. Например, третий аргумент может определять, в какой стадии — всплы-
тия или погружения — будет вызван обработчик. Если оставить третий аргу-
мент пустым или явно указать его значение false, то обработка произойдет в
фазе всплытия. При значении true обработчик будет запущен в фазе погруже-
ния. Кроме того, в качестве третьего аргумента могут быть иные данные,
например указание разрешать вызов обработчика не более одного раза (о чем я
расскажу в следующем разделе).
Обратите внимание: метод addEventListener обращается к любому собы-
тию без использования префикса on: надо писать load, click, focus, submit и т. д.
1. Обработчики событий окна load, scroll и resize зарегистрировать проще
всего. Например, для события load это можно сделать так

window.addEventListener("load", start);
window.addEventListener("load", func);

или так

window.addEventListener("load", start);
window.addEventListener("load", function()
{
...
});

2. Регистрация обработчика как ответ на событие load. Например так:

window.addEventListener("load", function()
{
document.getElementById("im").addEventListener("click", func);
});

61
или так:
window.addEventListener("load", function()
{
document.getElementById("im").addEventListener("click", function()
{
...
});
});
3. Регистрация сразу нескольких обработчиков. Пример:
window.addEventListener("load", function()
{
document.getElementById("im").addEventListener("click", func);
document.getElementById("im").addEventListener("mouseout", doc);
document.getElementById("im").addEventListener("mouseover", res);
});
Кстати, можно записать и вот так:
window.addEventListener("load", function()
{
func();
doc();
res();
});
Правда, такой прием в практике разработчиков встречается крайне редко.
Его единственное достоинство — более короткий код.
4. Комбинированная регистрация — когда обработчики одних событий
регистрируются в начале, а другие — в процессе выполнения сценария. Может
выглядеть следующим образом:
window.addEventListener("load", function()
{
document.getElementById("im").addEventListener("click", func);
});
function func()
{
...
document.getElementById("te").addEventListener("click", doc);
}
function doc()
{
...
}
5. Еще один комбинированный случай — регистрация обработчика и од-
новременный запуск внешней функции:
window.addEventListener("load", function()
{
document.getElementById("im").addEventListener("click", func);
sum();
});
Метод addEventListener позволяет комбинировать регистрацию обработ-
чиков весьма разнообразно.
62
Еще один способ регистрации обработчика
методом addEventListener

Этот способ в определенном смысле проще. Чтобы понять его, посмотри-


те пример вот такой страницы (файл 4.2_1.html в папке «Глава4» zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Регистрация обработчика</title>
</head>
<body>
...
<p id="te">Текст</p>
...
</body>
</html>
<script>
document.getElementById("te").addEventListener("click", doc);
function doc()
{
...
}
</script>

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

window.addEventListener("load", function()
{
...
});

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

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

63
Главное достоинство метода addEventListener в том, что его можно вы-
зывать сколько угодно раз и зарегистрировать с его помощью сколько угодно
обработчиков для одного и того же события на одном и том же элементе стра-
ницы:

window.addEventListener("load", function()
{
document.getElementById("im").addEventListener("click", func);
document.getElementById("im").addEventListener("click", doc);
document.getElementById("im").addEventListener("click", res);
});

Еще одно замечание: есть ситуации, когда классические приемы бессиль-


ны и без метода addEventListener не обойтись. Дело в том, что этот метод поз-
воляет привязать обработчик к любому элементу DOM, а не только к HTML-
элементам. Например, в случае регистрации обработчика для события
DOMContentLoaded можно воспользоваться только методом
addEventListener. Так что это еще один плюс в пользу применения
addEventListener.
И, наконец, последнее соображение. Если, например, вы пытаетесь мо-
дернизировать старую программу и внедрить в нее вместо классических спосо-
бов регистрацию обработчиков методом addEventListener, будьте вниматель-
ны и осторожны: в очень редких случаях такая замена может несколько изме-
нить результаты действия сценария.

4.3. Остановка, удаление обработчика

У программиста часто бывают ситуации, когда необходимо зарегистриро-


вать обработчик, вызывающийся ограниченное количество раз, а не бесконечно.
На примере метода addEventListener рассмотрим 3 варианта, как можно по-
ступить в этом случае.
Вариант 1. Нам необходимо вызвать обработчик только один раз. Как я
уже писал выше, метод addEventListener может третьим аргументом прини-
мать указание реагировать на определенное событие на выбранном элементе
однократно. Остальные аналогичные события на этом элементе не вызовут ре-
акции сценария. Делается это, например, так (приведен упрощенный вариант
разметки и размещения сценария; файл 4.3_1.html в папке «Глава4» zip-архива):

<p id="te">ТЕКСТ</p>

<script>
document.getElementById("te").addEventListener("click", func,
{once: true});

let i=0;
function func()
{
i++;

64
alert ("Количество кликов: "+i);
}
</script>

Здесь третьим аргументом {once: true} передано указание регистратору


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

<p id="te">ТЕКСТ</p>

<script>
document.getElementById("te").addEventListener("click", func);
// Код сценария
...
...
...
// Если выполняется заданное условие, обработчик удаляется
if(условие)
document.getElementById("te"). removeEventListener("click", func);
</script>

Важно: содержимое круглых скобок при регистрации обработчика мето-


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

document.getElementById("te").addEventListener("click", func);
...
document.getElementById("te"). removeEventListener("click", func);

Действующий пример кода с использованием удаления обработчика


(файл 4.3_2.html в папке «Глава4» zip-архива):

<p id="te">ТЕКСТ</p>

<script>
document.getElementById("te").addEventListener("click", func);

let a=0;
function func()
{
a++;

if(a==5)
document.getElementById("te").removeEventListener("click", func);
alert(a);
}
</script>
65
Вариант 3. Вновь необходимо вызвать обработчик несколько раз. Опять
возьмем в качестве примера число 5. Способ, который продемонстрирован ни-
же, позволяет остановить обработчик, не удаляя его (файл 4.3_3.html в папке
«Глава4» zip-архива):

<p id="te">ТЕКСТ</p>

<script>
document.getElementById("te").addEventListener("click", func);

let a=0;

function func()
{
a++;

if(a<6)
alert(a);
}
</script>

В этом сценарии выполнение функции func останавливается после 5 кли-


ков на строке текста. Удобство данного метода в том, что на определенном эта-
пе выполнения программы вы можете обнулить переменную a, и функция func
возобновит свою работу при очередном клике на тексте. Посмотрим пример,
демонстрирующий «восстановление» работоспособности функции func (файл
4.3_4.html в папке «Глава4» zip-архива):

<p id="te">ТЕКСТ</p>
<p id="xt">ЕЩЕ ТЕКСТ</p>

<script>
document.getElementById("te").addEventListener("click", func);
document.getElementById("xt").addEventListener("click", doc);

let a=0;
function func()
{
a++;
if(a<6)
alert(a);
}
function doc()
{
a=0;
}
</script>

Здесь клик на строке с текстом «ЕЩЕ ТЕКСТ» обнулит переменную a.


Причем это может быть сделано на любом этапе: и после 5 кликов, и после 2–3.

66
Впрочем, аналогичный результат может быть достигнут и таким способом
(файл 4.3_5.html в папке «Глава4» zip-архива):

<p id="te">ТЕКСТ</p>
<p id="xt">ЕЩЕ ТЕКСТ</p>
<script>
document.getElementById("te").addEventListener("click", func);
document.getElementById("xt").addEventListener("click", doc);
let a=0;

function func()
{
a++;

if(a==5)
document.getElementById("te").removeEventListener("click", func);
alert(a);
}
function doc()
{
a=0;
document.getElementById("te").addEventListener("click", func);
}
</script>

В последнем варианте после 5 щелчков на основном тексте обработчик


будет удален, а после клика на дополнительном тексте зарегистрирован вновь.
Если же вы щелкнули на основном тексте, допустим, 2 раза, а затем кликнули
на строке «ЕЩЕ ТЕКСТ», обработчик будет не удален, а перезаписан.

4.4. Глобальные и локальные переменные

В предыдущих разделах в некоторых примерах мы уже пользовались пе-


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

67
let i=1;
или так
let i="ТЕКСТ";

или так
let i;

Здесь let — это оператор объявления переменных.


В первых двух случаях мы сразу присваиваем переменной текущее значе-
ние, а в последнем случае это значение будет присвоено позже на каком-то из
этапов выполнения программы.
JavaScript позволяет объявить сразу несколько переменных, например так:
let i, t, b;

И даже вот так:


let i, t=0, b;

Поменять тип данных, сохраненных в переменной на предыдущем этапе,


очень просто:
let i=1;
i="Строка";

или
let i="Строка";
i=1;

Если вам необходимо преобразовать числовое значение из переменной в


строку, то сделать это можно, «сложив» число из переменной с пустой или за-
полненной строкой. Хотя бы вот так:
let i=1;
let b=i+" января - Новый год!";

Для преобразования строки в число применяйте специальную встроенную


функцию языка JavaScript — parseFloat:
let i="3.14";
let t=parseFloat(i);

Если необходимо определить тип переменной, воспользуйтесь операто-


ром typeof, например таким образом:

<script>
let a="Строка";
let b=typeof a;
alert(b);
</script>

68

Powered by TCPDF (www.tcpdf.org)


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

<script>
let i=1;
let t=0;

код сценария
</script>

В результате переменные i и t доступны в самых разных частях сценария.


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

function func()
{
let a=1;
...
}

В этом примере значение переменной a доступно только внутри функции


func. Обращение к переменной a из другой функции закончится неудачей.
Наконец, в «недрах» функции может быть своя иерархия переменных. То
есть одни из них оказываются глобальными внутри данной функции, а дру-
гие — локальными внутри цикла или условия, являющихся частью данной
функции. Для иллюстрации подобных ситуаций создайте HTML-файл и поме-
стите в него такой код (файл 4.4_1.html в папке «Глава4» zip-архива):

<script>
func();
function func()
{
let a=1;
if(a==1)
{
let v=0;
alert(v);
}

alert(v);
}
</script>

69
Запустите страницу в вашем браузере. Что вы увидите? Откроется первое
окно, на котором будет представлено значение переменной v — это число 0.
Нажмите «ОК» (или «Закрыть» в Яндекс.Браузере). Все. Хотя у нас в сценарии
есть еще вызов второго диалогового окна, оно не появится, так как переменная
v — локальная для тела функции и видна только внутри условия if(a==1) {...}.
Все то же самое будет происходить и с переменной, созданной внутри
цикла (файл 4.4_2.html в папке «Глава4» zip-архива):
<script>
func();
function func()
{
for(let c=0; c<2; c++)
alert(c);

alert(c);
}
</script>

В этом случае сначала откроется первое окно, на котором будет представ-


лено начальное значение переменной c — число 0. Нажмите «ОК». Появится
второе окно с новым значением переменной c — единицей. На этом выполне-
ние цикла завершается. Снова жмем «ОК» и убеждаемся, что третье диалоговое
окно не появилось, так как переменная c — локальная для тела функции и вид-
на только внутри цикла for(...) {...}.

4.5. let или var (или const)?


Давайте теперь обсудим еще одну тему: операторы объявления перемен-
ных. В предыдущих разделах вы видели, что все переменные в наших сценари-
ях объявлялись оператором let. Однако во многих программах, написанных не-
сколько лет назад, а также во многих даже современных книгах по программи-
рованию на JavaScript вы можете встретить операторы объявления переменных
var.
Дело в том, что многие годы у разработчиков существовал только один
способ объявить переменную — оператором var. В 2015 г. к этому оператору
добавился еще один — let, что породило некоторый разброс в написании про-
грамм.
Так все-таки var или let? Как же правильно объявлять переменную? Дело
в том, что заметить разницу между этими объявлениями в простых программах
чаще всего невозможно. И в одном, и в другом случае сценарии, приведенные в
главе 6 этой книги, будут работать одинаково, каким бы способом мы не объяв-
ляли переменные.
Но в чем же принципиальное отличие? Вот что на эту тему сообщает
Mozilla Foundation на сайте https://developer.mozilla.org/: «Область видимости
переменной, объявленной через var, это ее текущий контекст выполнения. Ко-
торый может ограничиваться функцией или быть глобальным для переменных,
70
объявленных за пределами функции». И еще: «Директива let позволяет объ-
явить локальную переменную с областью видимости, ограниченной текущим
блоком кода».
Разница все равно не ясна? Тогда вот вам два коротких сценария, которые
наглядно покажут это различие.
Создайте два html-файла. В один запишите такой код (файл 4.5_1.html в
папке «Глава4» zip-архива):
<script>
var i=1;
if(true)
{
var i=2;
}
alert(i);
</script>

Во второй (файл 4.5_2.html в папке «Глава4» zip-архива):


<script>
let i=1;
if(true)
{
let i=2;
}
alert(i);
</script>

Рис. 4.5.1. Результат выполнения сценария с переменными,


объявленными оператором var

71
Рис. 4.5.2. Результат выполнения сценария с переменными,
объявленными оператором let

По очереди запустите эти сценарии в браузере. Что вы видите? В первом


случае в диалоговом окне появится цифра 2 (рис. 4.5.1), а во втором — цифра 1
(рис. 4.5.2). А все потому, что вторая переменная let объявлена внутри блока if
и ее значение — 2 — видно только внутри этого блока. Надеюсь, теперь глав-
ная и принципиальная разница ясна.
Но эта разница не единственная. Посмотрим следующее отличие снова на
примере двух коротких сценариев. Первый (файл 4.5_3.html в папке «Глава4»
zip-архива):
<script>
alert(i);
var i=1;
</script>

Второй (файл 4.5_4.html в папке «Глава4» zip-архива):


<script>
alert(i);
let i=1;
</script>

Если по очереди запустить данные сценарии, то вы обнаружите, что в


первом случае появится диалоговое окно с сообщением «undefined», что озна-
чает «переменная не определена, но она существует». А во втором диалоговое
окно вообще не будет запущено. Это значит, что «переменная не существует».
72
Вывод: оператор let — более современный. Опытные разработчики сове-
туют использовать объявления let. Я придерживаюсь аналогичного мнения и
рекомендую везде применять let.
Теперь вернемся к первым двум примерам, где у нас объявлялись одно-
именные глобальные и локальные переменные. Точнее, к примеру с объявлени-
ем переменной let:

<script>
let i=1;
if(true)
{
let i=2;
}
alert(i);
</script>

Такой сценарий хорош только для демонстрации разницы между операто-


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

const v=document.getElementById("im").style;
v.width="100px";
v.opacity="0.5";
v.border="2px solid #0000CC";

Еще пример с данным оператором:

for(let k=1; k<3; k++)


{
let s="im"+k;
const v=document.getElementById(s).style;

v.width="100px";
v.opacity="0.5";
v.border="2px solid #0000CC";
}

В этом случае константа v создается каждый раз заново, поэтому в дан-


ном примере ошибка не возникнет.

73
Корректно будет работать и такой сценарий:
function func(z)
{
let s="im"+z;
const v=document.getElementById(s).style;
v.width="100px";
v.opacity="0.5";
v.border="2px solid #0000CC";
}

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


глобальными, как и переменные, объявленные операторами let или var.
И еще два момента:
 при объявлении константы ей сразу должно быть присвоено значение;
 имя константы не может совпадать с именами функций или переменных
let (и var) той же области видимости.

4.6. Меньше или больше переменных?

Ответ на этот вопрос зависит от того, какой фрагмент кода мы возьмемся


рассматривать.
Помните, в предыдущем разделе у нас был пример с оператором const?
Мы говорили, что его удобно использовать для сокращения программы, если в
ней, например, происходят многократные обращения к одному и тому же эле-
менту разметки:
const v=document.getElementById("im").style;
v.width="100px";
v.opacity="0.5";
v.border="2px solid #0000CC";
v. float="right";

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


document.getElementById("im").style.width="100px";
document.getElementById("im").style.opacity="0.5";
document.getElementById("im").style.border="2px solid #0000CC";
document.getElementById("im").style .float="right";

Как мы видим, в первом случае создание необязательной переменной v


полностью оправдано. Зато в следующем примере объявленные константы
окажутся явно лишними:
const v=document.getElementById("im1").style;
v.width="100px";
const w=document.getElementById("im2").style;
w.width="200px";

74
Если в программе происходит только однократное обращение к элементу,
проще переписать данный пример так:
document.getElementById("im1").style.width="100px";
document.getElementById("im2").style.width="200px";

В таком варианте налицо явная экономия кода.


Пример другого порядка. В теле функции есть два цикла:
function func()
{
...
for(let d=0; d<5; d++)
{
...
}
...
for(let f=0; f<3; f++)
{
...
}
...
}

Поскольку циклы работают по одному разу и последовательно друг за


другом, получается, что переменная d после выполнения соответствующего
блока кода уже не нужна, однако остается висеть в памяти компьютера. Так
может, имеет смысл использовать ее снова — теперь уже во втором цикле? Ре-
ализуем данный подход, переписав тело функции:
function func()
{
...
let d;
for(d=0; d<5; d++)
{
...
}
...
for(d=0; d<3; d++)
{
...
}
...
}

Тем самым мы чуть-чуть сэкономим на памяти, которая теперь занята не


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

4.7. Массивы

О массивах написано достаточно много. Я лишь напомню некоторые


«факты» и приведу один не совсем обычный пример использования массива.
Итак, начнем. Массивы — это объектный тип данных.
Объявить массив очень просто:
let mas=[элемент 1, элемент 2, ... элемент n];

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


выполнения сценария:
let mas=[];

Для получения отдельного элемента используются записи следующего


вида:
mas[0]
mas[1]
...
mas[n]

Чтобы узнать, сколько элементов в массиве, надо обратиться к свойству


length:
let d=mas.length;

Обратите внимание: индексы элементов массива начинаются с 0, а их ко-


личество отсчитывается с 1.
Добавить новый элемент массива тоже очень просто:

mas[n+1]=элемент n+1;

Начинающие программисты наиболее часто используют методы обработ-


ки массивов, которые приведены в таблице 4.7.1.
Таблица 4.7.1
Методы обработки массивов
Метод Описание
concat() Объединяет несколько исходных массивов и создает новый
indexOf() Выполняет поиск элемента в массиве
Формирует из элементов массива текстовую строку
join()
с разделителем, переданным аргументом

76
Продолжение табл. 4.7.1
Метод Описание
Удаляет последний элемент массива и возвращает
pop()
этот элемент
Добавляет новые элементы в конец массива и возвращает
push()
его новую длину
reverse() Выполняет пересортировку массива в обратном порядке
Удаляет элемент массива с индексом 0 и возвращает
shift()
этот элемент
Производит сортировку элементов массива по заданной
sort()
функции сравнения
Удаляет часть массива, а на его место добавляет новые
splice()
элементы
unshift() Добавляет элементы в начало массива

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


в сценариях. Однако массив может пригодиться и для создания определенных
визуальных эффектов. Примером служит страница, приведенная ниже (файл
4.7_1.html в папке «Глава4» zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Таймер загрузки</title>
<style>
div {text-align: center;}
.tim {width: 15px; height: 10px; background: #0000CC;
display: inline-block; margin: 1px;}
</style>

<script>
addEventListener("load", race);
let i=0;
let d=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];

function race()
{
let t="";

for(let a=0; a<=d[i]; a++)


t+="<div class='tim'></div>";

document.getElementById("loa").innerHTML=t;

i++;
if(i==d.length)
i=0;

77
setTimeout(race, 80);
}
</script>
</head>

<body>
<div id="loa"></div>

</body>
</html>

Такой сценарий можно использовать при создании индикатора процесса


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

<div id="loa"></div>

Есть счетчик элементов массива

let i=0;

и сам массив

let d=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,


14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];

Для упрощения примера сценарий запускается сразу после загрузки до-


кумента:

addEventListener("load", race);

Функция race, управляющая индикатором, вызывается каждые 80 мс

function race()
{
...
setTimeout(race, 80);
}

благодаря чему в контейнер с равными (и очень короткими) промежутками вы-


водятся очередные показания:

document.getElementById("loa").innerHTML=t;

В исходном состоянии переменная t «пустая»:

let t="";

78
Ее заполнение производится в цикле
for(let a=0; a<=d[i]; a++)
t+="<div class='tim'></div>";

При каждом вызове функции race количество проходов цикла определя-


ется числом из очередного элемента массива:
a<=d[i]

Внутри цикла переменная t заполняется кодом, который при выводе на


страницу образует картинку из нескольких штрихов
t+="<div class='tim'></div>";

определенного размера и цвета:


.tim {width: 15px; height: 10px; background: #0000CC;
display: inline-block; margin: 1px;}

Количество штрихов равно очередному числу из массива.


Повторимся: изображение показывается на странице после заполнения
переменной t при каждом вызове функции race:
document.getElementById("loa").innerHTML=t;

после чего счетчик элементов массива увеличивается на единицу:


i++;

Когда функция переберет все элементы массива, счетчик i обнулится и


процесс начнется заново:
if(i==d.length)
i=0;

Рис. 4.7.1. Индикатор (таймер) загрузки

79
Поскольку значения элементов массива сначала идут по возрастающей, а
достигнув числа 15, начинают убывать, индикатор выглядит как непрерывно
бегущие от минимума к максимуму и обратно штрихи (рис. 4.7.1).
Как видите, массив оказался полезен даже в таком необычном качестве.

4.8. Операторы

Один из важнейших аспектов выполнения сценариев на JavaScript — опе-


рации с переменными. Для этого существуют несколько видов операторов.
Арифметические операторы. Для начинающего наиболее важны те, что
представленные в таблице 4.8.1.
Таблица 4.8.1
Арифметические операторы
Оператор Действие Пример Примечание
x=1; Присваивает переменной данные
= Присваивание
x="Текст"; любого типа
Применяется также к переменным,
+ Сложение x+y содержащим строки; результатом
будет новая строка
- Вычитание x-y
* Умножение x*y
/ Деление x/y
** Возведение в степень x**y

Обычно результат выполнения операции присваивается либо новой пере-


менной

let z=x+y;

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

x=x+y;

Для второго случая существуют сокращенные формы записи операций


присваивания, представленные в таблице 4.8.2.
Таблица 4.8.2
Сокращенная форма записи операций
Сокращенная запись Аналог Примечание
Применяется также к переменным, содержащим
x+=y; x=x+y;
строки; результатом будет новая строка
x-=y; x=x-y;
x*=y; x=x*y;
x/=y; x=x/y;
x**=y; x=x**y;

80
Последний сокращенный оператор x**=y; практически не упоминается в
компьютерной литературе, но тем не менее он существует и прекрасно работает.
Вот пример (файл 4.8_1.html в папке «Глава4» zip-архива):
<script>
let x=2;
let y=4;
x**=y;
alert(x);
</script>

Еще две важные операции: инкремента и декремента. Разъяснения к ним


даны в таблице 4.8.3.
Таблица 4.8.3
Операторы инкремента и декремента
Оператор Название Действие Пример
Увеличение значения
++ Оператор инкремента x++;
переменной на 1
Уменьшение значения
– Оператор декремента x–;
переменной на 1

В некоторых случаях с операторами инкремента и декремента надо обра-


щаться очень аккуратно. Почему? Дело в том, что формы записи данных опера-
торов могут иметь два вида. Например, оператор инкремента: x++; и ++x;. Опе-
ратор декремента: x--; и --x;.
В каких ситуациях форма записи играет решающее значение? Когда вы
хотите для сокращения кода выполнить сразу два действия: увеличить пере-
менную и присвоить ее значение другой переменной. Здесь возможны следую-
щие варианты.
1. Сначала присваиваем текущее значение первой переменной (назовем
ее x) второй переменной (назовем ее y), а потом увеличиваем на единицу пере-
менную x, то есть используем форму записи x++ (файл 4.8_2.html в папке
«Глава4» zip-архива):
<script>
let x=1;
let y=x++;
alert(y+" "+x);
</script>

2. Сначала увеличиваем текущее значение переменной x на единицу, то


есть используем форму записи ++x, а потом присваиваем ее значение перемен-
ной y (файл 4.8_3.html в папке «Глава4» zip-архива):

<script>
let x=1;
let y=++x;
alert(y+" "+x);
</script>

81
Как вы можете убедиться, запустив данные файлы в своем браузере, пе-
ремена мест оператора инкремента влияет на результат. Аналогично поведет
себя сценарий, где используются операторы декремента.
Пойдем дальше. Следующие у нас на очереди операторы сравнения, кото-
рые представлены в таблице 4.8.4.
Таблица 4.8.4
Операторы сравнения
Оператор Проверяемое условие Пример
== Равно x==y
!= Не равно x!=y
> Больше x>y
>= Больше или равно x>=y
< Меньше x<y
<= Меньше или равно x<=y
=== Строго равно x===y
!== Строго не равно x!==y

Про последние два оператора хотелось бы рассказать подробнее. Назна-


чение данных операторов можно продемонстрировать на четырех примерах.
Пример 1 (файл 4.8_4.html в папке «Глава4» zip-архива):
<script>
let a=0;
if(a==0)
alert(typeof a);
</script>

Пример 2 (файл 4.8_5.html в папке «Глава4» zip-архива):


<script>
let a="0";
if(a==0)
alert(typeof a);
</script>

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


на различие типов данных программа считает, что в обоих случаях значения
переменной x равно 0 и, соответственно, выводит подтверждающее сообщение.
А все потому, что мы использовали оператор простого сравнения ==. Чтобы из-
бежать подобных ошибок и существуют операторы строго сравнения. Приме-
нив один из них (===) в третьем и четвертом примерах, мы получим требуемый
результат.
Пример 3 (файл 4.8_6.html в папке «Глава4» zip-архива):
<script>
let a="0";
if(a===0)
alert(typeof a);
</script>

82
В этом примере диалоговое окно с сообщением о типе переменной не по-
явится, так как условие строгого равенства не выполнено.
Пример 4 (файл 4.8_7.html в папке «Глава4» zip-архива):

<script>
let a=0;
if(a===0)
alert(typeof a);
</script>

В этом случае диалоговое окно с сообщением о типе переменной, наобо-


рот, откроется, так как условие строгого равенства выполнено.
Наконец, завершающая таблица 4.8.5 содержит перечень логических опе-
раторов.
Таблица 4.8.5
Логические операторы

Оператор Действие Возвращаемый результат


true, если все операнды истинны и false,
&& Логическое умножение (И)
если хотя бы один операнд ложен
true, если операнд ложен и false,
! Логическое отрицание (НЕ)
если операнд истинен
true, если хотя бы один операнд истинен
|| Логическое сложение (ИЛИ)
и false, если все операнды ложны

Теперь вернемся к операторам сравнения. Обращаю ваше внимание, что


запись двойного сравнения, к которой мы привыкли в школе, в JavaScript не
сработает. Так писать нельзя: 1<x<10.
Правильная запись: x>1&&x<10. В этом варианте выполняется коррект-
ная проверка обоих условий, а затем логическое умножение, о котором мы го-
ворили только что.

4.9. Условные операторы

Условные операторы необходимы для проверки определенных данных на


соответствие некоторым критериям.
Вариант наиболее простого использования условного оператора выглядит
так:

if(условие)
{
инструкции
}

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

if(условие)
{
инструкции 1
}
else
{
инструкции 2
}

Инструкции блока else будут выполняться в случае ложности условия.


Бывает, надо проверить несколько условий. В этой ситуации используют
такую конструкцию:
if(условие 1)
{
инструкции 1
}
else if(условие 2)
{
инструкции 2
}
...
else if(условие n)
{
инструкции n
}
else
{
инструкции на случай ложности всех условий
}

Иногда требуется проверить несколько условий, но при этом не выпол-


нять никаких действий, если все условия окажутся ложными. Представим, что у
нас есть переменная t, которая может принимать разные числовые значения, но
для нас важны только три из них: 1, 2 и 3. В этом случае условные выражения
можно записать так
if(t==1)
{
инструкции 1
}
if(t==2)
{
инструкции 2
}
if(t==3)
{
инструкции 3
}

84
и даже так:
if(t==1)
{
инструкции 1
}
else if(t==2)
{
инструкции 2
}
else if(t==3)
{
инструкции 3
}

Еще один способ проверки — оператором выбора switch. Он может быть


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

switch (выражение)
{
case значение 1:
инструкции 1
break;
case значение 2:
инструкции 2
break;
...
case значение n:
инструкции n
break;
default:
инструкции на случай отсутствия совпадений
}

Здесь значение из каждого блока последовательно сравнивается с задан-


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

switch (t)
{
case 1:
инструкции 1
break;
case 2:
инструкции 2
break;
case 3:
инструкции 3
}

85

Powered by TCPDF (www.tcpdf.org)


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

switch (t)
{
case 1:
инструкции 1
break;
case 2:
case 3:
инструкции 2
}

Если у вас после проверки условия выполняется только одна инструкция,


то можно использовать сокращенную форму записи выражения:
if(условие)
инструкция

или

if(условие)
инструкция 1
else
инструкция 2

или

if(условие 1)
инструкция 1
else if(условие 2)
инструкция 2
...
else if(условие n)
инструкция n
else
инструкция на случай ложности всех условий

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


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

let a=условие ? инструкция 1 : инструкция 2;

Как видите, результат выполнения тернарного оператора можно присво-


ить переменной.
Пример реальной записи:
let a=b<5?0:1;

И еще одно интересное замечание. В качестве проверяемых выражений в


условные операторы можно передавать возвращаемые значения из внешних
86
функций. Такой пример показан ниже (файл 4.9_1.html в папке «Глава4» zip-
архива):

<input type="button" value="TRUE" id="bu1">


<input type="button" value="FALSE" id="bu2">
<script>
document.getElementById("bu1").addEventListener("click", but);
document.getElementById("bu2").addEventListener("click", but);

function b(a)
{
if(a==1)
return true;
if(a==2)
return false;
}

function but(ev)
{
let t=parseFloat(ev.target.id.substr(2));
if(b(t))
alert(true);
}
</script>

Документ содержит две кнопки

<input type="button" value="TRUE" id="bu1">


<input type="button" value="FALSE" id="bu2">

которые запускают одну и ту же функцию but:

document.getElementById("bu1").addEventListener("click", but);
document.getElementById("bu2").addEventListener("click", but);

Первым делом из id выделяется число:

let t=parseFloat(ev.target.id.substr(2));

А затем проверяется условие

if(b(t))

выражение которого вычисляется путем вызова функции b

function b(a)
{
If(a==1)
return true;
if(a==2)
return false;
}

87
с полученным числом в качестве аргумента:
b(t)

Таким образом, фактически на истинность проверяется значение, возвра-


щаемое из функции.
Если запустить в браузере указанную страницу, вы увидите, что диалого-
вое окно появится только после нажатия кнопки «TRUE». Если нажать кнопку
«FALSE», функция b вернет false, и условие станет ложным.
Читатели должны понимать, что пример этот демонстрационный и не
имеет практической пользы, так как проверку значения, полученного из id, ра-
циональнее было бы производить внутри основной функции but. Но данный
сценарий разъясняет принцип, которым можно воспользоваться в реальных
программах, если это необходимо.

4.10. Операторы циклов


Операторы циклов необходимы для перебора некоторых значений в за-
данных пределах и поочередного выполнения определенных инструкций, ино-
гда зависящих от значения на очередном проходе, а иногда нет. Их удобно ис-
пользовать в тех случаях, когда какую-то часть программного кода надо вы-
полнить многократно.
Наиболее универсален оператор for. Его структура обычно выглядит так:
for(исходное значение; условие; приращение)
{
инструкции
}

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


for(let a=1; a<7; a++)
{
let b="im"+a;
document.getElementById(b).style.width=500+"px";
}

Если в цикле выполняется только одна инструкция, то можно использо-


вать сокращенную форму записи:
for(let a=1; a<7; a++)
document.getElementById("im"+a).style.width=500+"px";

В цикле for допустимо объявлять сразу несколько переменных-счетчиков


и одновременно производить над ними различные операции (файл 4.10_1.html
в папке «Глава4» zip-архива):

for(let a=1, b=5; a<7; a++, b--)


alert(a+" "+b);

88
Кроме того, при необходимости легко применить множественные условия
(файл 4.10_2.html в папке «Глава4» zip-архива):

for(let a=1, b=5; a<7&&b>2; a++, b--)


alert(a+" "+b);

Еще один полезный оператор цикла — while. Он устроен несколько


проще:

while(условие)
{
инструкции
}

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

let a=1;

while (a<7)
{
let b="im"+a;
document.getElementById(b).style.width=500+"px";
a++;
}

Наконец, третий оператор цикла: do-while. Этот оператор имеет одно


принципиальное отличие от while. В цикле while сначала проверяется условие,
а потом, если оно истинно, выполняются инструкции. Таким образом, если
условие изначально ложно, ни одного прохода не состоится. Оператор do-while
сначала выполняет инструкции, а потом проверяет условие. Поэтому даже при
изначально ложном условии инструкции будут выполнены один раз. Форма за-
писи оператора do-while:

do
{
инструкции
}
while(условие)

Пример из реального сценария:

let a=1;

do
{
let b="im"+a;
document.getElementById(b).style.width=500+"px";
a++;
}
while(a<7)

89
Как и условные операторы, циклы for и while могут получать условия и
приращения из внешних функций. Вот пример на данную тему (файл
4.10_3.html в папке «Глава4» zip-архива):

<input type="button" value="Пуск" id="bup">


<script>
document.getElementById("bup").addEventListener("click", but);

let a=1;
let b=function()
{
a++;
}

function but()
{
for(a=1; a<7; b())
alert(a);
}
</script>

Если нажать кнопку «Пуск» шесть раз подряд, откроются диалоговые ок-
на, поочередно демонстрирующие цифры от 1 до 6.
Этим процессом управляет функция but

function but()
{
...
}

в теле которой есть оператор цикла for:

for(a=1; a<7; b())


alert(a);

Значение приращения на каждом проходе цикл получает из функции b:

let b=function()
{
a++;
}

При каждом обращении к ней функция b увеличивает значение счетчика a


на единицу. Вызов функции происходит шесть раз (пока значение счетчика
меньше 7):

a<7

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


бо увеличивается, либо уменьшается. Получение приращения из внешней

90
функции может быть полезно, чтобы «заставить» работать один и тот же цикл в
разных «направлениях». Вот код страницы, демонстрирующей такой прием
(файл 4.10_4.html в папке «Глава4» zip-архива):
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Передача приращения в двунаправленный цикл</title>
<script>
addEventListener("load", function()
{
document.getElementById("bup").addEventListener("click", but);
document.getElementById("bum").addEventListener("click", but);
});
let a=1;
let c;
let b=function()
{
if(c=="bup")
a++;
if(c=="bum")
a--;
};
function but(ev)
{
c=ev.target.id;
for(a; a>0&&a<7; b())
alert(a);
if(a==7)
a=6;
if(a==0)
a=1;
}
</script>
</head>
<body>
<input type="button" value="Вперед" id="bup">
<input type="button" value="Назад" id="bum">
</body>
</html>

В новом примере у нас две кнопки


<input type="button" value="Вперед" id="bup">
<input type="button" value="Назад" id="bum">

привязанные к одному обработчику — функции but:


document.getElementById("bup").addEventListener("click", but);
document.getElementById("bum").addEventListener("click", but);

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

c=ev.target.id;

После этого запускается цикл

for(a; a>0&&a<7; b())


alert(a);

который получает приращение из функции b:

let b=function()
{
if(c=="bup")
a++;

if(c=="bum")
a--;
};

Эта функция в зависимости от id нажатой кнопки либо увеличивает зна-


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

if(a==7)
a=6;
if(a==0)
a=1;

Запустим страницу в браузере и нажмем кнопку «Вперед». После этого


шесть раз подряд откроются диалоговые окна, поочередно демонстрирующие
цифры от 1 до 6. Теперь нажмем кнопку «Назад». Снова поочередно откроются
шесть диалоговых окон, но теперь на каждом из них цифры будут идти по убы-
вающей — от 6 до 1.
Понятно, что данная программа не имеет практического значения, а толь-
ко демонстрирует принцип. Позднее в главе 6 в разделе 6.7 мы увидим, что та-
кая технология вполне применима в реальных сценариях.

4.11. Функции

Одни из самых важных «действующих лиц» любой программы на


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

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

function func()
{
document.getElementById("flo").style.width="200px";
document.getElementById("photo").src="im.jpg";
}

Задача такой функции — увеличить одно изображение и поменять адрес


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

let a=0;
function func()
{
a++;
}

В этом примере функция при каждом вызове увеличивает значение пере-


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

// Описание функции
function func()
{
...
}
// Вызов функции
func();

или

// Вызов функции как обработчика события


document.getElementById("te").addEventListener("click", doc);
// Описание функции
function doc()
{
...
}

2. Анонимные функции, то есть не имеющие имени и «приписанные»


непосредственно к регистратору обработчика. Пример:
93
document.getElementById("im").addEventListener("click", function()
{
...
});

3. Анонимные функции, изначально присвоенные какой-либо переменной.


Допустим, вот так:
let v=function(x)
{
...
}

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


let z=v(8);

И даже еще проще:


v(8);

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


считать «условно анонимными», ведь к ним можно обратиться по имени пере-
менной. Однако я не уверен, что со мной согласятся все программисты.
Есть еще один интересный случай — функции-стрелки. Их особен-
ность — более короткая и простая запись. Функции-стрелки объявляются двумя
последовательными символами =>. Один из возможных вариантов такой функ-
ции:
let h=0;

let y=(g)=>
{
h++;
let f=g+" - переданное число<br>"+h+" - количество кликов";
return f;
};

Вызвать данную функцию можно так:


document.getElementById("p1").addEventListener("click", function()
{
document.getElementById("p2").innerHTML=y(10);
});

Чтобы пример был понятнее, напишем упрощенный вариант страницы с


полным кодом (файл 4.11_1.html в папке «Глава4» zip-архива):

<p id="p1">СТРОКА</p>
<p id="p2"></p>
<script>
let h=0;

94
let y=(g)=>
{
h++;
let f=g+" - переданное число<br>"+h+" - количество кликов";
return f;
};
document.getElementById("p1").addEventListener("click", function()
{
document.getElementById("p2").innerHTML=y(10);
});
</script>

Здесь после каждого клика на первой строке — во второй будет появлять-


ся переданное в функцию число и меняющийся показатель количества щелчков.
Функции-стрелки могут быть записаны гораздо проще, если выполняют
всего одну операцию. Вот пример (файл 4.11_2.html в папке «Глава4» zip-
архива):

let k=()=>25**3;

Вызовем эту функцию:

document.getElementById("p1").addEventListener("click", function()
{
document.getElementById("p2").innerHTML=k();
});

Часто программисту необходимо использовать рекурсивные функции —


то есть те, что вызывают сами себя. Это можно сделать двумя способами.
Допустим, мы хотим вычислить сумму квадратов чисел от 1 до 5. Напи-
шем такой сценарий (файл 4.11_3.html в папке «Глава4» zip-архива):

let x=1;
let t=0;
func(5);

function func(i)
{
t+=x**2;
x++;
if(x<=i)
func(i);
}

В этом примере переменная x — счетчик повторов функции, t — пере-


менная для хранения результатов вычислений. Пока x меньше значения аргу-
мента, функция вызывается вновь:

if(x<=i)
func(i);

95
Результаты вычислений можно вывести на страницу:

document.getElementById("kv").insertAdjacentHTML("beforeend", t);

Для этого в теле документа у нас есть строка:

<p id="kv">Сумма квадратов чисел от 1 до 5: </p>

Создавая рекурсивную функцию, обязательно предусмотрите предел ее


выполнения. Если этого не сделать, процесс станет бесконечным и страница с
функцией зависнет. В случае нашего примера такой предел установлен — ко-
личество повторов равно 5.
Еще один способ создания рекурсивной функции — использование тай-
мера setTimeout. Рассмотрим вариант программы с таймером. А для этого в
предыдущий пример внесем некоторые изменения. Теперь сценарий будет вы-
глядеть так (файл 4.11_4.html в папке «Глава4» zip-архива):

let x=0;
let t=0;

func();
function func()
{
x++;
t+=x**2;
if(x<=5)
{
window.setTimeout(func, 1000);
document.getElementById("kv").innerHTML=t;
}
}

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

<p>Сумма квадратов чисел от 1 до 5: <span id="kv"></span></p>

Если запустить такую страницу в браузере, вы увидите, как показания в


строке «Сумма квадратов чисел от 1 до 5:» станут изменяться 1 раз/с.
Теперь о передаче параметров в функцию. Как вы уже видели из упомя-
нутых примеров, это можно сделать разными способами.
1. Передать данные в качестве аргумента (или аргументов, если их много).
Вот так:

func(15);

function func(i)
{
// инструкции, выполняемые в зависимости от значения аргумента i
}

96
2. Использовать для передачи данных значение глобальной переменной:
let a=15;
function func()
{
// инструкции, выполняемые в зависимости от значения переменной a
}

3. Еще один вариант, который мы пока не упоминали: передача в качестве


аргумента функции — объекта события (с последующим извлечением данных,
например id). Рассмотрим этот случай подробнее.
Пусть документ содержит несколько изображений:
<img id="i1" src="..." alt="Фото">
...
<img id="in" src="..." alt="Фото">

Станем фиксировать щелчки мышью на странице:


window.addEventListener("click", res);

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


данные из интерфейса event (его еще называют объектом события):
function res(ev)
{
...
}

Сначала проверим, где произошло событие. Нам важны щелчки на изоб-


ражениях:
if(ev.target.tagName=="IMG")
{
...
}

Если условие верно, определим id рисунка:

ev.target.id

и «вычленим» из него число:

let p=ev.target.id.substr(1);

Теперь, в зависимости от полученного результата, выполняются какие-то


инструкции:
if(условие, зависящее от значения переменной p)
{
инструкции
}

97
Как видите, по сути функция res получает аргументы из id. Запишем
функцию целиком (файл 4.11_5.html в папке «Глава4» zip-архива):
function res(ev)
{
if(ev.target.tagName=="IMG")
{
let p=ev.target.id.substr(1);
if(условие, зависящее от значения переменной p)
{
инструкции
}
}
}

4. Наконец, допустим смешанный вариант — это когда одна и та же


функция может получать аргументы совершенно разных типов. Например, чис-
ло или данные из интерфейса event. Частный случай такой функции показан
ниже (файл 4.11_6.html в папке «Глава4» zip-архива):
<p id="p1">СТРОКА</p>
<script>
document.getElementById("p1").addEventListener("click", func);
func(5);
function func(ev)
{
if(typeof ev==="number")
alert(ev);
else
alert(ev.target.id);
}
</script>

В этом примере кроме кода JavaScript у нас есть еще текстовая строка, ко-
торая нужна для полноценной демонстрации работы функции.
Сразу после загрузки страницы функция получает в качестве аргумента
число, которое выводится в диалоговом окне. Если после его закрытия щелк-
нуть мышью на текст, то откроется новое окно с сообщением, что событие про-
изошло на элементе, id значение которого — p1.
Раз в функцию можно передавать какие-то параметры, значит, можно и
получать результаты работы функции. Покажем два способа.
1. Прямое указание в теле функции возвращаемого значения. Для этого
используется оператор return. Выше мы говорили о стрелочных функциях и
проиллюстрировали рассказ таким примером:
let y=(g)=>
{
h++;
let f=g+" - переданное число<br>"+h+" - количество кликов";
return f;
};

98
Как видите, здесь прямо указано, что функция возвращает значение

return f;

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


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

let a=0;

function func()
{
// операции с переменной a
}

Здесь новое значение переменной a условно можно считать возвращае-


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

4.12. Технология Ajax

Напомню, что Ajax расшифровывается как «Асинхронный JavaScript и


XML» и представляет собой передовую технологию взаимодействия между
страницей, загруженной в браузер, и сервером.
Как происходило такое взаимодействие раньше? Клиент отправлял ин-
формацию, а сервер передавал в браузер новую HTML-страницу с измененны-
ми данными или сообщениями о результате отправки данных из формы. Клю-
чевой момент ранних технологий — загрузка новой страницы вместо исходной.
Это увеличивает время ожидания ответа сервера, повышает интернет-трафик.
Такие факторы особенно чувствительно ощущались во времена подключения к
Интернету через модемы.
Потом разработчики придумали хитроумный способ обходиться без но-
вой страницы. Чтобы выводить необходимую информацию, текущий документ
снабжался невидимым фреймом. Когда появлялась нужда в фоновом режиме
получить какие-то данные с сервера, у фрейма программным способом менялся
адрес загруженного файла на новый в соответствии с запросом клиента. Таким
образом, во фрейме появлялись затребованные данные, после чего происходило,
опять же на программном уровне, считывание полученного файла и добавление
информации на страницу. Однако и этот метод имел определенные ограни-
чения.

99
И только создание технологии Ajax позволило делать сайты, где инфор-
мация добавлялась на страницу без лишних выкрутасов и очень быстро. А учи-
тывая, что современные посетители сайтов любят, когда все происходит без
лишних задержек, автор советует web-программистам использовать Ajax при
любой возможности.
Рассмотрим некоторые варианты применения Ajax в web-разработках.
Один из наиболее типичных примеров — отправка клиентом заполненной
формы. В этом случае результаты проверки данных отображаются прямо на те-
кущей странице. Нажал кнопку «Отправить» — и тут же получил ответ «Ваша
заявка зарегистрирована» или «Вы указали некорректные сведения».
Рассмотрим конкретную программу. Пусть у нас имеется форма с не-
сколькими полями, в том числе и для загрузки файлов. Присвоим форме имя
dat (файл 4.12_1.html в папке «Глава4» zip-архива):

<form name="dat" enctype="multipart/form-data">


Ваше имя: <input type="text" name="F1"><br>
Телефон: <input type="text" name="F2"><br>
E-mail: <input type="text" name="F3"><br>
Сообщение: <textarea name="F4"></textarea><br>
Фото: <input type="file" name="F5"><br>
<input id="bu" type="button" value="Отправить">
</form>

Добавим на страницу место вывода ответа сервера. Для этого используем


пустой слой div, расположив его под формой:

<div id="att"></div>

Зарегистрируем обработчик для клика на кнопке отправки данных:

window.addEventListener("load", function()
{
document.getElementById("bu").addEventListener("click", ret);
});

Напишем функцию ret, предназначенную для отправки формы:

function ret()
{
let dt=new FormData(document.forms.dat);
let re=new XMLHttpRequest();
re.open("POST", "rec.php", true);
re.send(dt);
re.addEventListener("load", show);
}

Здесь rec.php — серверная программа, обрабатывающая данные полей


формы. Просто для примера мы указали, что она написана на PHP. Но с таким
же успехом это может быть файл Python, Perl или Ruby.

100

Powered by TCPDF (www.tcpdf.org)


Напомню некоторые определения.
1. FormData — прикладной интерфейс, создающий тело запроса, состоя-
щего из нескольких частей. Такими частями могут быть текст или файлы. Не-
обходим для создания запроса с данными в формате multipart/form-data.
2. XMLHttpRequest — это класс прикладного интерфейса, каждый эк-
земпляр которого предоставляет свойства и методы, позволяющие формировать
запрос к серверу и получать данные из ответа.
3. Метод open формирует запрос к серверной программе.
4. Метод send отправляет запрос.
После загрузки ответа будет запущена функция show:

re.addEventListener("load", show);

Задача функции show — вывести ответ сервера на текущую страницу:

function show()
{
let ans=this.responseText;
if(ans==1)
document.getElementById("att").innerHTML="СООБЩЕНИЕ ДОСТАВЛЕНО";
else
document.getElementById("att").innerHTML="НЕКОРРЕКТНЫЕ ДАННЫЕ";
}

Свойство responseText предоставляет доступ к ответу сервера. Значение


responseText — текстовая строка.
Если программа rec.php передала в качестве ответа число 1, значит, со-
общение успешно обработано. Если число 2, то в отправленных данных были
некорректно заполненные поля. Соответствующие предупреждения появятся у
нас в слое div.
Обратите внимание: в серверных программах, используемых в ре-
альных проектах, необходимо обязательно предусмотреть меры безопасно-
сти, чтобы перекрыть недоброжелателям любые возможности нанести
урон вашему сайту. Контроль на стороне сервера должен быть очень стро-
гим, ведь именно здесь ставится барьер для недоброжелателей. Надо обя-
зательно проверять объем входящей информации, типы файлов, наличие
исполняемого кода и запрещенных символов, соответствие ссылок требуе-
мому формату и разрешенным адресам.
Мы вывели на страницу ответ сервера в виде текстового сообщения, рас-
положенного под формой. Однако это не единственный вариант. Для демон-
страции ответа сервера можно использовать слои с заранее подготовленными
фразами и показывать эти слои, например поверх формы. Рассмотрим такой ва-
риант.
Есть два слоя:

<div id="att1">СООБЩЕНИЕ ДОСТАВЛЕНО</div>


<div id="att2">НЕКОРРЕКТНЫЕ ДАННЫЕ</div>

101
В исходном состоянии они не видны:

<style>
div {visibility: hidden;}
</style>

В этом случае функция show будет записана так (файл 4.12_2.html в пап-
ке «Глава4» zip-архива):

function show()
{
let ans="att"+this.responseText;
document.getElementById(ans).style.visibility="visible";
}

Тот или иной слой показывается в зависимости от числа, переданного в


ответе сервера. Кстати, не забудьте предусмотреть на каждом слое какой-
нибудь значок, по щелчку на котором сообщение будет скрываться.
Наконец, могу предложить еще один вариант демонстрации ответа серве-
ра — креативный. Можно писать сообщения непосредственно на кнопке от-
правки данных.
Вот конкретный пример. В исходном состоянии кнопка имеет текст «От-
править»:

<input id="bu" type="button" value="Отправить">

Поменяем кое-что в функции show (файл 4.12_3.html в папке «Глава4»


zip-архива):

function show()
{
let ans=this.responseText;
if(ans==1)
document.getElementById("bu").value="СООБЩЕНИЕ ДОСТАВЛЕНО";
else
document.getElementById("bu").value="НЕКОРРЕКТНЫЕ ДАННЫЕ";
}

В результате при положительном ответе сервера на кнопке появится текст


«СООБЩЕНИЕ ДОСТАВЛЕНО», а при отрицательном — «НЕКОРРЕКТНЫЕ
ДАННЫЕ».
Примеры вывода сообщений от сервера показаны на рисунке 4.12.1.
Еще вариант применения технологии Ajax — добавление контента на
страницу по мере ее прокрутки вниз. Подобные технологии используются, в
частности, при разработке социальных сетей.
Упрощенная программа бесконечной ленты, в которую информация до-
бавляется по частям, есть в моей книге «JavaScript. Обработка событий на при-
мерах». Посмотреть сценарий в действии можно на сайте поддержки книги:
https://testjs.ru/p24.html.

102
Рис. 4.12.1. Варианты вывода предупреждений от сервера:
исходный вид формы; сообщение в строке ниже формы;
сообщение на специально оформленном слое;
сообщение на кнопке отправки данных

Несколько изменив ее код, покажу, как можно реализовать загрузку по


частям.
Пусть у нас в теле документа будет слой, который изначально содержит
какое-то количество графических элементов и текста:
<div id="pic">
...
</div>

Зарегистрируем обработчик события прокрутки страницы:


window.addEventListener("scroll", scro);

Функция scro (файл 4.12_4.html в папке «Глава4» zip-архива):


function scro()
{
if(window.pageYOffset+window.innerHeight>=document.body.
clientHeight)
{
let re=new XMLHttpRequest();
re.open("GET", "ajax.php", true);
re.send();
re.addEventListener("load", demo);
}
}

103
Сначала мы проверяем, не достиг ли ползунок конца документа:

if(window.pageYOffset+window.innerHeight>=document.body.
clientHeight)

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


новые данные, полученные от серверной программы ajax.php:

function demo()
{
document.getElementById("pic").insertAdjacentHTML("beforeend",
this.responseText);
}

Как мы видим, информация добавляется на слой в самый конец перед за-


крывающим тегом </div>:

insertAdjacentHTML("beforeend", this.responseText);

Страница тем самым «удлинится», и появится пространство для дальней-


шей прокрутки. Если вновь сместить ползунок в самый низ, опять запустится
функция scro и загрузятся очередные данные. Таким образом и достигается эф-
фект бесконечной ленты.
Третий пример использования технологии Ajax — замена одних данных в
текущем документе на другие. Вариант совсем простой:
 регистрируем обработчик события, в результате которого должна за-
гружаться новая информация;
 по аналогии с предыдущими случаями пишем функцию, формирующую
и отправляющую запрос на сервер — думаю, вы уже в состоянии написать ее
самостоятельно;
 заменяем существующие данные новыми.
Последнюю операцию можно выполнить так. Опять представим, что у нас
есть слой с картинками и текстом:

<div id="pic">
...
</div>

Пишем несколько измененную версию функции demo (файл 4.12_5.html в


папке «Глава4» zip-архива):

function demo()
{
document.getElementById("pic").innerHTML=this.responseText;
}

Здесь существующая информация полностью заменяется на новую.

104
Наконец, завершая рассказ, хочу отметить, что с помощью технологии
Ajax можно не только взаимодействовать с серверными программами, но и
просто загружать файлы, например текстовые. Сделать это можно так (файл
4.12_6.html в папке «Глава4» zip-архива):
re.open("GET", "text.txt", true);

В этом случае мы обращаемся к файлу, содержащему простой текст или


HTML-разметку, которая будет преобразована на странице в соответствующие
элементы:
document.getElementById("pic").insertAdjacentHTML("beforeend",
this.responseText);

или
document.getElementById("pic").innerHTML=this.responseText;

4.13. Подсказки и проверка данных

Как утверждает народная мудрость, никто не застрахован от ошибок.


В том числе и посетители вашего сайта, заполняющие форму отправки сообще-
ний. Часто казусы возникают, когда клиент не знает, в каком виде необходимо
ввести информацию или каким пределом ограничен ее объем.
Во избежание подобных ошибок дальновидные разработчики внедряют на
страницы подсказки для клиентов.
Рассмотрим один из вариантов таких подсказок. Напишем сценарий, про-
веряющий количество символов, введенных в текстовое поле, и сообщающий,
сколько знаков еще можно добавить.
Итак, есть текстовое поле:
<textarea id="tx" maxlength="100"></textarea>

А над ним табло для вывода сообщений:


<div id="att">Можно ввести 100 знаков</div>

Регистрируем обработчик события нажатия любой клавиши для ситуации,


когда курсор установлен в текстовом поле:
window.addEventListener("load", function()
{
document.getElementById("tx").addEventListener("keyup", func);
});

Пишем функцию (файл 4.13_1.html в папке «Глава4» zip-архива):


function func()
{
let t=100-document.getElementById("tx").value.length;

105
const a=document.getElementById("att");
a.innerHTML="Осталось: "+t;

if(t<=20)
a.style.color="#CC0000";
else
a.style.color="#006600";
}

Здесь предусмотрен не только подсчет оставшегося количества знаков, но


и смена цвета сообщений, когда до конца остается менее 20 символов. Как та-
кие подсказки выглядят на реальной странице, видно по рисунку 4.13.1.

Рис. 4.13.1. Заполняя текстовое поле, клиент видит,


сколько знаков еще можно ввести

Если вам уже приходилось читать в книгах по web-программированию о


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

<form>
Ваш e-mail: <input type="text" name="F1" id="F1" required><br>
<input type="submit" value="Отправить">
</form>

106
Напишем сценарий, который станет проверять корректность e-mail.
Самый короткий адрес электронной почты обязан содержать знак @ («со-
бака» или коммерческое «эт»). Кроме того, должна присутствовать точка, раз-
деляющая доменные имена первого и второго уровня. Она будет отстоять от
первого символа адреса как минимум на пять позиций (два символа в начале +
@ + два символа доменного имени второго уровня). Также адрес должен иметь
длину не менее 8 символов: два в начале + @ + два символа доменного имени
второго уровня + точка + два символа доменного имени первого уровня. Будем
выполнять проверку исходя из перечисленных требований.
Зарегистрируем обработчик для события изменения значения поля формы:

window.addEventListener("load", function()
{
document.getElementById("F1").addEventListener("input", test);
});

Теперь сама функция (файл 4.13_2.html в папке «Глава4» zip-архива):

function test()
{
let na=document.forms[0].F1;
if((na.value.length<8)||(na.value.indexOf("@")<2)||
(na.value.indexOf(".")<5))
na.setCustomValidity("В e-mail допущена ошибка!");
else
na.setCustomValidity("");
}

Проверяем введенную строку на предмет ее соответствия ранее перечис-


ленным параметрам. Если допущена ошибка, выводим методом
setCustomValidity сообщение о данном факте. На рисунке 4.13.2 показан слу-
чай с пропущенным знаком @.

Рис. 4.13.2. Сообщение об ошибке в адресе электронной почты

Завершая этот раздел, хочу добавить, что организовать подсказки и про-


верку данных в полях формы позволяет и ряд специальных методов, встроен-
ных в современные браузеры. В последнем примере мы добавляли полю input
атрибут required. Даже если бы на странице не было описанного выше сцена-

107
рия, мы все равно не смогли бы отправить форму, если поле адреса электрон-
ной почты осталось бы незаполненным. Напишем такую страницу (файл
4.13_3.html в папке «Глава4» zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Проверка</title>
</head>
<body>
<form>
Ваш e-mail: <input type="text" name="F1" required><br>
<input type="submit" value="Отправить">
</form>
</body>
</html>

Как видите, здесь «подключен» только атрибут required. Нажмем кнопку


«Отправить», не заполняя формы. Отправка не состоится, а в браузерах
Microsoft Edge, Google Chrome и Opera рядом с полем возникнет предупрежде-
ние (рис. 4.13.3) «Заполните это поле» («Пожалуйста, заполните это поле» в
браузере Mozilla Firefox и «Вы пропустили это поле» в Яндекс.Браузере). Так
работает встроенная в браузеры простейшая проверка. Кстати, она может быть
гораздо сложнее — и опять без использования JavaScript.

Рис. 4.13.3. Проверка без использования сценария на JavaScript

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


исчерпывающей и советуют применять для этого сценарии на JavaScript.

4.14. Регулярные выражения

В предыдущем разделе мы проверяли наличие в адресе электронной по-


чты точки, знака @, а также положение этих элементов в строке и ее длину.
Однако такая проверка не гарантирует, что e-mail будет введен правильно. Мы
не проверили адрес на предмет запрещенных символов. Решить данную про-
блему нам поможет регулярное выражение.
108
Вы уже, наверное, знаете, что регулярное выражение — это некий шаблон,
по которому можно выполнить поиск на наличие или, наоборот, отсутствие
совпадающих фрагментов в небольшой строке или объемном тексте. Также с
помощью регулярных выражений проверяют данные на их соответствие опре-
деленным требованиям. В общем, это то, что нам нужно для оценки корректно-
сти e-mail перед отправкой формы.
Попробуем написать регулярное выражение для проверки адреса почты,
зарегистрированной в зоне .ru.
Вообще, корректный e-mail может содержать самые разнообразные сим-
волы. Но мы будем ориентироваться на требования, которые предъявляют к ад-
ресам российские почтовые сервисы. А они заметно снижают перечень таких
символов. Например, в Яндекс.Почте установлены следующие требования:
 разрешены буквы латинского алфавита, цифры, точки, дефисы;
 адрес не должен начинаться с цифры, дефиса или точки;
 адрес не может заканчиваться точкой или дефисом.
Есть правила и для доменных имен второго уровня:
 они могут содержать латинские буквы, цифры и дефис;
 имя может начинаться с цифры или буквы;
 имя не может завершаться дефисом.
Итак, наше регулярное выражение должно отслеживать выполнение дан-
ных условий. Начнем по порядку.
Адрес не должен начинаться с цифры, дефиса или точки. Этому требова-
нию удовлетворяет вот такой фрагмент выражения:
[a-z]

То есть первый символ — только буква. Затем идет некоторое количество


произвольных символов из тех, что разрешены — буквы, цифры, дефис, точка:
[a-z\d-\.]+

Знак + означает, что символов может быть более одного. Наконец, завер-
шающий символ — буква или цифра:
[a-z\d]

Дальше используем почтовый знак @.


Теперь проверка доменного имени. Оно начинается с буквы или цифры:
[a-z\d]

Следом могут идти буквы, цифры и дефис:


[a-z\d-]+

Завершающий символ — буква или цифра:


[a-z\d]

109
Имя домена второго уровня отделяется точкой от имени домена первого
уровня:

\.ru

Напомню, что регулярное выражение заключается в слеши, а для обозна-


чения конца и начала ввода используются специальные знаки ^ и $:

/^...$/i;

Добавлю, что благодаря наличию символа i сценарий игнорирует регистр


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

/^[a-z][a-z\d-\.]+[a-z\d]@[a-z\d][a-z\d-]+[a-z\d]\.ru$/i;

Заменим в функции test старую систему проверки на новую. Вот что у нас
выйдет (файл 4.14_1.html в папке «Глава4» zip-архива):

function test()
{
let na=document.forms[0].F1;
let rv=/^[a-z][a-z\d-\.]+[a-z\d]@[a-z\d][a-z\d-]+[a-z\d]\.ru$/i;
if(rv.test(na.value)==false)
na.setCustomValidity("В e-mail допущена ошибка!");
else
na.setCustomValidity("");
}

Здесь метод test внутри одноименной функции выполняет сопоставление


регулярного выражения с указанной строкой:

rv.test(na.value)==false

Результаты проверки адреса почты новым сценарием можно видеть на ри-


сунке 4.14.1.

Рис. 4.14.1. Сообщение об ошибке на основе проверки адреса


электронной почты регулярным выражением

110
Хочу обратить внимание, что, хотя описанный выше сценарий довольно
неплохо проверяет адреса электронной почты, но это все-таки упрощенный ва-
риант. Сделан он таковым для «облегчения» примера. В идеале необходимо до-
бавить проверку:
 длины адреса — а она, как вы знаете, ограничена;
 наличие таких нарушений, как два дефиса или две точки подряд (а так-
же дефиса и точки рядом в любом порядке).
Пойдем дальше. Применение регулярных выражений, конечно, не огра-
ничено только проверкой почты. На самом деле вариантов использования регу-
лярных выражений очень много. Рассмотрим еще один пример — подсвечива-
ние ссылок.
Допустим, на нашу страницу загружаются данные из файла, где в виде
простого текста есть адрес ссылки, не «обрамленный» соответствующими те-
гами a. Наша задача «подсветить» ссылку, так чтобы она стала рабочей, веду-
щей на конкретную страницу некоего сайта.
Задача эта не совсем простая — и вот по какой причине. Хорошо, если
ссылка отделена пробелами от других слов или знаков загружаемого текста.
Тогда «подсветить» ее довольно просто. Но она может быть расположена в
конце предложения, допустим, вот так: «Вы были на сайте https://testjs.ru?».
Или так: «Есть много ресурсов с примерами кода (один из них — сайт
https://testjs.ru)». Более того, по ошибке ссылка может вообще не иметь пробе-
лов между соседними словами и выглядеть так: «В тексте есть вот та-
каяhttps://testjs.ruссылка». Подобное нередко случается при наборе текста со
смартфона. Как в таком случае отделить ссылку?
Для этого существуют разные способы. Я хочу продемонстрировать вам
довольно оригинальный вариант, в котором проверяется не сама ссылка, а зна-
ки в начале и в конце нее. Сценарий такой проверки содержит всего одно ко-
роткое регулярное выражение (файл link.html zip-архива):

/[а-яА-Я\(\),!;\.:\?]/

Начну с некоторых оговорок. Чтобы упростить код, в сценарии приняты


некоторые ограничения:
 по умолчанию считается, что адрес ссылки — корректный;
 ссылка должна вести на домен в зоне .ru;
 ссылка должна начинаться с https://.
Если не соблюсти эти ограничения, программа работать не будет. Но, как
вы понимаете, наша задача рассмотреть общий принцип действия в аналогич-
ной ситуации и показать еще один вариант применения регулярного выражения.
Для наглядности чуть изменим условия и представим, что мы загружаем в
браузер текст, введенный пользователем в форму. Пусть данный текст включа-
ет адрес ссылки без тегов.
Что мы можем получить из формы? Текст со знаками препинания в конце
ссылки. Текст, в котором по ошибке ссылка не отделена пробелами от соседних
слов. Или текст, в котором ссылка выделена пробелами.
111
Программа начинает с того, что разбивает загруженный текст по пробе-
лам на отдельные слова.
Если в начале слова обнаружено объявление протокола https, а в конце
символы .ru, программа считает, что это ссылка, и добавляет к ней:
 открывающий тег a с атрибутом href, значение которого — адрес вы-
численной ссылки;
 закрывающий тег a.
После чего текст выводится на страницу.
Если имя протокола не отделено от предыдущего слова или знака пробе-
лом, то скрипт отделяет ссылку, используя разбиение текста по https://:

split('https://')

Все, что «до», воспринимается сценарием, как обычный текст.


Если после .ru следуют еще какие-то символы, то программа начинает
искать конец ссылки. После имени домена первого уровня .ru может быть:
 слеш и адрес конкретной страницы;
 знаки препинания — скобка, точка, запятая, точка с запятой, двоеточие,
многоточие, вопросительный и восклицательный знаки;
 слово из букв кириллицы, по ошибке не отделенное пробелом.
Возможны также смешанные варианты, когда после .ru идет адрес стра-
ницы, а за ним знак препинания или слово на кириллице.
Итак, представим ситуацию: после .ru следуют еще символы. Как мы уже
узнали, в этом случае программа начинает искать конец ссылки. Здесь как раз и
необходимо регулярное выражение

/[а-яА-Я\(\),!;\.:\?]/

Последовательность действий такова: берется последний знак из слова и


сравнивается с шаблоном. Если совпадений нет, то все, что находится до этого
символа, включая его, является адресом ссылки. Если происходит совпадение,
то выполняется сравнение второго знака с конца слова. Если второй знак не
удовлетворяет условиям шаблона, то все, что находится до этого символа,
включая его, является адресом ссылки. Если происходит совпадение, то выпол-
няется сравнение третьего знака с конца слова и т. д.
В каком случае поиск совпадений будет прекращен? Когда сценарий об-
наружит в конце слова латинскую букву, цифру или слеш, т. е. те знаки, что от-
сутствуют в шаблоне. Оставшаяся часть слова, включая эту букву, цифру или
слеш, и будет ссылкой. Дальше программа добавит к ней соответствующие теги
a и атрибут href.
Как и в ситуации с проверкой адреса электронной почты, обращаю ваше
внимание, что данный пример не является на сто процентов действенным, ведь
он не проверяет адрес на протокол http или адрес, в котором вообще не указан
протокол. Кроме того, комбинация знаков после доменного имени первого
уровня иногда бывает настолько причудливой, что данный скрипт может дать

112
ошибку. Однако в качестве заготовки для серьезной программы он вполне под-
ходит.
Кстати, если вы хотите расширить рамки действия сценария, то можете
добавить в него массив существующих доменных имен первого уровня и срав-
нивать его элементы с доменным именем из ссылки.
Поскольку сценарий довольно объемный, я не стал приводить его код. Вы
можете посмотреть действующую программу здесь: https://testjs.ru/kpp
/link.html. Зайдите на страницу. В исходном состоянии вы увидите текстовое
поле с надписью, содержащей ссылку, «застрявшую» в словах без пробелов.
Кликните мышью по текстовому полю — и увидите, что ссылка будет опреде-
лена правильно (рис. 4.14.2). Чтобы посмотреть код сценария, щелкните правой
кнопкой мыши на любом свободном участке страницы с примером и в открыв-
шемся меню выберите пункт «Посмотреть исходный код» (Microsoft Edge), или
«Просмотр кода страницы» (Google Chrome), или «Исходный код страницы»
(Opera и Mozilla Firefox), или «Посмотреть код страницы» (Яндекс.Браузер).

Рис. 4.14.2. Результат выполнения сценария «подсвечивания» ссылки

4.15. Обработка строк

Раз уж мы заговорили о регулярных выражениях, с помощью которых


проверяли текст и находили ссылки, имеет смысл вспомнить о методах, ис-
пользуемых для работы с текстом. А если быть точнее — со строковыми объек-
тами.
Методы, которые наиболее часто применяются для обработки строк, пе-
речислены в таблице 4.15.1.
Таблица 4.15.1
Методы обработки строк
Метод Описание
Возвращает символ с номером n из строки. Другой способ
«строка».charAt()
получения данного результата: «строка»[n]
Объединяет две строки и возвращает новую строку.
«строка 1».concat("строка 2") Другой способ для получения такого результата:
«строка 1»+№строка 2»

113
Продолжение табл. 4.15.1
Метод Описание
Определяет, находится ли одна строка внутри другой.
«строка».includes()
Возвращает true или false
Возвращает индекс первого вхождения в строке
«строка».indexOf()
указанных символов или –1, если вхождений нет
Возвращает индекс последнего вхождения в строке
«строка».lastIndexOf()
указанных символов или –1, если вхождений нет
«строка».match() Выполняет сопоставление регулярного выражения строке
Возвращает строку, состоящую из n повторений
«строка».repeat(n)
исходной строки
В случае совпадения части строки с искомой подстрокой
«строка».replace()
заменяет участок совпадения новой подстрокой
Выполняет поиск совпадения регулярного выражения
«строка».search()
со строкой
«строка».slice() Извлекает часть строки и возвращает новую строку
Разбивает строку на несколько строк с помощью
«строка».split()
заданного разделителя
«строка».substr(n) Возвращает все символы из строки, начиная с позиции n
Возвращает все символы из строки, начиная с позиции n
«строка».substring(n, m)
до m, но не включая символ из позиции m
«строка».toLocaleLowerCase() Переводит символы в строке в верхний регистр
«строка».toLocaleUpperCase() Переводит символы в строке в нижний регистр
«строка».trim() Удаляет пробелы в начале и конце строки

Кроме того, есть еще очень полезное свойство length, которое возвращает
длину строки: "строка".length.
Чтобы посмотреть использование этих методов и свойства length на прак-
тике, напишем вот такой сценарий (файл 4.15_1.html в папке «Глава4» zip-
архива):

<script>
console.log("кот".length);
console.log("кот"[2]);

console.log("кот".charAt(0));
console.log("кот".concat(" Васька"));

console.log("кот"+" Васька");
console.log("кот".includes("от"));

console.log("кот Васька".indexOf("а"));
console.log("кот Васька".lastIndexOf("а"));

console.log("кот Васька".match(/[a-z]/));
console.log("кот ".repeat(3));

console.log("кот Васька".replace("Васька", "Мурзик"));

114
console.log("кот Васька".search("ась"));
console.log("кот Васька".slice(4, 7));
console.log("кот Васька".split(" "));
console.log("кот Васька".substr(4));
console.log("кот Васька".substring(2, 5));
console.log("кот Васька".toLocaleLowerCase());
console.log("кот Васька".toLocaleUpperCase());
console.log(" кот Васька ".trim());
</script>

Запустим сценарий в браузере, после чего откроем пункт меню «Средства


разработчика» (в некоторых браузерах «Инструменты разработчика»), щелкнем
на вкладке «Консоль» (в некоторых браузерах «Console») и посмотрим, что у
нас вышло (рис. 4.15.1). Как видите, применяя методы обработки строк, можно
получить интересные и полезные результаты.

Рис. 4.15.1. Результат работы сценария, демонстрирующего методы


обработки строк

4.16. Комментарии
В языке JavaScript существуют два способа размещать комментарии непо-
средственно в теле сценария. Однострочный комментарий выделяется двумя
наклонными чертами — слешами:
// Это комментарий

115
Например:

function func()
{
// Объявляем счетчик кликов на изображении
let d=0;

...
}

Многострочный обрамляется слешем со звездочкой в его начале и звез-


дочкой со слешем в конце:

/* Это комментарий,
/* который состоит
/* из нескольких строк */

Например:

function func(t)
{
let m=0;

/* Запускаем цикл из 4 проходов и проверяем


/* переданное в функцию значение. Если оно
/* меньше текущего значения d,
/* увеличиваем счетчик m на единицу */
for(let d=0; d<5; d++)
{
if(t<d)
{
m++;
}
}
...
}

Как вы понимаете, комментарии размещают в коде программы, чтобы по-


яснить ее отдельные фрагменты. Также комментарии могут использоваться в
качестве различных временных заметок, необходимых в процессе написания
сценария. Такие заметки по завершении разработки обычно удаляют.
Пояснение фрагментов кода целесообразно по двум причинам:
1) по прошествии времени разработчику иногда трудно вспомнить, что
делают те или иные части сценария. Наличие комментариев упрощает этот
процесс;
2) если вы работаете в IT-компании, необходимо, чтобы ваш код понима-
ли не только вы, но и другой программист, которому, вполне возможно, при-
дется использовать его в качестве составной части какого-либо проекта.
Дам несколько советов по использованию комментариев.
Не загромождайте код комментариями к тем частям, назначение которых
понятно без лишних слов.
116
Например:

// Регистрируем обработчик щелчка на изображении


document.getElementById("im").addEventListener("click", func);

Здесь комментарий явно лишний — любой программист, увидев данную


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

// Объявляем функцию func


function func(t)
{
// Регистрируем счетчик кликов
let m=0;
// Запускаем цикл из 4 проходов
for(let d=0; d<5; d++)
{
// Проверяем переданное в функцию значение
if(t<d)
{
// Если переданное значение меньше d, увеличиваем счетчик на еди-
ницу
m++;
}
}
}

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


строки кода «теряются» среди строк пояснений. А это, в свою очередь, услож-
няет восприятие программы. Лучше сгруппировать все пояснения в много-
строчный комментарий и расположить его перед объявлением функции:

/* Объявляем функцию func и регистрируем счетчик кликов m.


/* Запускаем цикл из 4 проходов и проверяем переданное
/* в функцию значение. Если оно меньше текущего значения d,
/* увеличиваем счетчик m на единицу */

function func(t)
{
let m=0;
for(let d=0; d<5; d++)
{
if(t<d)
{
m++;
}
}
}

117

Powered by TCPDF (www.tcpdf.org)


При таком «построении» и тело функции «как на ладони», и сами ком-
ментарии вполне понятны.
Другой вариант — добавлять комментарии в конце строк кода:

function func(t) // Объявляем функцию func


{
let m=0; // Регистрируем счетчик кликов
for(let d=0; d<5; d++) // Запускаем цикл из 4 проходов
{
if(t<d) // Проверяем переданное в функцию значение
{ // Если переданное значение меньше d,
m++; // увеличиваем счетчик кликов на единицу
}
}
}

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


привязаны к нужным строкам.

118
5. Тестирование сценариев

Допустим, вы зашли на какой-то сайт. Он вам нравится — красиво


оформлен, интересные эффекты, все работает. Однако это только внешняя сто-
рона. К своему удивлению, проведя несложный «аудит» этого сайта с примене-
нием специальных, но очень простых методов, вы можете обнаружить, что за
красивым фасадом скрываются многочисленные ошибки в разметке, стилях и
коде JavaScript. Порой количество ошибок и недочетов переваливает за 100 и
даже за 200 только на одной странице. А суммарно они иногда исчисляются
тысячами. Причем такие показатели обнаруживаются даже у корпоративных
ресурсов студий web-дизайна, которые рекламируют услуги по созданию сай-
тов (в разделе 5.2 этой главы я продемонстрирую один пример из великого
множества подобных нелепиц, см. рис. 5.2.5).
Сайты с ошибками в коде страниц — это бракованный товар! «Грязный»
код ухудшает ранжирование сайтов в поисковых системах. А это — потерян-
ные посетители, клиенты, покупатели, заказчики.

Сомнительные практики
Очень придирчиво относитесь к выбору популярных платформ,
движков и CMS для создания сайтов. Да, такие системы очень облегчают
труд дизайнера и верстальщика. Но почти любая из них содержит в сво-
ем «ядре» многочисленные ошибки, которые потом перекочевывают в
ваши разработки. Если вам необходимо выполнить крупный проект —
смиритесь с неизбежным: без платформы, движка или CMS, скорее все-
го, не обойтись. Если проект небольшой и не нуждается в системе
управления контентом, попробуйте самостоятельно написать его «от
корки до корки». Так вы можете создать ресурс с абсолютно чистым ко-
дом. Есть еще один вариант действий: выберите CMS, которая вам нра-
вится больше всего, скачайте дистрибутив на свой компьютер и попро-
буйте исправить его исходный код. Работа большая и трудоемкая, зато у
вас появится исправленный экземпляр CMS, на копиях которого вы в
дальнейшем станете делать «чистые» сайты.

119
Надеюсь, вы не хотите оказаться в рядах горе-программистов? Тогда про-
веряйте разметку, стили и сценарии ваших проектов самым тщательным обра-
зом. Вот перечень самых необходимых действий:
 оптимизация кода;
 проверка кода с помощью валидаторов и устранение ошибок;
 проверка работоспособности сценариев в различных браузерах, в том
числе достижение идентичности результатов работы сценариев в различных
браузерах;
 устранение логических ошибок (если они обнаружены в процессе тести-
рования) в скриптах.
Рассмотрим этот перечень подробнее.

5.1. Оптимизация кода

Оптимальной можно считать программу, которая:


 максимально лаконична;
 занимает минимально возможное дисковое пространство;
 максимально быстро работает.
Современные посетители сайтов любят, чтобы все делалось как можно
быстрее, поэтому оптимизация кода играет очень важную роль. Долго грузится
страница? Самые нетерпеливые разочарованно покинут ваш ресурс. И наоборот,
быстрая загрузка уже на начальном этапе создаст у клиента приятное впечат-
ление.
На чем можно сэкономить при написании кода? На очень многом. Покажу
вам это на ряде примеров.
Допустим, у вас есть переменная a и вам надо увеличить ее значение на
единицу. Можно записать данную операцию так:

a=a+1;

А можно иначе:

a++;

Понятно, что вторая запись более лаконична — по сравнению с первой


мы сэкономили 2 символа.
Теперь представим, что у вас есть цикл со счетчиком. Его можно записать
так:

let i;
for(i=0; i<5; i++)
{
...
}

120
То есть сначала объявляем переменную i, а потом в описании условий
цикла присваиваем ей начальное значение.
Ясно, что следующий вариант данного цикла более краток:
for(let i=0; i<5; i++)
{
...
}

И опять экономия составила два символа.


Кстати, обратите внимание, что правильный выбор оператора цикла меж-
ду следующими вариантами — for, while, do while — также может сократить
ваш код.
Используйте досрочный выход из цикла посредством оператора break,
если вы уже получили требуемый результат, но предельное условие для цикла
еще не достигнуто.
Пойдем дальше. Помните, в главе 4 в разделе 4.2 у нас были примеры од-
новременной регистрации обработчиков событий? Вернемся к этим примерам.
Допустим, вам надо зарегистрировать три обработчика для одного события:
document.getElementById("im").addEventListener("click", func);
document.getElementById("im").addEventListener("click", doc);
document.getElementById("im").addEventListener("click", res);

Это слишком «тяжелая» запись. Ее можно сделать более «легкой»:


const v=document.getElementById("im");
v.addEventListener("click", func);
v.addEventListener("click", doc);
v.addEventListener("click", res);

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


document.getElementById("im").addEventListener("click", function()
{
func();
doc();
res();
});

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


регистрации обработчиков событий уровня окна можно не указывать объект
window. Вместо

window.addEventListener("load", func);
window.addEventListener("scroll", scro);

можно написать

addEventListener("load", func);
addEventListener("scroll", scro);

121
Вспомним про еще один способ оптимизации — путем уменьшения коли-
чества переменных. В разделе 4.11 у нас был пример с функцией, которая со-
держала два цикла. Вместо двух переменных, являющихся счетчиками — каж-
дая своего цикла, — мы использовали только одну переменную, тем самым
экономя на памяти:
function func()
{
...
let d;
for(d=0; d<5; d++)
{
...
}
...
for(d=0; d<3; d++)
{
...
}
...
}

Здесь у нас после завершения первого цикла переменная d обнуляется,


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

if(a<5)
{
b=0;
}

написать

if(a<5)
b=0;

Вместо

if(a<5)
{
b=0;
}
else
{
b=1;
}

122
использовать укороченную запись:

if(a<5)
b=0;
else
b=1;

Или вообще для проверки таких условий применять тернарный оператор:

let b=a<5?0:1;

Как вы помните, сократить можно и оформление оператора цикла, если


внутри него выполняется только одна инструкция. Вместо

for(let c=0; c<2; c++)


{
alert(c);
}

можно написать

for(let c=0; c<2; c++)


alert(c);

Экономить удастся даже на способах «внедрения» результатов работы


сценария на HTML-страницы. Конкретный пример. Допустим, у нас есть стра-
ница, на которую сразу после загрузки выводятся квадраты чисел от 1 до 5.
Напишем ее (файл 5.1_1.html в папке «Глава5» zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Квадраты чисел</title>
<script>
window.addEventListener("load", function()
{
let t="<table><tr><td>Число</td><td>Его квадрат</td></tr>";
for(let i=1; i<6; i++)
t+="<tr><td>"+i+"</td><td>"+i**2+"</td></tr>";
t+="</table>";
document.getElementById("di").insertAdjacentHTML("beforeend", t);
});
</script>
</head>
<body>
<div id="di">Квадраты чисел</div>
</body>
</html>

И сравним с более «легкой» версией этой же программы (файл 5.1_2.html


в папке «Глава5» zip-архива):
123
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Квадраты чисел</title>
<script>
window.addEventListener("load", function()
{
let t="<table><tr><td>Число</td><td>Его квадрат</td></tr>";
for(let i=1; i<6; i++)
t+="<tr><td>"+i+"</td><td>"+i**2+"</td></tr>";
document.getElementById("di").innerHTML="Квадраты чисел"+t+
"</table>";
});
</script>
</head>

<body>
<div id="di"></div>
</body>
</html>

В чем разница между версиями? В первом случае текст «Квадраты чисел»


уже размещен на странице. Поэтому добавлять результаты расчетов нам прихо-
дится в самый конец контейнера div методом insertAdjacentHTML, который
выглядит несколько громоздко. К тому же мы сначала добавляли к переменной
t завершающий тег таблицы

t+="</table>";

и только потом выводили на страницу готовый фрагмент разметки. В результа-


те у нас получился файл размером 501 байт.
Во втором случае контейнер div мы изначально оставили пустым, что
позволило использовать для вывода результатов более компактное свойство
innerHTML. Кроме того, мы окончательно сформировали необходимый фраг-
мент разметки прямо во время его вывода на страницу:

document.getElementById("di").innerHTML="Квадраты чисел"+t+
"</table>";

В результате второй вариант оказался легче первого на 25 байт.


В ряде случаев для «облегчения» кода и ускорения работы программы
можно использовать функции-стрелки, с которыми мы познакомились в главе 4,
разделе 4.11.
Ускоряет «запуск» сайта в браузере пользователя и такая вещь, как за-
грузка контента по частям. Если у вас «длинная» страница с большим объемом
текста и рисунков, разбейте данные на части, которые будут подгружаться по
мере прокрутки документа вниз. Для этого удобно воспользоваться технологи-
ей Ajax, о которой говорилось в главе 4, разделе 4.12. Примеры таких программ
вы можете посмотреть в моих уже упоминавшихся книгах «JavaScript. Готовые
программы» и «JavaScript. Обработка событий на примерах».
124
5.2. Валидаторы

Валидатор — это специальная программа для проверки кода, написанного


на одном из языков, предназначенном для создания web-проектов. Задача такой
программы — найти ошибки, если они есть, и выдать их перечень разработчику,
или сообщить, что код безупречный, если ошибок нет.
Можно сказать и по-другому: назначение того или иного валидатора —
проверять код на соответствие стандартам языка программирования.
Стандарты HTML и CSS разрабатывает Консорциум Всемирной Паутины.
Он же предоставляет и валидаторы для проверки разметки страниц и написания
таблиц стилей. Их адреса в сети:
 валидатор HTML5 — https://validator.w3.org/ (рис. 5.2.1);
 валидатор CSS3 — http://jigsaw.w3.org/css-validator/ (рис. 5.2.2).
Данные валидаторы очень удобны. Они позволяют проверять разметку и
таблицы стилей:
 страниц, уже загруженных в сеть;
 файлов, загруженных в валидатор с вашего компьютера;
 в коде, скопированном из вашего файла и помещенном в специальное
текстовое поле.
Например, после обработки кода разметки все ошибки и предупреждения
с комментариями на английском языке появятся на странице HTML-валидатора
в ее нижней части. При этом ошибки будут сопровождаться надписью Error на
розовым фоне, а предупреждения — надписью Warning на желтом фоне.

Рис. 5.2.1. Валидатор HTML5

125
Рис. 5.2.2. Валидатор CSS3

Рис. 5.2.3. Чистый код Консорциум Всемирной Паутины


предлагает отметить своеобразным «знаком качества»

126
Например, строка кода <script type="application/javascript"> получит
предупреждение, так как по современным стандартам указывать тип скрипта не
надо, но в то же время данная ошибка некритична, так как не мешает нормаль-
ному отображению страницы. Зато следующая строка <img src="img/
im0.jpg"> будет отмечена надписью Error, так как у рисунка пропущен обяза-
тельный, согласно стандартам HTML5, атрибут alt.
У HTML-валидатора есть еще одна полезная функция: по завершении
проверки он показывает, сколько времени было потрачено на загрузку вашей
страницы.
Если ошибок нет, валидаторы сообщат вам об этом. Любопытно заметить,
что о безупречности HTML-кода вы получите простое текстовое подтвержде-
ние. А вот качественную таблицу стилей Консорциум Всемирной Паутины
предлагает отметить специальным знаком (рис. 5.2.3) — что-то вроде знака ка-
чества, который вам будет предложено разместить на странице сайта (по край-
ней мере, так было на момент написания книги).
Что касается валидатора JavaScript, то здесь ситуация несколько иная. Та-
ких валидаторов много. Я опробовал несколько и выбрал из них вариант, в ко-
тором реализован самый строгий подход к оценке кода. Его адрес: валидатор
JavaScript — https://beautifytools.com/javascript-validator.php (рис. 5.2.4).

Рис. 5.2.4. Валидатор JavaScript

Он позволяет вставить в специальное текстовое поле ваш код, после чего


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

127
ные и т. д. Понятно, что задача хорошего программиста — исправить все ошиб-
ки и получить идеально чистый и правильный код.
Вообще код, полностью отвечающий всем стандартам — очень большая
редкость на просторах Интернета. Даже такие монстры, как Яндекс, Google и
YouTube, имеют множество ошибок в своих страницах. Хотите убедиться —
проверьте.

Рис. 5.2.5. Пример некачественной разметки

128
Мне осталось, как и обещал, привести случай некачественной разметки.
Я проверил код случайно выбранного сайта первой попавшейся мне студии
web-дизайна (рис. 5.2.5). Как видите, валидатор Консорциума Всемирной Пау-
тины обнаружил на одной странице ровно 100 нарушений стандартов HTML5.

5.3. Браузеры

Один из важнейших этапов тестирования сценариев — проверка их рабо-


тоспособности в разных web-обозревателях. Написав сценарий и убедившись,
что он корректно выполняется в вашем любимом браузере, не останавливайтесь
на достигнутом. Ведь может оказаться, что в другом браузере программа будет
работать совсем не так, как вы ожидаете.
Чем в большем количестве web-обозревателей вы проведете испытания,
тем лучше для вашего проекта. Это гарантирует, что все посетители вашего
сайта увидят на его страницах именно то, что вы хотели продемонстрировать.
На мой взгляд, обязательный набор разработчика должен включать мини-
мум 5 браузеров. Если вы решили серьезно заниматься программированием на
JavaScript, советую установить на свой компьютер эти браузеры. Расскажу о
них по порядку.
1. Браузер Microsoft Edge. Он входит в состав операционной системы
Windows 10, поэтому не нуждается в скачивании и установке. Многие опытные
программисты не любят данный браузер и игнорируют его при проверке сцена-
риев. Я считаю, что это серьезная ошибка. Дело в том, что Microsoft Edge явля-
ется браузером по умолчанию в операционной системе Windows 10. Многие
пользователи, не такие продвинутые, как программисты, просто используют то,
что у них изначально установлено на ПК. Следовательно, большой процент по-
сетителей Интернета прибегают к услугам Microsoft Edge.
2. Google Chrome. На мой субъективный взгляд — лучший браузер. И, су-
дя по некоторым опросам, наиболее популярный. Он моложе некоторых своих
конкурентов, но снабжен самыми передовыми решениями и очень быстро раз-
вивается. И это неудивительно, ведь за ним стоит такой гигант, как компания
Google. Скачать ПО можно здесь: https://www.google.ru/chrome/.
3. Яндекс.Браузер. После его создания компания Яндекс вложила замет-
ные средства в рекламу и популяризацию своей разработки. Поэтому данный
браузер стоит у многих владельцев компьютеров с ОС Windows. Этих людей
обязательно надо учитывать. Скачать дистрибутив можно здесь: https://browser.
yandex.ru/.
4. Opera — один из старейших браузеров, существовавших еще во време-
на борьбы за лидерство между Netscape Navigator и Internet Explorer. В России у
него много поклонников. Недаром среди российских пользователей Интернета
показатель его популярности в несколько раз выше, чем общемировой уровень.
Кстати, браузер Opera был одним из первых, кто начал поддерживать таблицы
стилей. Скачать программное обеспечение можно здесь: https://www.opera.
com/ru.

129
5. Mozilla Firefox. В этом браузере сценарии необходимо проверять в обя-
зательном порядке. У него достаточно высокий уровень популярности. При
этом есть ряд отличий в обработке кода по сравнению с четырьмя перечислен-
ными выше браузерами. Что-то Mozilla Firefox обрабатывает аналогично
остальным браузерам, а что-то по-своему. Во всяком случае, я неоднократно
сталкивался с ситуациями, когда код, отлично работавший в других браузерах,
начинал «капризничать» в Mozilla Firefox. Скачать браузер можно здесь:
https://www.mozilla.org/ru/firefox/.
Неплохо установить на свой смартфон мобильные версии перечисленных
браузеров. Тогда вы после размещения сайта на удаленном хостинге сможете
проверить, как работают ваши сценарии на мобильных устройствах.
И уж совсем идеальной можно считать проверку, которую, кроме всего
прочего, удалось провести на устройствах Apple с операционными системами
macOS и iOS и браузером Safari.
Думаю, что цель таких масштабных испытаний с использованием группы
разных браузеров понятна: необходимо добиться совершенно одинаковой рабо-
ты ваших программ во всех браузерах. Только получив этот результат, можно
считать свои программы вполне качественными.

5.4. Логические ошибки

Начну с общих рассуждений. Позволяя клиенту выполнять какие-то дей-


ствия на сайте, обязательно проверяйте, не содержит ли его функционал не
предусмотренных вами возможностей. Объясню ситуацию на примере из моей
практики. Однажды я запускал интернет-портал, в работе которого могли при-
нимать участие зарегистрированные пользователи. Они имели возможность
оставлять на своих страницах разные сообщения, а также редактировать по-
следнее из них. Для этого предназначались две кнопки: «Добавить сообщение»
и «Редактировать последнее сообщение». По моим представлениям, каждый
человек должен был интуитивно понимать, что сначала надо что-то написать, а
потом уже редактировать написанное. Поэтому обе кнопки изначально присут-
ствовали на странице. Такое решение оказалось в корне неверным. В качестве
первых участников портала я пригласил нескольких своих знакомых. И тут ме-
ня ожидал сюрприз. Двое из них начали с того, что попытались отправить текст
через форму «Редактировать последнее сообщение», что привело к возникнове-
нию ошибок в работе портала. Пришлось менять подход — кнопка редактиро-
вания стала появляться только после написания первого поста. Этот опыт
научил меня всегда придерживаться принципа: если посетитель может что-то
делать на сайте, необходимо так направлять его действия, чтобы он двигался
только одним, единственно верным путем.
Мы рассмотрели пример ошибки при создании интерфейса. Впрочем,
главная тема этого раздела — логические ошибки в коде сценариев. Они возни-
кают, когда вы упускаете какие-то нюансы или неверно применяете математи-
ческие алгоритмы. Казалось бы, проверка в валидаторах убеждает нас в полном

130
соответствии кода стандартам. Тем не менее наличие такой ошибки обнаружи-
вается, когда ваш сценарий начинает работать не так, как вы ожидали. При
этом очень часто бывает, что вычислить логическую ошибку совсем непросто.
Я расскажу вам о некоторых элементарных приемах поиска, обнаружения и ис-
правления ошибок.
Конечно, для проверки кода лучше всего использовать специальные ин-
струменты разработчика, встроенные во многие браузеры. Однако тема эта до-
статочно большая и сложная, формат данной книги не позволяет досконально
рассказать о всех возможностях таких инструментов. Поэтому мы рассмотрим
более простые способы.
Способ первый. В те блоки программы, в которых логика работы наруша-
ется, поочередно добавляйте в разные точки вызов метода alert(). Например, у
вас есть «сомнительный» фрагмент:
...
let c=a+b;
let t=v/d;
if(c<t/2)
{
n++;
document.getElementById("pict").style.left=n+"px";
}
else
{
n=0;
c=document.getElementById("img").offsetLeft;
}
...

Вставьте в разные точки этого блока вызов alert(), например так:


...
let c=a+b;
alert(c);

let t=v/d;
...

Потом так:
...
let t=v/d;
alert(t);

if(c<t/2)
...

А потом вот так:


...
if(c<t/2)
{
n++;
alert(n);

131
document.getElementById("pict").style.left=n+"px";
}
...

Или сразу во все три точки. Таким образом, вы последовательно увидите,


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

<div id= "test "><div>

Найдите фрагмент, вызывающий сомнения, например:

...
function coor()
{
let h=event.pageX;
let v=event.pageY;
let sh=document.getElementById("bat").offsetLeft;
let sv=document.getElementById("bat").offsetTop;
dh=h-sh;
dv=v+sv;
}
...

Добавьте в код сценария команду выведения данных:

...
dh=h-sh;
dv=v+sv;

document.getElementById("test").innerHTML=dh+" "+dv;
}
...

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


dv растет вместо того, чтобы оставаться постоянной. А значит, в выражении
dv=v+sv; знак плюс нужно поменять на минус.
Чтобы приблизить эти рассуждения к реалиям, рассмотрим возникнове-
ние ошибки и способ ее поиска на конкретном примере. Уже неоднократно
упоминавшаяся книга «JavaScript. Обработка событий на примерах» имеет сайт
поддержки, на котором представлены различные сценарии. В том числе, ресурс
содержит две страницы, не упоминаемые в книге. Страницы эти созданы спе-
циально для иллюстрации рассуждений данного раздела.

132
Итак, зайдем на первую из них: https://testjs.ru/e_p11.html. После загруз-
ки документа мы видим на странице только рамку. Пока указатель мыши не
попадает внутрь рамки, ничего не происходит.
Теперь медленно проведите курсором внутри рамки слева направо. Вы
обнаружите, что на белом фоне плавно возникнет фотография старинного ав-
томобиля (рис. 5.4.1).

Рис. 5.4.1. Пример страницы с логической ошибкой в сценарии

Рис. 5.4.2. Чтобы обнаружить ошибку, проведите по странице


указателем мыши так, как показано на рисунке

133
Проведите курсором в обратном направлении — справа налево. Фотогра-
фия также плавно «растворится». Наилучший эффект достигается именно при
медленном движении мыши. Если быстро провести указателем по рабочей об-
ласти, фотография проявится или растворится только частично. То же произой-
дет, если указатель начал двигаться не от боковых сторон рамок, а, например,
от середины изображения.
Не правда ли, интересный пример? Но вот беда: несмотря на кажущееся
благополучие в сценарии допущена логическая ошибка (естественно, сделано
это умышленно). Причем ошибку можно обнаружить не сразу. В чем же она?
Давайте еще поэкспериментируем. Проведите указателем мыши по рамке
так, как это показано на рисунке 5.4.2. Сначала слева направо по периметру
рамки, а затем ниже рисунка — когда вы возвращаете указатель справа налево.
Проделайте так трижды, а на четвертый раз медленно возвращайте указатель
мыши теперь уже по рисунку. Вы думаете, фото автомобиля плавно растворит-
ся? А вот и нет. Снимок по-прежнему будет ярким и сочным. В чем же дело?
Посмотрим код страницы:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Событие mousemove</title>
<style>
#pic {border: 1px solid #000000; width: 500px;}
#car {opacity: 0;}
</style>
<script>
window.addEventListener("load", function()
{
document.getElementById("car").addEventListener("mousemove",
opa);
});
let d=0;
let i=0;

function opa()
{
let h=event.pageX;
if(h-d>3)
{
i+=0.01;
document.getElementById("car").style.opacity=i;
d=h;
}
if(d-h>3)
{
i-=0.01;
document.getElementById("car").style.opacity=i;
d=h;
}
}

134

Powered by TCPDF (www.tcpdf.org)


</script>
</head>

<body>

<div id="pic"><img src="pict/oldcar.jpg" id="car" alt="Фото">


</div>
</body>
</html>

Как работает программа? В документе есть слой с фотографией автомо-


биля:

<div id="pic"><img src="pict/oldcar.jpg" id="car" alt="Фото">


</div>

В исходном состоянии автомобиль не виден:

#car {opacity: 0;}

В теле сценария у нас объявлены две переменные:

let d=0;
let i=0;

Переменная d предназначена для хранения предыдущей по отношению к


текущей координаты мыши по оси X. Переменная i — счетчик уровня непро-
зрачности.
Как только событие mousemove произойдет над рисунком (т. е. указатель
мыши начнет движение над рисунком), будет выполнен запуск обработчика.
Вот его код:

function opa()
{
let h=event.pageX;

if(h-d>3)
{
i+=0.01;
document.getElementById("car").style.opacity=i;
d=h;
}
if(d-h>3)
{
i-=0.01;
document.getElementById("car").style.opacity=i;
d=h;
}
}

135
Давайте считать, что сейчас курсор двигается слева направо. Первым де-
лом проверяется условие if(h-d>3). В начальный момент d имеет значение 0,
поэтому разность между h и d удовлетворяет условию. Таким образом, при по-
падании указателя мыши на фото будут выполняться инструкции первого бло-
ка if:

i+=0.01;
document.getElementById("car").style.opacity=i;
d=h;

В первой строке счетчик увеличивается на 0.01. Во второй строке его зна-


чение присваивается свойству непрозрачности изображения. В третьей выпол-
няется операция присвоения текущей координаты мыши переменной d.
Смотрим, что произойдет дальше. Курсор мыши продолжает движение
вправо, а значит, вторично запускается функция opa. Опять выполняется про-
верка условия if(h-d>3). Как видите, теперь сравнивается новая координата ука-
зателя со старой, полученной на предыдущем шаге и помещенной в перемен-
ную d. Если условие истинно, показатель непрозрачности опять увеличится.
Продолжаем вести мышь слева направо — значение переменной i все увеличи-
вается и увеличивается. В какой-то момент уровень непрозрачности достиг-
нет 1 и фотография станет видна полностью.
Теперь разберемся, что будет происходить во время движения указателя
мыши по картинке в обратном направлении. Как раз вторая часть функции и
предназначена для обработки таких действий посетителя:

if(d-h>3)
{
i-=0.01;
document.getElementById("car").style.opacity=i;
d=h;
}

В данном случае проверяется обратное условие:

if(d-h>3)

То есть если предыдущая координата мыши больше текущей, значит, ука-


затель двигается в обратном направлении, и показатель непрозрачности умень-
шается на 0.01:

i-=0.01;
document.getElementById("car").style.opacity=i;
d=h;

По достижении переменной i нулевой «отметки» рисунок полностью ис-


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

136
Чтобы разобраться в проблеме, давайте используем метод поиска логиче-
ских ошибок, о котором говорилось выше. Так как за показатель непрозрачно-
сти отвечает переменная i, проверим, что с ней происходит «за кадром». Лучше
всего внедрить в страницу элемент визуального отображения значений этой пе-
ременной. Например, вот так:

<div id="pic"><img src="pict/oldcar.jpg" id="car" alt="Фото">


</div>
<div id="ind">0</div>

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


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

function opa()
{
let h=event.pageX;
document.getElementById("ind").innerHTML=i;
...

Рис. 5.4.3. Данная версия сценария позволила нам обнаружить ошибку —


неконтролируемый рост значений переменной i

Для демонстрации работы новой версии программы у нас есть другая


страница: https://testjs.ru/i_p11.html. Повторим теперь на ней ход второго экс-
перимента, проводя указателем мыши по картинке слева направо и под картин-
кой — в обратном направлении. И мы тут же обнаружим, что, достигнув значе-
ния 1, программа не останавливается и переменная i продолжает расти (рис.
5.4.3). У возможных значений этой переменной вообще нет ограничений. Вот в
чем ошибка! Проведя 4 раза указателем мыши по рисунку, мы подняли значе-
ние i выше предельного значения для показателя непрозрачности. В результате,

137
после того, как мы один раз провели мышью в обратном направлении, показа-
тель непрозрачности по-прежнему остался выше 1 и картинка не растворилась.
То есть мы ошиблись с логикой работы программы, не учтя, что посети-
тель может повести себя на странице не так, как мы от него ожидаем. Как ис-
править недочет? Очень просто: надо ограничить рост и снижение значений пе-
ременной i. Ее показания при любом развитии событий не должны быть выше 1
и ниже 0.
На основе полученных данных внесем изменения в сценарий:
function opa()
{
let h=event.pageX;
if(h-d>3)
{
if(i<=1)
{
i+=0.01;
document.getElementById("car").style.opacity=i;
d=h;
}
else
{
i=1;
}
}
if(d-h>3)
{
if(i>=0)
{
i-=0.01;
document.getElementById("car").style.opacity=i;
d=h;
}
else
{
i=0;
d=0;
}
}
}

Здесь мы ввели проверку двух дополнительных условий:


if(i<=1)

и
if(i>=0)

Если они нарушаются, то выполняется такой блок инструкций:


else
{
i=1;
}

138
Или такой:
else
{
i=0;
d=0;
}

Теперь программа будет работать как надо. Ее правильную версию вы


можете увидеть в действии на странице https://testjs.ru/p11.html.
Надеюсь, рассмотрение этого случая полезно — как пример алгоритма
поиска логических ошибок в программах.
Наконец, еще один прием, который может вам пригодиться при отладке
программ. Если какой-либо фрагмент сценария или разметки вызывает у вас
сомнения, можно временно «отключить» его функционирование.
Допустим, на странице необходимо «изъять» некий элемент. Это можно
сделать, применив дескриптор комментария HTML:
<!-- ... -->

Вот конкретный пример временного удаления фотографии из разметки:

<!-- <img src="im1.png" id="im1"> -->

Отключить часть сценария можно, поставив перед необходимым фраг-


ментом символы комментариев, о которых мы говорили в разделе 4.16. Вот так:

// document.getElementById("im1").addEventListener("click", func);

Или так:
/* document.getElementById("im1").addEventListener("click",
func); */

Естественно, что первый вариант комментирования строки в сценарии


более рационален, так как символы // добавляются и удаляются быстрее, чем
во втором варианте.
Если временно требуется отключить несколько элементов разметки, то
вновь используйте дескриптор комментария HTML:
<!-- <img src="im1.png" id="im1">
<img src="im2.png" id="im2">
<img src="im3.png" id="im3"> -->

Отключить несколько строк в сценарии можно теми же способами, что и


в случае с одной строкой. Так
// document.getElementById("im1").addEventListener("click", func);
// document.getElementById("im2").addEventListener("click", func);
// document.getElementById("im3").addEventListener("click", func);

139
и так

/* document.getElementById("im1").addEventListener("click", func);
/* document.getElementById("im2").addEventListener("click", func);
/* document.getElementById("im3").addEventListener("click",
/* func); */

В этом случае более рационален второй вариант комментирования строк в


сценарии, так как символы /* и */ добавляются и удаляются быстрее, чем в
первом варианте.

5.5. Кстати

Раз уж мы в предыдущем разделе коснулись примера со сценарием


управления непрозрачностью фотографии, давайте посмотрим не только на то,
как четко отработана его логика. Проверим, какие результаты покажет код про-
граммы и разметка страницы при тестировании в соответствующих валида-
торах.
Приступим. Откройте в своем браузере страницу https://testjs.ru/p11.html.
Щелкните на пустой части документа правой кнопкой мыши и выберите пункт
«Посмотреть исходный код» (Microsoft Edge), или «Просмотр кода страницы»
(Google Chrome), или «Исходный код страницы» (Opera и Mozilla Firefox), или
«Посмотреть код страницы» (Яндекс.Браузер). В открывшейся вкладке с кодом
страницы скопируйте содержимое сценария между тегами <script> </script>
(но не копируя сами теги).
Запустите JavaScript-валидатор: https://beautifytools.com/javascript-
validator.php. В текстовое поле вставьте скопированный код. Кнопку «Validate
Code» можно не нажимать — программа начнет анализ кода сразу после его
вставки. Справа в поле для вывода результатов анализа вы увидите надпись
«No syntax errors!» (рис. 5.5.1), что означает отсутствие ошибок. Как видите, в
предыдущем разделе мы изучали вполне чистый и качественный сценарий.
Теперь запустите HTML-валидатор: https://validator.w3.org/. Скопируйте
адрес проверяемой страницы https://testjs.ru/p11.html и вставьте его в тексто-
вое поле «Address:». Нажмите кнопку «Check» и программа начнет анализ раз-
метки. Как только он завершится, откроется окно с извещением о результате
(рис. 5.5.2). Как видите, в разметке тоже нет ни единого нарушения — об этом
нам сообщает строка «Document checking completed. No errors or warnings to
show».
Проделаем аналогичные манипуляции для проверки качества заполнения
таблицы стилей в странице с примером. Запустите CSS-валидатор: http://
jigsaw.w3.org/ css-validator/. Скопируйте адрес проверяемой страницы https://
testjs.ru/p11.html и вставьте его в текстовое поле «Адрес». Нажмите кнопку
«Проверить». По завершении процесса откроется новое окно с результатом
анализа (рис. 5.5.3). Как видите, и в таблице стилей нет нарушений — об этом
нам сообщает строка «Поздравляем! Ошибок не обнаружено».

140
Рис. 5.5.1. Результат проверки сценария, «проявляющего» фотографию

Рис. 5.5.2. Результат проверки разметки страницы

141
Рис. 5.5.3. Результат проверки таблицы стилей

Что следует из результатов наших испытаний? А то, что я как автор не


только призываю вас писать «чистый» код, но и сам строго придерживаюсь
всех изложенных в этой книге правил. Кроме того, анализируя данную страни-
цу, мы научились пользоваться валидаторами. Это тоже полезно.

142
6. Примеры сценариев

Теория, конечно, хорошо, но практика лучше. Именно в процессе написа-


ния реальных сценариев программист по-настоящему осваивает JavaScript. Бу-
дем придерживаться такого подхода и мы. Рассмотрим изложенные в преды-
дущих главах принципы на примерах создания нескольких сценариев, предна-
значенных для работы с изображениями.
Но перед тем, как приступить к делу, несколько слов о программах, с ко-
торыми вам предстоит познакомиться. Во-первых, они максимально оптимизи-
рованы согласно рекомендациям раздела 5.1. главы 5. Я постарался сделать код
как можно «легче». Во-вторых, они проверены во всех валидаторах, упомяну-
тых в разделе 5.2. Никаких ошибок в коде нет. В-третьих, программы испытаны
во всех браузерах, упоминавшихся в разделе 5.3. Везде сценарии работают со-
вершенно одинаково.
Вы можете не только видеть примеры в действии, но также изучить их
«начинку». Как посмотреть код страницы вы уже знаете. Более того, вы можете
скачать zip-архив со всеми примерами и графическими файлами. Архив нахо-
дится по адресу https://testjs.ru/kpp/kpp.zip.
Наконец, последний комментарий — в сценариях использованы одни и те
же изображения автомобилей, но в двух форматах: png и jpg. Дело в том, что
для второго примера важно, чтобы рисунки «просвечивали» в тех частях, где
отсутствует изображение. А для четвертого, наоборот, необходимы картинки на
белом фоне.

6.1. Выбор картинки

Итак, приступим.
Самым первым рассмотрим в действии сценарий, который при выделении
одного рисунка меняет вид остальных.
Запустите в своем браузере страницу https://testjs.ru/kpp/k1.html. Вы
увидите 6 изображений различных автомобилей — современных и ретро (рис.
6.1.1).
Наведите указатель мыши на одну из картинок. Ее вид не изменится.
А вот остальные автомобили плавно «растворятся», да так, что будут еле видны
(рис. 6.1.2). Тем самым мы получили желаемый результат, выделив одно изоб-
ражение на фоне остальных.

143
Рис. 6.1.1. Так выглядит страница после загрузки

Рис. 6.1.2. При наведении указателя мыши на один из рисунков остальные


«бледнеют»

Приступим к написанию такой программы.


В первую очередь создадим шаблон документа, как мы это уже делали в
разделе 3.1 главы 3:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Выбор картинки</title>
</head>

<body>

144
</body>
</html>

Разместим на странице контейнер для остальных элементов:

<div class="bas">

</div>

Зададим его ширину — 1000 пикселей — и расположим посередине. Для


этого вставим в головную часть документа теги стилей

<style>

</style>

и в них пропишем настройки контейнера:

.bas {position: relative; width: 1000px; margin: auto;}

Добавим на страницу необходимые изображения:

<img id="i1" class="im" src="img/p1.png" alt="Фото">


<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">

Обратите внимание: каждый рисунок имеет собственный id, по которому


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

.im {width: 300px; border: 1px solid #000000; margin: 10px;}

При ширине рабочей области 1000px автомобили расположатся двумя ря-


дами.
Начнем писать сценарий. Добавим в заголовочный блок теги для скрип-
тов:

<script>
</script>

Назначим обработчик события уровня окна браузера — перемещению


указателя мыши по странице. Нам важно событие mouseover:

addEventListener("mouseover", ...);

145
Для сокращения программы используем в качестве обработчика стрелоч-
ную функцию:

addEventListener("mouseover", (ev)=>
{
...
});

В качестве аргумента ev функция будет получать параметры из интерфей-


са event (объекта события).
Как вы понимаете, нам важно не любое событие mouseover, а только
наведение указателя мыши на изображение. Поэтому первым делом обработчик
проверяет, где событие произошло:
if(e.tagName=="IMG")
{
...
}

Данные об этом мы получаем из интерфейса event:

let e=ev.target;

if(e.tagName=="IMG")

Если выясняется, что указатель мыши был наведен на одну из картинок,


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

if(e.tagName=="IMG")
{
...
}
else
{
...
}

Определим id рисунка:

e.id

Таким образом мы узнаем, какой из автомобилей останется в неизменном


виде.
Остальные авто надо сделать менее заметными.
Для этого в первой части условного выражения

if(e.tagName=="IMG")
{
...
}

146
напишем цикл, в котором все картинки, кроме выделенной, будут «мутнеть»:

for(let i=1; i<7; i++)


{
let b="i"+i;
if(e.id!=b)
{
let t=document.getElementById(b).style;
t.transition="opacity 1s";
t.opacity=0.1;
}
}

Поскольку рисунков 6, то условие выполнения цикла — i<7.


В каждом проходе мы вычисляем id очередного рисунка:

let b="i"+i;

Если текущий id не совпадает с id выделенного изображения

e.id!=b

то происходит плавное «растворение» очередного автомобиля в течение одной


секунды:

if(e.id!=b)
{
let t=document.getElementById(b).style;
t.transition="opacity 1s";
t.opacity=0.1;
}

Как видите, здесь мы меняем значение свойства opacity, которое отвечает


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

else
{
for(let i=1; i<7; i++)
{
let b="i"+i;
let t=document.getElementById(b).style;
t.transition="opacity 2s";
t.opacity=1;
}
}

147
Так как выделенный рисунок уже имеет уровень непрозрачности, равный
единице, то его можно не исключать из цикла for и не вводить ограничение
if(a!=b). В этой ситуации на каждом проходе мы плавно возвращаем каждое
изображение к исходному уровню максимальной непрозрачности.
При наведении указателя мыши на другое изображение все будет проис-
ходить точно таким же образом.
Думаю, что принцип действия сценария понятен всем читателям.
Соберем все фрагменты в единое целое (файл k1.html zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Выбор картинки</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;}
.im {width: 300px; border: 1px solid #000000; margin: 10px;}
</style>

<script>
addEventListener("mouseover", (ev)=>
{
let e=ev.target;
if(e.tagName=="IMG")
{
for(let i=1; i<7; i++)
{
let b="i"+i;
if(e.id!=b)
{
let t=document.getElementById(b).style;
t.transition="opacity 1s";
t.opacity=0.1;
}
}
}
else
{
for(let i=1; i<7; i++)
{
let b="i"+i;
let t=document.getElementById(b).style;
t.transition="opacity 2s";
t.opacity=1;
}
}
});
</script>
</head>

<body>
<div class="bas">
<img id="i1" class="im" src="img/p1.png" alt="Фото">
<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">

148
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">

</div>
</body>
</html>

Осталось добавить, что разное время «растворения» и «проявления»


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

6.2. Увеличиваем рисунки

Возьмемся за следующий пример. Его можно посмотреть по адресу


https://testjs.ru/kpp/k2.html.
Внешний вид страницы такой же, как и в предыдущем случае: снова 6 ав-
томобилей, расставленных в 2 ряда (рис. 6.2.1). Щелкните указателем мыши на
любом изображении и вы увидите, как оно плавно увеличится в размерах и од-
новременно сместится в центр (рис. 6.2.2). При этом оставшиеся авто не менее
плавно исчезнут (рис. 6.2.3). В таком состоянии страница может находиться
сколь угодно долго. Чтобы вернуть ей исходный вид, вторично щелкните по
данной картинке. Она уменьшится и займет свое место, а остальные рисунки
вновь станут видны. И опять все изменения произойдут плавно. Так можно
увеличивать и просматривать любые картинки, находящиеся на странице.
С чем мы имеем дело?
Основой служит все тот же слой

<div class="bas">
</div>

с теми же самыми настройками:

.bas {position: relative; width: 1000px; margin: auto;}

Тот же набор рисунков:

<img id="i1" class="im" src="img/p1.png" alt="Фото">


<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">

149
Рис. 6.2.1. Страница в исходном состоянии

Рис. 6.2.2. Выбранное изображение начинает увеличиваться


и смещаться к центру, а остальные рисунки «растворяются»

150
Рис. 6.2.3. Изображение заняло центральное положение,
остальные картинки в это время не видны

Но дальше начинаются принципиальные различия с предыдущим случаем.


В первую очередь у картинок иные настройки. Теперь изображения раз-
мещены не по блочному принципу, а методом позиционирования на слое-
контейнере:
.im {position: absolute; width: 300px;
border: 1px solid #000000; z-index: 1;}

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


z-index, равное единице.
Координаты для картинок:

#i1 {left: 10px; top: 10px;}


#i2 {left: 322px; top: 10px;}
#i3 {left: 634px; top: 10px;}
#i4 {left: 10px; top: 200px;}
#i5 {left: 322px; top: 200px;}
#i6 {left: 634px; top: 200px;}

Перейдем к сценарию.
Объявим 3 глобальные переменные:
let y=1;
let w=0;
let h=0;

Переменная y выполняет роль идентификатора кликов. Она позволяет


сценарию определить — первый или второй щелчок был на рисунке. То есть

151

Powered by TCPDF (www.tcpdf.org)


увеличивать изображение или, наоборот, уменьшать. Если y принимает значе-
ние 1, это означает первый клик, если 2 — второй.
Переменные w и h нужны для временного хранения координат рисунка,
на котором произошел клик, чтобы программа «знала», куда возвращать эту
картинку.
В новом сценарии мы регистрируем обработчик события click уровня
окна:

addEventListener("click", ...);

И опять используем функцию-стрелку:

addEventListener("click", (ev)=>
{
...
});

Так как обращений к интерфейсу event будет несколько, сократим код,


объявив переменную e:

let e=ev.target;

Проверяем, где выполнен клик:

if(e.tagName=="IMG")

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


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

let t=document.getElementById(e.id).style;

Если на данной картинке случился первый щелчок

if(y==1)

то последовательно выполняются следующие инструкции. Сначала выясняются


координаты рисунка относительно границ слоя-контейнера:

let d=document.getElementById(e.id);
w=d.offsetLeft;
h=d.offsetTop;

Затем переменной-идентификатору присваивается новое значение:

y=2;

152
Дальше стартует уже знакомый нам цикл, в котором выполняются опера-
ции «растворения» остальных изображений:

for(let i=1; i<7; i++)


{
let b="i"+i;
if(e.id!=b)
{
let p=document.getElementById(b).style;
p.transition="opacity 1s";
p.opacity=0;
}
}

Следом приходит очередь изменить состояние «главного действующего


лица». Сначала увеличивается z-index картинки, чтобы она в процессе движе-
ния располагалась поверх остальных рисунков:

t.zIndex=2;

Потом одновременно плавно увеличиваем ее размер и смещаем к центру


страницы:

t.transition="width 1s, left 1s, top 1s";


t.width=600+"px";
t.left=200+"px";
t.top=100+"px";

Суммарно результат выполнения первой части условного выражения вы-


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

else
{
...
}

Первым делом изменится значение, хранящееся в идентификаторе:

y=1;

Потом стартует цикл, в котором фоновые снимки появляются вновь, так


как меняется значение их непрозрачности:

for(let i=1; i<7; i++)


{
let b="i"+i;
let p=document.getElementById(b).style;

153
p.transition="opacity 2s";
p.opacity=1;
}

После чего запускается процесс уменьшения и обратного перемещения


просмотренного рисунка в точку, которая определена координатами, сохранен-
ными в переменных w и h:
t.transition="width 1s, left 1s, top 1s";
t.width=300+"px";
t.left=w+"px";
t.top=h+"px";

В последнюю очередь меняется свойство z-index. Оно возвращается к ис-


ходному значению:
t.zIndex=1;

Так как идентификатор в данный момент хранит единицу, сценарий готов


обработать следующий щелчок и увеличить любую другую картинку (или ту же
самую).
На этом описание работы программы завершено.
Полный код страницы (файл k2.html zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Увеличиваем рисунки</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;}
.im {position: absolute; width: 300px;
border: 1px solid #000000; z-index: 1;}
#i1 {left: 10px; top: 10px;}
#i2 {left: 322px; top: 10px;}
#i3 {left: 634px; top: 10px;}
#i4 {left: 10px; top: 200px;}
#i5 {left: 322px; top: 200px;}
#i6 {left: 634px; top: 200px;}
</style>

<script>
let y=1;
let w=0;
let h=0;
addEventListener("click", (ev)=>
{
let e=ev.target;

if(e.tagName=="IMG")
{
let t=document.getElementById(e.id).style;

if(y==1)
{

154
let d=document.getElementById(e.id);
w=d.offsetLeft;
h=d.offsetTop;

y=2;

for(let i=1; i<7; i++)


{
let b="i"+i;
if(e.id!=b)
{
let p=document.getElementById(b).style;
p.transition="opacity 1s";
p.opacity=0;
}
}

t.zIndex=2;
t.transition="width 1s, left 1s, top 1s";
t.width=600+"px";
t.left=200+"px";
t.top=100+"px";
}

else
{
y=1;
for(let i=1; i<7; i++)
{
let b="i"+i;
let p=document.getElementById(b).style;
p.transition="opacity 2s";
p.opacity=1;
}
t.transition="width 1s, left 1s, top 1s";
t.width=300+"px";
t.left=w+"px";
t.top=h+"px";
t.zIndex=1;
}
}
});
</script>
</head>

<body>
<div class="bas">

<img id="i1" class="im" src="img/p1.png" alt="Фото">


<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">
</div>
</body>
</html>

155
6.3. Галерея
Сценарий, который мы сейчас разберем, пожалуй, самый простой.
Зайдите на страницу https://testjs.ru/kpp/k3.html. Вы увидите, что
начальное расположение элементов изменилось. Теперь документ содержит
всего одно, но более крупное изображение автомобиля (рис. 6.3.1). Справа от
него находится указатель-стрелка. Нажмем ее. Изображение поменяется и од-
новременно слева появится еще один указатель (рис. 6.3.2). Продолжаем нажи-
мать правую стрелку до тех пор, пока не загрузится последний, шестой рисунок.
В этот момент правая стрелка исчезнет (рис. 6.3.3).
Как вы уже, наверное, поняли, стрелки-указатели выполняют роль кнопок
перемотки вперед или назад. Когда загружается последний рисунок, выключа-
ется перемотка вперед. После загрузки самого первого рисунка выключается
перемотка назад. Вот такая простая галерея картинок.

Рис. 6.3.1. В исходном состоянии, а также, когда галерея промотана влево


до конца, видна только правая кнопка

Рис. 6.3.2. Если демонстрируется не первый и не последний рисунки,


видны обе кнопки

156
Рис. 6.3.3. Когда галерея промотана вправо до конца,
видна только левая кнопка

Разберемся, как это все работает.


Базовый слой

<div class="bas">
</div>

остался неизменным. Сохранились и его основные параметры:

.bas {position: relative; width: 1000px; margin: auto;


text-align: center;}

Но появилось одно дополнение:

text-align: center;

То есть теперь элементы внутри контейнера выравниваются по середине.


Собственно, элементов только три.
1. Рисунок:

<img id="i1" class="im" src="img/p1.png" alt="Фото">

Как видите, в исходном состоянии загружена картинка p1.png.


Параметры рисунка:

.im {width: 600px; border: 1px solid #000000;


margin-top: 10px;}

2. Кнопка перемотки назад:

<img id="nz" class="bu" src="img/nz.png" alt="Назад">

157
3. Кнопка перемотки вперед:

<img id="vp" class="bu" src="img/vp.png" alt="Вперед">

Их настройки:

.bu {vertical-align: 140px; margin: 20px;}

Кроме того, сразу после загрузки документа кнопка перемотки назад


скрыта:

#nz {visibility: hidden;}

Для управления процессом у нас зарегистрированы 2 обработчика собы-


тий click на кнопках:

addEventListener("load", function()
{
document.getElementById("nz").addEventListener("click", nz);
document.getElementById("vp").addEventListener("click", vp);
});

Кроме того, мы предварительно объявили глобальную переменную i —


счетчик изображений. Поскольку на начальном этапе загружен рисунок p1.png,
переменной присвоено значение 1:

let i=1;

Рассмотрим сначала, что произойдет, если нажать кнопку перемотки впе-


ред. Будет запущена функция vp:

function vp()
{
if(i<6)
{
i++;
document.getElementById("nz").style.visibility="visible";
document.getElementById("i1").src="img/p"+i+".png";

if(i==6)
document.getElementById("vp").style.visibility="hidden";
}
}

Как видите, она очень простая. Ее инструкции выполняются до тех пор,


пока значение счетчика не превышает шести — то есть общего количества
изображений:

if(i<6)

158
Поскольку начальное значение счетчика равно единице, функция готова к
работе сразу после загрузки страницы.
Щелкнем мышью на кнопке перемотки вперед. Первым делом произойдет
увеличение переменной-счетчика:
i++;

Затем станет видимой кнопка перемотки назад:


document.getElementById("nz").style.visibility="visible";

После этого рисунку с авто будет присвоен новый адрес:


document.getElementById("i1").src="img/p"+i+".png";

Так как пока мы щелкнули только однажды, загрузится изображение


p2.png — следующее по очереди. Кликнем на кнопке перемотки вперед еще
раз — и увидим уже третью картинку. Так будет происходить до тех пор, пока
выполняется главное условие функции.
Когда галерея промотана до конца
if(i==6)

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


document.getElementById("vp").style.visibility="hidden";

Теперь посмотрим на функцию перемотки галереи назад. Она очень по-


хожа:
function nz()
{
if(i>1)
{
i--;
document.getElementById("vp").style.visibility="visible";
document.getElementById("i1").src="img/p"+i+".png";
if(i==1)
document.getElementById("nz").style.visibility="hidden";
}
}

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


не достигла начала галереи:
if(i>1)

Поскольку рисунки следуют в обратном порядке, после каждого щелчка


на кнопке перемотки назад переменная-счетчик уменьшается:
i--;

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

document.getElementById("vp").style.visibility="visible";

Это гарантирует, что при обратном движении изображений от последнего


кадра к началу скрытая кнопка перемотки вперед «объявится» вновь.
Пойдем дальше. После щелчка на кнопке перемотки назад будет показано
предыдущее изображение:

document.getElementById("i1").src="img/p"+i+".png";

Когда на странице появится первый рисунок

if(i==1)

кнопка перемотки назад станет невидимой:

document.getElementById("nz").style.visibility="hidden";

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


наоборот. Вы можете изучить 2–3 первых рисунка, а потом вернуться назад.
Прокрутить галерею до конца, потом отмотать ее на пару снимков назад и сно-
ва прокрутить вперед. Последовательность роли не играет. Во всех случаях
сценарий будет работать корректно.
Подведем черту — мы написали такую программу (файл k3.html zip-
архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Галерея</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;
text-align: center;}
.bu {vertical-align: 140px; margin: 20px;}
.im {width: 600px; border: 1px solid #000000;
margin-top: 10px;}
#nz {visibility: hidden;}
</style>

<script>
addEventListener("load", function()
{
document.getElementById("nz").addEventListener("click", nz);
document.getElementById("vp").addEventListener("click", vp);
});

160
let i=1;
function nz()
{
if(i>1)
{
i--;
document.getElementById("vp").style.visibility="visible";
document.getElementById("i1").src="img/p"+i+".png";
if(i==1)
document.getElementById("nz").style.visibility="hidden";
}
}
function vp()
{
if(i<6)
{
i++;
document.getElementById("nz").style.visibility="visible";
document.getElementById("i1").src="img/p"+i+".png";
if(i==6)
document.getElementById("vp").style.visibility="hidden";
}
}
</script>
</head>
<body>
<div class="bas">
<img id="nz" class="bu" src="img/nz.png" alt="Назад">
<img id="i1" class="im" src="img/p1.png" alt="Фото">
<img id="vp" class="bu" src="img/vp.png" alt="Вперед">
</div>
</body>
</html>

6.4. Слайдер

Рассмотрим следующий сценарий.


Он управляет слайдером, за работой которого можно наблюдать на стра-
нице https://testjs.ru/kpp/k4.html.
Посередине экрана расположен рисунок автомобиля (рис. 6.4.1), а под
ним кнопка перемотки с надписью «Следующий» (т. е. следующий кадр).
Нажмем ее. Первое изображение плавно уедет вверх, уступив место второму
(рис. 6.4.2). Снова нажмем кнопку. Второе изображение будет заменено треть-
им. И так далее — до последней, шестой картинки. Когда она на экране,
нажмем кнопку еще раз. Последнее изображение исчезнет за верхней границей
окна браузера, и останется только белый «лист» с кнопкой. Но буквально через
секунду на странице плавно возникнет первый рисунок (рис. 6.4.3). Слайдер го-
тов демонстрировать автомобили по новой.

161
И опять базой для элементов страницы служит контейнер
<div class="bas">

</div>

с настройками стилей
.bas {position: relative; width: 1000px; margin: auto;}

В контейнере 6 рисунков.

Рис. 6.4.1. Начальная картинка слайдера

Рис. 6.4.2. Первый кадр «уплывает» вверх, освобождая место


следующему рисунку

162
<img id="i1" class="im" src="img/p1.jpg" alt="Фото">
<img id="i2" class="im" src="img/p2.jpg" alt="Фото">
<img id="i3" class="im" src="img/p3.jpg" alt="Фото">
<img id="i4" class="im" src="img/p4.jpg" alt="Фото">
<img id="i5" class="im" src="img/p5.jpg" alt="Фото">
<img id="i6" class="im" src="img/p6.jpg" alt="Фото">

Рис. 6.4.3. Когда все изображения просмотрены, первое вновь


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

Их положение и оформление:
.im {position: absolute; top: 10px; left: 200px;
width: 600px; border: 1px solid #000000;}

Благодаря присвоению каждому изображению z-index, на единицу мень-


ше лежащего выше
#i1 {z-index: 7;}
#i2 {z-index: 6;}
#i3 {z-index: 5;}
#i4 {z-index: 4;}
#i5 {z-index: 3;}
#i6 {z-index: 2;}

получается, что все рисунки расположились «стопкой».


Наконец, добавляем кнопку
<input type="button" value="Следующий" id="bu">

позиционируем ее и оформляем внешний вид:


#bu {position: absolute; top: 380px; left: 200px;
width: 250px; height: 40px; border: 1px solid #000000;
box-shadow: #333333 5px 5px 8px; font-size: 20px;}

163
Зарегистрируем обработчик нажатия кнопки:

addEventListener("load", function()
{
document.getElementById("bu").addEventListener("click", but);
});

Объявим две глобальные переменные:

let k=10;
let s=0;

Переменная k необходима для временного хранения текущей координаты


рисунка, движущегося вверх. Переменная s — счетчик кадров.
Первое, что делает функция but после щелчка на кнопке, увеличивает на
единицу счетчика кадров

s++;

и присваивает переменной k начальное значение верхней координаты рисунка:

k=10;

На этом моменте остановимся подробнее. Зачем переменной k вновь при-


сваивать значение, которое присвоено ей при объявлении? Как мы уже узнали,
эта переменная хранит текущую координату движущейся картинки. Допустим,
первый рисунок уехал вверх за границы браузера. В момент, когда он остано-
вится, k будет хранить значение точки остановки, равное 700 пикселей. Значит,
при втором щелчке на кнопке переменной k нужно вернуть начальное значение
координаты по оси Y. Ведь именно эта координата взята за основу положения
второго рисунка в исходном состоянии (как, впрочем, и всех остальных).
Обработчик события имеет 2 стадии выполнения:

if(s<6)
...

if(s==6)
{
...
}

Пока мы не промотали все кадры, выполняется первое условие. Здесь все


просто:

if(s<6)
mov();

164
Как вы видите, при каждом щелчке запускается рекурсивная функция
mov:

function mov()
{
if(k>=-700)
{
document.getElementById("i"+s).style.top=k+"px";
k-=10;
setTimeout(mov, 10);
}
}

Она вызывает сама себя до тех пор, пока движущийся рисунок не достиг-
нет верхней границы:

if(k>=-700)

Когда это условие верно, свойству top рисунка последовательно с интер-


валом 10 миллисекунд присваивается новое значение координаты, на 10 пиксе-
лей меньше предыдущего:

document.getElementById("i"+s).style.top=k+"px";
k-=10;
setTimeout(mov, 10);

Достигнув верхнего предела, картинка останавливается.


Так происходит со всеми изображениями вплоть до предпоследнего, пя-
того. Щелчок на кнопке перемотки — и очередной кадр уезжает вверх.
Что меняется, когда вы в шестой раз нажимаете кнопку? Выполняется
условие

if(s==6)

В действие вступает второй блок инструкций

if(s==6)
{
...
}

Опять запускается функция mov, которая отправляет последнее изобра-


жение за границы экрана:

mov();

Через одну секунду переменным k и s присваиваются исходные значения:

setTimeout(()=>{k=10; s=0;}, 1000);

165
Тем самым сценарий подготавливается к новому запуску слайд-шоу с са-
мого начала.
Далее объявляем переменную h
let h=document.getElementById("i1").style;

посредством которой мы станем обращаться к свойствам первого рисунка. Де-


лаем его полностью невидимым:
h.opacity=0;

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


setTimeout(()=>
{
h.top="10px";
h.transition="opacity 1s";
h.opacity=1;
setTimeout(()=>
{
if(h.opacity==1)
{
for(let a=2; a<7; a++)
document.getElementById("i"+a).style.top="10px";
}
}, 1000);
}, 1000);

Первый таймер сработает через одну секунду. Второй будет запущен че-
рез одну секунду после первого.
Инструкции первого таймера возвращают первый рисунок в исходное по-
ложение
h.top="10px";

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


h.transition="opacity 1s";
h.opacity=1;

Теперь про вложенный таймер. Он стартует после того, как непрозрач-


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

if(h.opacity==1)

После этого запускается цикл


for(let a=2; a<7; a++)
document.getElementById("i"+a).style.top="10px";

который проходит по всем кадрам, начиная со второго, и возвращает их в ис-


ходное положение. С этого момента слайдер готов вновь прокручивать рисунки.

166
Вот что в итоге у нас получилось (файл k4.html zip-архива):
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Слайдер</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;}
.im {position: absolute; top: 10px; left: 200px;
width: 600px; border: 1px solid #000000;}
#bu {position: absolute; top: 380px; left: 200px;
width: 250px; height: 40px; border: 1px solid #000000;
box-shadow: #333333 5px 5px 8px; font-size: 20px;}
#i1 {z-index: 7;}
#i2 {z-index: 6;}
#i3 {z-index: 5;}
#i4 {z-index: 4;}
#i5 {z-index: 3;}
#i6 {z-index: 2;}
</style>
<script>
addEventListener("load", function()
{
document.getElementById("bu").addEventListener("click", but);
});
let k=10;
let s=0;
function but()
{
s++;
k=10;
if(s<6)
mov();
if(s==6)
{
mov();
setTimeout(()=>{k=10; s=0;}, 1000);
let h=document.getElementById("i1").style;
h.opacity=0;
setTimeout(()=>
{
h.top="10px";
h.transition="opacity 1s";
h.opacity=1;
setTimeout(()=>
{
if(h.opacity==1)
{
for(let a=2; a<7; a++)
document.getElementById("i"+a).style.top="10px";
}
}, 1000);
}, 1000);
167
}
}

function mov()
{
if(k>=-700)
{
document.getElementById("i"+s).style.top=k+"px";
k-=10;
setTimeout(mov, 10);
}
}
</script>
</head>

<body>
<div class="bas">

<img id="i1" class="im" src="img/p1.jpg" alt="Фото">


<img id="i2" class="im" src="img/p2.jpg" alt="Фото">
<img id="i3" class="im" src="img/p3.jpg" alt="Фото">
<img id="i4" class="im" src="img/p4.jpg" alt="Фото">
<img id="i5" class="im" src="img/p5.jpg" alt="Фото">
<img id="i6" class="im" src="img/p6.jpg" alt="Фото">

<input type="button" value="Следующий" id="bu">

</div>
</body>
</html>

6.5. Круговорот изображений

Пятый сценарий довольно простой.


Зайдем на страницу https://testjs.ru/kpp/k5.html. Она напоминает преды-
дущую. Разница только в отсутствии кнопки. Перед нами новый вариант слай-
дера, где изображения не уезжают вверх, а меняются несколько иным образом.
Смотрим, что делается после загрузки страницы. Проходит несколько се-
кунд и первый рисунок растворяется, а сквозь него проявляется второй (рис.
6.5.1). Снова пауза в несколько секунд — и второй рисунок сменяется третьим.
Так повторяется до появления завершающего, шестого рисунка. Он, в свою
очередь, «постояв» некоторое время, будет заменен на первое изображение, и
демонстрация автомобилей начнется вновь (рис. 6.5.2). То есть цикл просмотра
картинок представляет собой замкнутый круг.
В теле документа у нас вновь привычный набор элементов.
1. Слой-контейнер

<div class="bas">
</div>

168

Powered by TCPDF (www.tcpdf.org)


с теми же параметрами:

.bas {position: relative; width: 1000px; margin: auto;}

Рис. 6.5.1. Смена первого рисунка на второй

Рис. 6.5.2. Последняя картинка растворяется и на ее месте вновь


появляется первая

2. Уже хорошо знакомые нам рисунки автомобилей:

<img id="i1" class="im" src="img/p1.jpg" alt="Фото">


<img id="i2" class="im" src="img/p2.jpg" alt="Фото">
<img id="i3" class="im" src="img/p3.jpg" alt="Фото">
<img id="i4" class="im" src="img/p4.jpg" alt="Фото">
<img id="i5" class="im" src="img/p5.jpg" alt="Фото">
<img id="i6" class="im" src="img/p6.jpg" alt="Фото">

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

.im {position: absolute; top: 10px; left: 200px;


width: 600px; border: 1px solid #000000; opacity: 0;}

Для правильной работы сценария сразу перезапишем значение свойства


непрозрачности первого рисунка:

#i1 {opacity: 1;}

После загрузки страницы через 2 секунды дается старт рекурсивной


функции diss:

addEventListener("load", function()
{
setTimeout(diss, 2000);
});

Назначение функции — выполнять показ картинок по кругу. В ней есть


два блока условий:

function diss()
{
if(s<6)
{
...
}
else
{
...
}
}

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

let s=1;

Раз исходное значение переменной s равно единице, то выполняется пер-


вый блок инструкций. Сначала формируется id для текущего рисунка (в на-
чальный момент работы сценария — для первого).

let a="i"+s;

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


уровня непрозрачности opacity с 1 до 0 (напоминаю, что в отличие от всех
остальных картинок, первую мы уже сделали видимой):

170
let t=document.getElementById(a).style;
t.transition="opacity 2s";
t.opacity=0;

Теперь значение s увеличивается на единицу:


s++;

Дальше формируется id следующего рисунка


a="i"+s;

после чего он плавно делается видимым:


t=document.getElementById(a).style;
t.transition="opacity 2s";
t.opacity=1;

Через 3 с функция diss запускается повторно:


setTimeout(diss, 3000);

Так происходит, пока выполняется условие

if(s<6)

Когда переменная s достигает значения 6 (т. е. на экране демонстрируется


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

s=1;

Последнее авто становится невидимым

let t=document.getElementById("i6").style;
t.transition="opacity 2s";
t.opacity=0;

уступая место первому рисунку:

t=document.getElementById("i1").style;
t.transition="opacity 2s";
t.opacity=1;

Через 3 с функция diss запускается по новой

setTimeout(diss, 3000);

и начинается второй круг.

171
Демонстрация автомобилей будет происходить непрерывно до тех пор,
пока документ находится в окне браузера. Прервать цикл можно, только поки-
нув страницу.
Как видите, все действительно оказалось просто.
Ранее мы говорили, что бесконечная рекурсия опасна зависанием страни-
цы. В данном случае процесс организован так, что никакого зависания не будет.
Это редкое исключение из правила.
Ниже приведен листинг данной программы (файл k5.html zip-архива):
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Круговорот изображений</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;}
.im {position: absolute; top: 10px; left: 200px;
width: 600px; border: 1px solid #000000; opacity: 0;}
#i1 {opacity: 1;}
</style>
<script>
addEventListener("load", function()
{
setTimeout(diss, 2000);
});
let s=1;

function diss()
{
if(s<6)
{
let a="i"+s;
let t=document.getElementById(a).style;
t.transition="opacity 2s";
t.opacity=0;
s++;

a="i"+s;
t=document.getElementById(a).style;
t.transition="opacity 2s";
t.opacity=1;
setTimeout(diss, 3000);
}
else
{
s=1;
let t=document.getElementById("i6").style;
t.transition="opacity 2s";
t.opacity=0;
t=document.getElementById("i1").style;
t.transition="opacity 2s";
t.opacity=1;

172
setTimeout(diss, 3000);
}
}
</script>
</head>

<body>
<div class="bas">

<img id="i1" class="im" src="img/p1.jpg" alt="Фото">


<img id="i2" class="im" src="img/p2.jpg" alt="Фото">
<img id="i3" class="im" src="img/p3.jpg" alt="Фото">
<img id="i4" class="im" src="img/p4.jpg" alt="Фото">
<img id="i5" class="im" src="img/p5.jpg" alt="Фото">
<img id="i6" class="im" src="img/p6.jpg" alt="Фото">
</div>
</body>
</html>

Интересно, что практически во всех сценариях, за исключением примера


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

6.6. Пасьянс из картинок

Очередной сценарий получил свое название от сходства с раскладывани-


ем карточного пасьянса.
Зайдем на страницу https://testjs.ru/kpp/k6.html. На ней мы увидим две
кнопки, расположенные в нижней части: «Показать» и «Скрыть». И все. В ис-
ходном состоянии на странице ничего не происходит.
Нажмем кнопку «Показать». В центре окна браузера по очереди, начиная
с верхнего левого угла, с интервалом в полсекунды станут появляться уже хо-
рошо знакомые нам изображения автомобилей (рис. 6.6.1). Нажмем кнопку
«Скрыть». Изображения станут исчезать с таким же интервалом, но в обратном
порядке.
Приступим к разбору сценария.
Традиционно основой для галереи картинок выбран слой-контейнер

<div class="bas">
</div>

с привычными настройками:

.bas {position: relative; width: 1000px; margin: auto;


text-align: center;}

173
Контейнер содержит 6 рисунков:

<img id="i1" class="im" src="img/p1.png" alt="Фото">


<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">

Рис. 6.6.1. Раскладываем автомобильный пасьянс

В начальном состоянии все рисунки невидимы, так как их свойствам не-


прозрачности opacity (вновь это полезное свойство!) присвоено значение 0:

.im {width: 300px; border: 1px solid #000000;


margin: 10px; opacity: 0;}

Кроме того, в контейнере есть 2 кнопки:

<input type="button" value="Показать" id="bup">


<input type="button" value="Скрыть" id="bum">

При нажатии любой из них запускается одна и та же функция but:

addEventListener("load", function()
{
document.getElementById("bup").addEventListener("click", but);
document.getElementById("bum").addEventListener("click", but);
});

174
Еще до регистрации обработчика мы объявляем две переменные:
let a=1;
let b;

Переменная a является счетчиком изображений, а b получает данные о


том, на какой из кнопок произошло событие click.
Итак, как уже было сказано выше, после клика на одной из кнопок запус-
кается функция but:
function but(ev)
{
b=ev.target.id;
opas();
}

Ее задача определить источник события click


b=ev.target.id;

а после этого запустить функцию opas, которая и выполняет основные манипу-


ляции с картинками:

opas();

Функция opas содержит два блока инструкций, которые выполняются в


зависимости от того, на какой кнопке был выполнен клик:

if(b=="bup")
{
...
}

if(b=="bum")
{
...
}

Предположим, что мы щелкнули на кнопке «Показать». Тогда будет за-


действован первый блок инструкций:
document.getElementById("i"+a).style.opacity=1;
a++;
if(a<7)
setTimeout(opas, 500);
else
a=6;

Поскольку начальное значение переменной a равно единице, инструкция


document.getElementById("i"+a).style.opacity=1;

175
сделает видимой первую картинку. После этого значение счетчика увеличится
на 1:

a++;

Поскольку на начальном этапе выполняется условие

if(a<7)

то через 500 миллисекунд функция opas запустится снова:

setTimeout(opas, 500);

Так происходит, пока не появятся все автомобили. Когда условие

if(a<7)

станет ложным, рекурсия будет остановлена. Все рисунки окажутся на страни-


це. В этот момент значение переменной a достигнет 7. Чтобы функция не поте-
ряла «работоспособность», вернем счетчику индекс последнего изображения:

else
a=6;

Теперь нажмем кнопку «Скрыть». В дело вступит следующий блок ин-


струкций:

if(b=="bum")
{
document.getElementById("i"+a).style.opacity=0;
a--;
if(a>0)
setTimeout(opas, 500);
else
a=1;
}

Здесь все будет происходить в обратном порядке: сначала исчезнет по-


следний рисунок, потом предпоследний и так до самого первого. На последнем
шаге переменная a получит значение 0. Теперь возвращаем ей индекс первой
картинки:

else
a=1;

Все. Программа вернулась в исходное состояние и готова к новому за-


пуску.

176
Обращаю ваше внимание, что нажатие кнопок «Показать» и «Скрыть» в
хаотичном порядке (например, два раза «Показать» и три раза «Скрыть») не
приведет к нарушению работы сценария.
Подведем черту, продемонстрировав код готовой программы (файл
k6.html zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Пример двунаправленной рекурсии</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;
text-align: center;}
.im {width: 300px; border: 1px solid #000000;
margin: 10px; opacity: 0;}
</style>
<script>
addEventListener("load", function()
{
document.getElementById("bup").addEventListener("click", but);
document.getElementById("bum").addEventListener("click", but);
});
let a=1;
let b;
function but(ev)
{
b=ev.target.id;
opas();
}

function opas()
{
if(b=="bup")
{
document.getElementById("i"+a).style.opacity=1;
a++;
if(a<7)
setTimeout(opas, 500);
else
a=6;
}

if(b=="bum")
{
document.getElementById("i"+a).style.opacity=0;
a--;

if(a>0)
setTimeout(opas, 500);
else
a=1;
}
}

177
</script>
</head>

<body>
<div class="bas">

<img id="i1" class="im" src="img/p1.png" alt="Фото">


<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">

<input type="button" value="Показать" id="bup">


<input type="button" value="Скрыть" id="bum">

</div>
</body>
</html>

6.7. Проявление и «растворение»

Помните, в разделе 4.10 мы говорили о том, что один и тот же цикл мож-
но «заставить» работать в любом направлении: как увеличивать значение счет-
чика, так и уменьшать? Сейчас мы займемся сценарием, в котором проделаем
такие «фокусы» с циклом while.
Страница находится по адресу https://testjs.ru/kpp/k7.html. Если зайти на
нее, то вы обнаружите то же самое, что и в предыдущем примере: две кноп-
ки — «Показать» и «Скрыть». А все потому, что новая программа — это твор-
ческое развитие предыдущей.
В чем недостаток примера из раздела 6.6? В резком появлении изображе-
ний. Давайте избавимся от этого эффекта. Пусть картинки появляются и скры-
ваются более плавно. И, как уже было неоднократно, в этом нам поможет свой-
ство opacity.
Нажмем кнопку «Показать». На странице поочередно и, главное, плавно
возникнут все шесть картинок (рис. 6.7.1). Щелкнем на кнопке «Скрыть». Ав-
томобили также поочередно и плавно «растворятся».
Начинка документа точно такая же, как и предыдущем случае.
Слой-контейнер

<div class="bas">

</div>

с настройками:

.bas {position: relative; width: 1000px; margin: auto;


text-align: center;}

178
6 рисунков:

<img id="i1" class="im" src="img/p1.png" alt="Фото">


<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">

Рис. 6.7.1. Проявление и «растворение» изображений

В начальном состоянии все рисунки невидимы:

.im {width: 300px; border: 1px solid #000000;


margin: 10px; opacity: 0;}

Две кнопки:

<input type="button" value="Показать" id="bup">


<input type="button" value="Скрыть" id="bum">

запускающие одну и ту же функцию but:

addEventListener("load", function()
{
document.getElementById("bup").addEventListener("click", but);
document.getElementById("bum").addEventListener("click", but);
});

Две переменные:
let a=1;
let b=1;

179
С этого момента начинаются различия. Переменной b мы сразу присвои-
ли значение 1. Также изменилась роль переменных: a теперь не только счетчик
изображений, но и показатель временного интервала «проявления» картинки, а
b хранит значения временного интервала «растворения» картинки.
Дальше. По сравнению с предыдущим случаем здесь у нас только одна
функция — but. Она и выполняет все необходимые манипуляции с изображе-
ниями.
Главное действующее «лицо» функции — цикл while:

while(true)
{
...
}

Так как в условии его выполнения указано значение «истина», такой цикл
не имеет изначально заданных ограничений в количестве проходов. Остановка
проходов выполняется в определенные моменты инструкцией break.
Внутри цикла есть 2 условных блока:

if(ev.target.id=="bup")
{
...
}
if(ev.target.id=="bum")
{
...
}

Инструкции первого выполняются, когда нажата кнопка «Показать», а


инструкции второго — при нажатии кнопки «Скрыть».
Пойдем по порядку. Представим, что щелчок выполнен на кнопке «Пока-
зать». Начинается выполнение первого блока:

document.getElementById("i"+a).style.transition="opacity "+a+"s";
document.getElementById("i"+a).style.opacity=1;
a++;
if(a==7)
{
a=6;
break;
}

Инструкции

document.getElementById("i"+a).style.transition="opacity "+a+"s";
document.getElementById("i"+a).style.opacity=1;

плавно добавляют первый рисунок на страницу в течение 1 с (так как начальное


значение переменной a — единица).
180
Затем показатель временного интервала увеличивается

a++;

и после второго прохода время появления второго изображения будет уже 2 с.


После этого временной интервал снова увеличивается. Так происходит шесть
раз. В результате последняя картинка «проявляется» в течение 6 с.
Мы видим, что на шестом проходе переменная a снова увеличивается и ее
значение достигает 7. В этот момент и происходит остановка цикла:

if(a==7)
{
a=6;
break;
}

Одновременно переменной a возвращаются показатели верхней границы


временного интервала и количества рисунков:

a=6;

Итак, все автомобили на странице. Теперь нажмем кнопку «Скрыть».


Начинают выполняться инструкции второго блока:

document.getElementById("i"+a).style.transition="opacity "+b+"s";
document.getElementById("i"+a).style.opacity=0;
a--;
b++;
if(a==0)
{
a=1;
b=1;
break;
}

Шестой рисунок плавно исчезает в течение 1 с (так как начальное значе-


ние переменной b — единица, а значение переменной a в этот момент равно 6):

document.getElementById("i"+a).style.transition="opacity "+b+"s";
document.getElementById("i"+a).style.opacity=0;

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

a--;

а показатель временного интервала, наоборот, возрастает:

b++;

181
Так происходит шесть раз. Номера картинок уменьшаются от последнего
к первому, а временной интервал «растворения» увеличивается от 1 до 6 с.
На шестом проходе переменная a снова уменьшается и ее значение дости-
гает 0. Переменная b тоже выходит «за рамки» — ее значение достигает 7.
А это значит, что наступил момент остановки цикла:

if(a==0)
{
a=1;
b=1;
break;
}

Одновременно переменным a и b возвращаются исходные значения:

a=1;
b=1;

С этого момента программа готова к повторному использованию.


Как и в предыдущем случае, нажатие кнопок «Показать» и «Скрыть» в
хаотичном порядке не приведет к нарушению работы сценария.
Полный код новой версии (файл k7.html zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Пример двунаправленного цикла</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;
text-align: center;}
.im {width: 300px; border: 1px solid #000000;
margin: 10px; opacity: 0;}
</style>
<script>
addEventListener("load", function()
{
document.getElementById("bup").addEventListener("click", but);
document.getElementById("bum").addEventListener("click", but);
});

let a=1;
let b=1;
function but(ev)
{
while(true)
{
if(ev.target.id=="bup")
{
document.getElementById("i"+a).style.transition=
"opacity "+a+"s";
document.getElementById("i"+a).style.opacity=1;
a++;

182
if(a==7)
{
a=6;
break;
}
}
if(ev.target.id=="bum")
{
document.getElementById("i"+a).style.transition=
"opacity "+b+"s";
document.getElementById("i"+a).style.opacity=0;
a--;
b++;
if(a==0)
{
a=1;
b=1;
break;
}
}
}
}
</script>
</head>
<body>
<div class="bas">
<img id="i1" class="im" src="img/p1.png" alt="Фото">
<img id="i2" class="im" src="img/p2.png" alt="Фото">
<img id="i3" class="im" src="img/p3.png" alt="Фото">
<img id="i4" class="im" src="img/p4.png" alt="Фото">
<img id="i5" class="im" src="img/p5.png" alt="Фото">
<img id="i6" class="im" src="img/p6.png" alt="Фото">
<input type="button" value="Показать" id="bup">
<input type="button" value="Скрыть" id="bum">
</div>
</body>
</html>

6.8. Рисунки по номерам

Мы уже рассматривали вариант галереи, где пролистывание изображений


выполнялось кнопками «Вперед» и «Назад». Теперь напишем сценарий для га-
лереи, где роль кнопок выполняют ссылки на номера картинок.
Такая галерея представлена здесь: https://testjs.ru/kpp/k8.html (рис. 6.8.1).
Как видите, все очень просто. В начальный момент в окне браузера пока-
зан первый автомобиль. Ссылка на него подсвечена красным цветом и не ак-
тивна. Нажмите на ссылку с любым другим номером — и соответствующее
изображение появится на странице. Одновременно ссылка с номером 1 активи-
руется, а нажатая, наоборот, станет неактивной (рис. 6.8.2). Плюс такой галереи
в том, что просматривать автомобили можно в любом порядке.
183
Рис. 6.8.1. Начальное состояние галереи картинок

Рис. 6.8.2. Пролистываем изображения

Приступим к изучению данной программы.


Как и в предыдущих случаях, вся «начинка» расположена в контейнере
<div class="bas">
</div>

с привычными свойствами:
.bas {position: relative; width: 1000px; margin: auto;
text-align: center;}
184
В центре контейнера — рисунок номер один:
<img id="im" src="img/p1.png" alt="Фото">

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


<p id="bu">
<a href="img/p1.png" class="curs">1</a>
<a href="img/p2.png">2</a>
<a href="img/p3.png">3</a>
<a href="img/p4.png">4</a>
<a href="img/p5.png">5</a>
<a href="img/p6.png">6</a>
</p>

с крупными цифрами:
p {font-size: 30px;}

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


a {margin: 20px; color: #0000CC;}

При этом первая из них

<a href="img/p1.png" class="curs">1</a>

не активна:

.curs {cursor: auto; color: #CC0000;}

В начале сценария объявляем переменную a:

let a;

Она понадобится для сохранения адреса контейнера <p id="bu"> </p>,


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

addEventListener("load", function()
{
a=document.getElementById("bu");
a.addEventListener("click", but);
});

Функция but очень простая:

function but(ev)
{
let h=ev.target.href;

185
if(h.indexOf("img"))
{
document.getElementById("im").src=h;

for(let n=0; n<a.children.length; n++)


{
let b=a.children[n];
b.className=(b!=ev.target)?"":"curs";
}
}

ev.preventDefault();
}

Из интерфейса event получаем значение атрибута href той ссылки, на ко-


торой был сделан щелчок

let h=ev.target.href;

и выясняем, действительно ли клик был именно на ссылке:

if(h.indexOf("img"))
{
...
}

Если условие истинно, присваиваем значение атрибута href атрибуту src


рисунка:

document.getElementById("im").src=h;

Таким образом, мы загружаем выбранную картинку вместо исходной.


Затем вычисляем ссылку, которая должна стать неактивной:

for(let n=0; n<a.children.length; n++)


{
let b=a.children[n];
b.className=(b!=ev.target)?"":"curs";
}

В этом цикле количество проходов ограничивается количеством дочерних


элементов контейнера <p id="bu"> </p>:

n<a.children.length

В теле цикла выполняем две операции.


1. Присваиваем переменной b адрес очередной ссылки (для сокращения
кода):

let b=a.children[n];

186
2. Посредством тернарного оператора выясняем, какие ссылки должны
стать активными, а какая ссылка, наоборот, неактивной:

b.className=(b!=ev.target)?"":"curs";

Для этого мы используем условие

b!=ev.target

Ссылке, на которой произошло событие click, присваивается значение


curs атрибута class. У остальных ссылок атрибут class получает пустое значе-
ние. Благодаря этому ссылка, на которой был щелчок, становится неактивной,
а остальные — активными.
Последняя инструкция

ev.preventDefault();

отменяет поведение браузера по умолчанию. Если бы ее не было, то при первом


же щелчке на ссылке загружалась бы другая страница с фотографией на темном
фоне.
Как видите, ничего сложного.
Ниже представлен код данной галереи (файл k8.html zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Галерея</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;
text-align: center;}
p {font-size: 30px;}
a {margin: 20px; color: #0000CC;}
.curs {cursor: auto; color: #CC0000;}
</style>
<script>
let a;
addEventListener("load", function()
{
a=document.getElementById("bu");
a.addEventListener("click", but);
});

function but(ev)
{
let h=ev.target.href;

if(h.indexOf("img"))
{
document.getElementById("im").src=h;

187
for(let n=0; n<a.children.length; n++)
{
let b=a.children[n];
b.className=(b!=ev.target)?"":"curs";
}
}
ev.preventDefault();
}
</script>
</head>
<body>
<div class="bas">
<img id="im" src="img/p1.png" alt="Фото">

<p id="bu">
<a href="img/p1.png" class="curs">1</a>
<a href="img/p2.png">2</a>
<a href="img/p3.png">3</a>
<a href="img/p4.png">4</a>
<a href="img/p5.png">5</a>
<a href="img/p6.png">6</a>
</p>
</div>
</body>
</html>

6.9. Три галереи

«На закуску» у нас довольно простая, но в то же время универсальная


программа.
Во всех предыдущих сценариях мы рассматривали ситуацию, когда доку-
мент содержал всего одну галерею изображений. Настало время усложнить за-
дачу. Попробуем написать страницу, где будет сразу три галереи. А для этого
воспользуемся методом, который мы использовали в примере из предыдущего
раздела. С одной оговоркой: метод необходимо подкорректировать.
Сценарий из раздела 6.8 составлен так, что функция but может работать
только с конкретным набором фотографий, размещенных в узле с id="bu".
Наша задача — изменить ситуацию и написать такую функцию, которая могла
бы «обслуживать» произвольное, не заданное изначально количество галерей.
Что мы успешно и реализуем в последнем примере.
Итак, есть страница https://testjs.ru/kpp/k9.html. Здесь три набора изоб-
ражений: автомобилей, самолетов, яхт (рис. 6.9.1). Под каждым рисунком
ссылки на остальные картинки из соответствующего набора. Ссылки можно
щелкать в любом порядке (рис. 6.9.2).
Убедившись, что все работает корректно, приступим к разбору сценария,
который управляет галереями.

188

Powered by TCPDF (www.tcpdf.org)


Рис. 6.9.1. Исходное состояние страницы

Рис. 6.9.2. Пролистываем фотографии сразу во всех галереях

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

<div class="bas">
</div>

с настройками:

.bas {position: relative; width: 1000px; margin: auto;


text-align: center;}

Поскольку у нас теперь три галереи, для их размещения мы использовали


таблицу

<table>
<tr>
<td><img src="img/p1.png" alt="Фото">
<p id="bu1">
<a href="img/p1.png" class="curs">1</a>
<a href="img/p2.png">2</a>
<a href="img/p3.png">3</a>
<a href="img/p4.png">4</a>
189
<a href="img/p5.png">5</a>
<a href="img/p6.png">6</a>
</p></td>

<td><img src="pict/p1.jpg" alt="Фото">


<p id="bu2">
<a href="pict/p1.jpg" class="curs">1</a>
<a href="pict/p2.jpg">2</a>
<a href="pict/p3.jpg">3</a>
<a href="pict/p4.jpg">4</a>
</p></td>
<td><img src="canv/p1.jpg" alt="Фото">
<p id="bu3">
<a href="canv/p1.jpg" class="curs">1</a>
<a href="canv/p2.jpg">2</a>
<a href="canv/p3.jpg">3</a>
<a href="canv/p4.jpg">4</a>
<a href="canv/p5.jpg">5</a>
</p></td>
</tr>
</table>

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


количество ссылок. Как и прежде, рисунки располагаются в контейнерах <p>
</p>. Только теперь в их id кроме символов bu добавлены порядковые номера.
Размеры фото мы уменьшили и сделали им рамки:

img {width: 300px; border: 1px solid #000000;}

Во всех галереях первые ссылки не активны:

.curs {cursor: auto; color: #CC0000;}

Компоновка данной страницы более плотная, поэтому расстояние между


ссылками уменьшено:

a {margin: 10px; color: #0000CC;}

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


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

addEventListener("load", function()
{
let t=document.getElementsByTagName("P");
for(let k=0; k<t.length; k++)
t[k].addEventListener("click", but);
});

На первом этапе получаем массив узлов p:


let t=document.getElementsByTagName("P");

190
А затем для каждого узла регистрируем обработчик, который вызывает
одну и ту же функцию but:
for(let k=0; k<t.length; k++)
t[k].addEventListener("click", but);

Сама функция содержит набор простых инструкций:


function but(ev)
{
let a=parseFloat(ev.target.parentElement.id.substr(2))-1;
document.getElementsByTagName("IMG")[a].src=ev.target.href;
for(let n=0; n<this.children.length; n++)
{
let b=this.children[n];
b.className=(b!=ev.target)?"":"curs";
}
ev.preventDefault();
}

Вычисляем номер галереи, где произошло событие click, удаляя из id ла-


тинские буквы:
let a=parseFloat(ev.target.parentElement.id.substr(2))-1;

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


к числовому:
parseFloat(ev.target.parentElement.id.substr(2))-1;

Операция -1 добавлена, поскольку нумерация id узлов начинается с 1,


а нумерация массива этих узлов — с 0.
Обратите внимание: щелчок мышью произошел на дочернем элементе
контейнера p. Поэтому мы обратились к свойству parentElement — оно воз-
вращает родительский узел для данной ссылки.
Из интерфейса event получаем значение атрибута href той ссылки, на ко-
торой был сделан щелчок, и присваиваем его значение атрибуту src рисунка
соответствующей галереи:
document.getElementsByTagName("IMG")[a].src=ev.target.href;

Здесь

document.getElementsByTagName("IMG")

массив начальных изображений на странице, а


document.getElementsByTagName("IMG")[a]

изображение из галереи, где произошло событие click.

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

for(let n=0; n<this.children.length; n++)


{
let b=this.children[n];
b.className=(b!=ev.target)?"":"curs";
}

Опять у нас количество проходов ограничивается количеством дочерних


элементов контейнера p:

n<this.children.length

Снова в цикле две операции.


1. Присваиваем переменной b адрес очередной ссылки:

let b=this.children[n];

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


стать активными, а какая ссылка — неактивной:

b.className=(b!=ev.target)?"":"curs";

Ссылке из текущей галереи, на которой произошло событие click, присва-


ивается значение curs атрибута class. У остальных ссылок атрибут class полу-
чает пустое значение. Ссылка, на которой был щелчок, становится неактивной,
остальные — активными.
Последняя инструкция

ev.preventDefault();

отменяет поведение браузера по умолчанию.


Вся программа выглядит так (файл k9.html zip-архива):

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Галереи</title>
<style>
.bas {position: relative; width: 1000px; margin: auto;
text-align: center;}
p {font-size: 30px;}
a {margin: 10px; color: #0000CC;}
img {width: 300px; border: 1px solid #000000;}
.curs {cursor: auto; color: #CC0000;}
</style>
<script>
addEventListener("load", function()

192
{
let t=document.getElementsByTagName("P");

for(let k=0; k<t.length; k++)


t[k].addEventListener("click", but);
});
function but(ev)
{
let a=parseFloat(ev.target.parentElement.id.substr(2))-1;
document.getElementsByTagName("IMG")[a].src=ev.target.href;
for(let n=0; n<this.children.length; n++)
{
let b=this.children[n];
b.className=(b!=ev.target)?"":"curs";
}
ev.preventDefault();
}
</script>
</head>
<body>
<div class="bas">
<table>
<tr>
<td><img src="img/p1.png" alt="Фото">
<p id="bu1">
<a href="img/p1.png" class="curs">1</a>
<a href="img/p2.png">2</a>
<a href="img/p3.png">3</a>
<a href="img/p4.png">4</a>
<a href="img/p5.png">5</a>
<a href="img/p6.png">6</a>
</p></td>
<td><img src="pict/p1.jpg" alt="Фото">
<p id="bu2">
<a href="pict/p1.jpg" class="curs">1</a>
<a href="pict/p2.jpg">2</a>
<a href="pict/p3.jpg">3</a>
<a href="pict/p4.jpg">4</a>
</p></td>
<td><img src="canv/p1.jpg" alt="Фото">
<p id="bu3">
<a href="canv/p1.jpg" class="curs">1</a>
<a href="canv/p2.jpg">2</a>
<a href="canv/p3.jpg">3</a>
<a href="canv/p4.jpg">4</a>
<a href="canv/p5.jpg">5</a>
</p></td>
</tr>
</table>
</div>
</body>
</html>

193
Обратите внимание: мы написали программу, которая совершенно не за-
висит от количества галерей на странице. У нас их три. А могло быть две, или
пять, или любое другое количество. Сколько бы фотогалерей мы ни добавили,
сценарий останется неизменным. Данную программу легко применить в своих
разработках. Главное, чтобы контейнеры p для фото имели в id латинские бук-
вы, bu и порядковый номер, а таблица — необходимое количество ячеек.

194
7. Подведем итоги

Любопытные тенденции наблюдаются среди нынешних специалистов: им


очень хочется, чтобы уровень входа в профессию web-разработчика был как
можно выше. Что это значит? Чем труднее начинающему освоить тот или иной
язык программирования, тем выше уровень входа в среду программистов дан-
ного языка. Что очень выгодно «действующим лицам»: чем сложнее начинаю-
щим, тем меньшее число из них станет профессионалами. А это означает низ-
кий уровень конкуренции, гарантии сохранения рабочего места и высокую зар-
плату тем, кто уже «при деле».
Однако среда web-разработки тем и привлекательна, что у нее низкий
уровень входа. Действительно, чтобы создать web-страницу, достаточно при-
митивного редактора «Блокнот». В нем вы можете написать HTML-код, табли-
цу стилей и сценарий на JavaScript. Не нужно устанавливать компилятор или
интерпретатор. Все необходимое для запуска такой страницы содержится в
«недрах» любого современного браузера. Да и язык JavaScript прост в изучении
и доступен любому, кто хочет его освоить.
В основных поисковых системах типа Яндекс или Google очень популя-
рен запрос «скачать JavaScript». Многие из интересующихся программировани-
ем даже не подозревают, что ничего скачивать не надо — JavaScript уже есть на
вашем компьютере. Обработчик кода этого языка изначально встроен в ваш
браузер, какой бы «марки» он у вас ни был. Поэтому разработка страниц сай-
та — один из самых интересных и легких процессов в программировании.
Конечно, в идеале было бы хорошо создать на своем ПК полноценную
среду разработки, как у профессионалов, а не пользоваться «Блокнотом». Сде-
лать это совсем не трудно, в чем вы могли убедиться, прочитав главу 1. Наде-
юсь, что отныне такая среда разработки есть и на вашем ПК.
Верстку шаблонов страниц, внедрение в документ таблиц стилей и сцена-
риев на JavaScript мы освоили в главе 3.
С особенностями, нюансами и тонкостями процесса объявления перемен-
ных, регистрации обработчиков, написания функций вы познакомились в гла-
ве 4. Думаю, это была информация, которую редко можно найти в других кни-
гах по программированию на JavaScript.
Глава 5 поведала вам о том, как оптимизировать, проверять и настраивать
программы. Следуя рекомендациям, приведенным в этой главе, вы научились
создавать сценарии, полностью соответствующие стандартам языка и не со-
держащие ни единой ошибки.

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

196
8. Об авторе

Валерий Викторович Янцев


Окончил Московский институт радиотехники, электроники и автоматики
по специальности «Инженер электронной техники».
Автор одного изобретения.
Опубликовал около 80 научно-популярных и технических статей по элек-
тронике.
Программированием занимается с 2003 г.
Владеет HTML, CSS, JavaScript, PHP, Perl, C++.
Работал в нескольких студиях web-дизайна.
В качестве фрилансера создал около 50 сайтов для различных компаний, в
одиночку выполнил несколько крупных коммерческих проектов.
В последние годы приоритетным для него является написание сценариев
на JavaScript.

197
Валерий Викторович ЯНЦЕВ
JAVASCRIPT
КАК ПИСАТЬ ПРОГРАММЫ
Учебное пособие

Зав. редакцией
литературы по информационным технологиям
и системам связи О. Е. Гайнутдинова
Ответственный редактор Н. А. Кривилёва
Корректор Л. Ю. Киреева
Выпускающий В. А. Плотникова

ЛР № 065466 от 21.10.97
Гигиенический сертификат 78.01.10.953.П.1028
от 14.04.2016 г., выдан ЦГСЭН в СПб
Издательство «ЛАНЬ»
[email protected]; www.lanbook.com
196105, СанктПетербург, пр. Юрия Гагарина, д.1, лит. А.
Тел.: (812) 3362509, 4129272.
Бесплатный звонок по России: 88007004071

Подписано в печать 29.10.21.


Бумага офсетная. Гарнитура Школьная. Формат 70×100 1/16.
Печать офсетная/цифровая. Усл. п. л. 16,25. Тираж 30 экз.
Заказ № 128821.
Отпечатано в полном соответствии
с качеством предоставленного оригиналмакета
в АО «Т8 Издательские Технологии»
109316, г. Москва, Волгоградский пр., д. 42, к. 5.

Powered by TCPDF (www.tcpdf.org)

Вам также может понравиться