JavaScript. Как Писать Программы
JavaScript. Как Писать Программы
ЯНЦЕВ
JAVASCRIPT
КАК ПИСАТЬ
ПРОГРАММЫ
Учебное пособие
•САНКТПЕТЕРБУРГ•МОСКВА•КРАСНОДАР•
•2022•
УДК 004
ББК 32.973я73
ISBN 9785811485598
УДК 004
ББК 32.973я73
Обложка
П. И. ПОЛЯКОВА
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. Введение
5
нако осталось еще много тем, о которых хотелось бы поведать читателям. Ду-
маю, что некоторые аспекты программирования на JavaScript будут интересны
не только начинающим, но и опытным разработчикам.
Сомнительные практики
Не спешите завершать свое образование в программировании на
JavaScript прочтением 2–3 книг. Язык этот предоставляет разработчику
настолько богатые и разнообразные возможности, что изучать их можно
очень долго, а совершенствовать свои знания всегда полезно, особенно,
если вы хотите стать настоящим профессионалом.
6
в чем различия между глобальными и локальными переменными;
какой вариант объявления переменных лучше выбрать и чем эти вари-
анты отличаются;
что лучше — больше переменных или, наоборот, меньше;
массивы, операторы, условия, циклы — их особенности;
какие бывают функции и как передавать в них параметры;
о пользе технологии Ajax;
как помочь клиенту правильно заполнить форму;
в чем польза регулярных выражений;
о способах и приемах добавления комментариев к вашему коду.
Глава 5 посвящена «шлифовке» сценариев. Мы последовательно раз-
берем:
1) что такое оптимизация кода. На конкретных примерах увидим, какими
способами можно реализовать этот процесс;
2) что такое валидаторы, какими они бывают и как помогают корректиро-
вать разметку, таблицы стилей и сценарии, чтобы «удержать» их в рамках стан-
дартов;
3) как и в каких браузерах проверять работоспособность ваших программ;
4) логические ошибки (как они возникают и как их находить). Продемон-
стрируем данный процесс на конкретном примере, специально написанном для
этой книги.
Глава 6 содержит примеры законченных программ для работы с изобра-
жениями. То есть, мы используем полученные знания на практике.
7
Кроме того, сценарии имеют особое типографское оформление в соответ-
ствии с их размещением в тексте.
Если сценарий выделен в отдельный блок, он оформлен моноширинным
шрифтом, например так:
let i=0;
function func()
{
i++;
alert ("Количество кликов: "+i);
}
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 пробела
9
function func()
{ // Скобка ниже имени функции
...
}
1.5. Благодарности
10
2. Программное обеспечение
Сомнительные практики
Первая ошибка, которую допускают многие начинающие програм-
мисты, состоит в попытках создать локальный хостинг, следуя описани-
ям из Интернета, где предлагаются «навороченные» методы. Их авторы
выделяют на диске C отдельные секторы, создают кучу дополнительных
директорий, вносят большое количество изменений в конфигурационные
файлы. В результате неопытный человек начинает путаться в этих слож-
ных описаниях, ошибаться в настройках программного обеспечения и
лишь путем многочисленных проб и консультаций с энной попытки
наконец получает требуемый результат (а иногда так и не получает, из-за
чего начинает искать другие описания на данную тему). Между тем со-
здание хостинга на своем ПК — дело несложное.
11
Обратите внимание: все процедуры описаны для компьютера с опе-
рационной системой Windows 10.
Итак, приступим к созданию локального хостинга. А начнем мы с уста-
новки сервера Apache.
12
Рис. 2.1.2. Разрядность или тип операционной системы
13
Рис. 2.1.4. Копируем папку Apache24 на диск C
14
Теперь надо инсталлировать, а затем запустить сервер. Для этого в окне
командной строки сразу после
C:\WINDOWS\System32>
наберите
C:\Apache24\bin\httpd.exe -k install
Должно получиться
C:\WINDOWS\System32>C:\Apache24\bin\httpd.exe -k install
15
Осталось добавить, что файлы ваших проектов необходимо помещать в
папку htdocs по адресу C:\Apache24\htdocs, а просматривать готовые страницы
сайтов в браузере по адресу http://localhost/ или http://localhost/
имя_страницы.html, например http://localhost/primer.html или http://
localhost/test/primer.html.
16
Рис. 2.2.2. Перемещаем папку php на диск C
и добавьте к ней
index.php
Должно получиться
DirectoryIndex index.html index.php
17
<?php
echo "Good !";
Сохраните этот файл под именем, например ph1.php. Откройте ваш брау-
зер и введите в строке адреса http://localhost/ph1.php. Если появилось сообще-
ние «Good !», значит, все в порядке — PHP работает (рис. 2.2.4).
18
Рис. 2.3.1. Сайт с файлами интерпретатора Python
19
Рис. 2.3.3. Здесь просто жмем «Next»
Рис. 2.3.4. Ставим «галочку» напротив пункта «Install for all users»,
меняем имя папки, в которую будет установлен интерпретатор,
и начинаем инсталляцию
20
Рис. 2.3.5. Процесс установки файлов интерпретатора Python
21
Рис. 2.3.7. Файл httpd.conf
и добавьте к ней
index.py
Должно получиться
DirectoryIndex index.html index.py
Должно получиться
Options Indexes FollowSymLinks ExecCGI
#! C:/Python/python
print ("Content-type: text/html\n\n")
print ("YES !")
Сохраните этот файл под именем py1.py. Откройте ваш браузер и введите
в строке адреса http://localhost/py1.py. Если появилось сообщение «YES !»,
значит, все в порядке (рис. 2.3.8).
#! C:/Python/python
23
Рис. 2.4.1. Сайт с файлами интерпретатора Perl
24
Рис. 2.4.3. Соглашаемся с условиями лицензии
25
Рис. 2.4.5. Начинаем инсталляцию
26
Снимите галочку в строке «Read README file.» и нажмите «Finish» (рис.
2.4.7). Установка завершена.
27
В папке C:\Apache24\conf с помощью текстового редактора «Блокнот»
откройте файл httpd.conf (рис. 2.4.8), найдите в нем строку
DirectoryIndex index.html
и добавьте к ней
index.pl
Должно получиться
DirectoryIndex index.html index.pl
ExecCGI
Должно получиться
.pl
Должно получиться
AddHandler cgi-script .cgi .pl
#! 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
29
Рис. 2.5.2. Соглашаемся с условиями лицензии
30
Рис. 2.5.4. В этом окне оставляем все без изменения
31
Рис. 2.5.6. Завершение установки
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
Должно получиться
ExecCGI
Должно получиться
Должно получиться
AddHandler cgi-script .cgi .rb
#! C:/Ruby/bin/ruby
puts "Content-type: text/html\n\n"
puts "OK !"
33
Сохраните этот файл под именем ru1.rb. Откройте ваш браузер и введите
в строке адреса http://localhost/ru1.rb. Если появилось сообщение «OK !», зна-
чит, все в порядке (рис. 2.5.8).
#! C:/Ruby/bin/ruby
34
35
ботает во всех основных операционных системах, в том числе Windows. Для
этой ОС существуют автоматические установщики. Необходимо отметить, что
последние версии СУБД доступны только для 64-разрядной ОС Windows, а для
32-разрядной можно скачать версии не выше 10.16. Сайт БД — https://
www.postgresql.org/ (рис. 2.6.2).
36
3. СУБД SQLite. Это библиотека на языке C, которая реализует пол-
нофункциональный механизм базы данных SQL. Файлы, созданные SQLite, яв-
ляются кроссплатформенными и обратно совместимыми. При этом разработчи-
ки обещают сохранить формат файлов в таком виде до 2050 г. Также разработ-
чики утверждают, что их СУБД — самая часто используемая в мире. Впрочем,
это заявление расходится со многими исследованиями по определению попу-
лярности баз данных. Сайт БД — https://www.sqlite.org/ (рис. 2.6.3).
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++
38
Рис. 2.7.3. Нажимаем кнопку «Далее»
39
Рис. 2.7.5. Выбираем папку для установки программы
40
На последнем этапе, непосредственно перед началом установки, на вклад-
ке появится пункт «Create Shortcut on Desktop» (рис. 2.7.7). То есть нам предла-
гают создать ярлык программы на рабочем столе. Делать это или нет — зависит
от вашего желания. Замечу, что после установки редактора ссылка на него до-
бавится в контекстное меню. Достаточно будет навести указатель мыши на
файл, щелкнуть правой клавишей и в списке возможных действий вы увидите
строку «Edit with Notepad++». Кликните по ней — и файл будет открыт в редак-
торе.
Разобравшись с вопросом, необходим ли ярлык на рабочем столе или нет,
нажмите кнопку «Установить». Дождитесь окончания процесса (рис. 2.7.8).
После завершения установки вам нужно будет принять еще одно решение:
сразу запустить программу или отложить это дело на потом. Для этого оставьте
или снимите «галочку» в пункте «Запустить Notepad++» (рис. 2.7.9). Теперь
нажмите кнопку «Готово». Поздравляю — отныне на вашем компьютере уста-
новлен профессиональный текстовый редактор!
Познакомимся с ним поближе. Как выглядит редактор, вы можете видеть
на рисунке 2.7.10.
Notepad++ позволяет:
выполнять поиск по файлу в разных направлениях;
производить замену по шаблону;
41
Рис. 2.7.8. Дождитесь окончания процесса
42
Рис. 2.7.10. Редактор Notepad++ с открытым в нем файлом
43
Скачивать лучше вариант «System Installer» (рис. 2.7.11). Эта версия про-
граммы позволяет установить ее для всех пользователей вашего компьютера.
Выбирайте файл для скачивания в соответствии с разрядностью вашей опера-
ционной системы. О том, как ее определить, мы говорили в разделе 2.1 данной
главы.
Рис. 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).
45
Рис. 2.7.14. Редактора Atom с введенным кодом
Sublime Text
Страница редактора — https://www.sublimetext.com/3.
Скачивая программу (рис. 2.7.15), выбирайте файл в соответствии с разрядно-
стью вашей операционной системы. О том, как ее определить, рассказано в раз-
деле 2.1 данной главы.
Рис. 2.7.15. Страница для выбора файла загрузки редактора Sublime Text
46
Процесс инсталляции довольно несложный. Можно оставить все настрой-
ки, предложенные установщиком, без изменений.
Как выглядит окно редактора, показано на рисунке 2.7.16.
47
3. Подготовительные работы
48
3.2. Шаблон страницы
<!DOCTYPE html>
<html>
<html lang="ru">
<head>
...
</head>
<meta charset="utf-8">
<head>
...
<meta name="description" content="описание страницы">
<meta name="keywords" content="ключевые слова">
</head>
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>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="description" content="описание страницы">
<meta name="keywords" content="ключевые слова">
<title>Заголовок страницы</title>
</head>
<body>
</body>
</html>
50
3.3. Добавляем элементы
<div>
элемент 1
элемент 2
элемент 3
и так далее
</div>
<div id=”d1”>
элемент 1
элемент 2
</div>
<div id=”d2”>
элемент 3
элемент 4
элемент 5
</div>
<div id=”d3”>
элемент 6
элемент 7
</div>
и так далее
51
<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.3 показано, как меняется структура доку-
мента, созданного на основе блоков <div> </div> при просмотре страницы в
браузере компьютера и в браузере мобильного устройства. Если на большом
экране блоки располагаются горизонтально, то на маленьком они перестроятся
53
в вертикальную последовательность. Но такой результат невозможно получить,
если применять табличную компоновку документа.
Наконец, есть еще один способ последовательного расположения элемен-
тов, рассчитанный на самый простой случай:
<body>
элемент 1
элемент 2
элемент 3
и так далее
</body>
54
Сомнительные практики
Мы не рассматриваем ситуации, когда стили указываются непо-
средственно в элементе разметки, например, так: <button style="...">.
Считается хорошим тоном отделять стили от разметки, а показанный ва-
риант не соответствует этому принципу.
<head>
...
<style>
</style>
</head>
<style>
body {background: #FFFFFF; color: #000000;}
a {text-decoration: none; color: #0000CC;}
...
</style>
где css.css — адрес файла с таблицей стилей. Обратите внимание: любой внеш-
ний файл с таблицей стилей должен иметь расширение .css. Естественно, что
внутри такого файла теги <style> </style> не указываются — описание стилей
начинается прямо с первой строки.
Второй способ — с применением элемента разметки link. В этом случае
файл таблицы стилей привязывается к документу внутри блока head так:
55
Какой вариант избрать — с директивой @import или элементом link —
решать вам. Замечу, что второй вариант применяется разработчиками гораздо
чаще, чем первый.
<head>
...
<script>
</script>
</head>
<script>
window. addEventListener("load", function()
{
let w=screen.width;
let h=screen.height;
document.getElementById("si").innerHTML=w+" x "+h;
});
</script>
<script src="js.js"></script>
56
4. Некоторые особенности программирования
4.1. События
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
не всплывающее и не отменяемое.
Разработчик сайта заранее выбирает, какие события на странице должны
вызвать программный отклик, а какие — нет.
<head>
...
...
...
<script>
регистрация обработчика, например, для клика на изображении
59
<head>
...
...
...
<script>
регистрация обработчика события load уровня окна
{
регистрация обработчика для клика на изображении
}
При таком подходе браузер сначала загрузит весь документ, следом запу-
стит обработчик события 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()">. Считается хорошим тоном отделять про-
граммный код от разметки, а размещение вызова функции внутри тега
элемента не соответствует этому принципу.
window.addEventListener("load", start);
window.addEventListener("load", func);
или так
window.addEventListener("load", start);
window.addEventListener("load", function()
{
...
});
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
<!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);
});
<p id="te">ТЕКСТ</p>
<script>
document.getElementById("te").addEventListener("click", func,
{once: true});
let i=0;
function func()
{
i++;
64
alert ("Количество кликов: "+i);
}
</script>
<p id="te">ТЕКСТ</p>
<script>
document.getElementById("te").addEventListener("click", func);
// Код сценария
...
...
...
// Если выполняется заданное условие, обработчик удаляется
if(условие)
document.getElementById("te"). removeEventListener("click", func);
</script>
document.getElementById("te").addEventListener("click", func);
...
document.getElementById("te"). removeEventListener("click", func);
<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>
<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>
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>
67
let i=1;
или так
let i="ТЕКСТ";
или так
let i;
или
let i="Строка";
i=1;
<script>
let a="Строка";
let b=typeof a;
alert(b);
</script>
68
<script>
let i=1;
let t=0;
код сценария
</script>
function func()
{
let a=1;
...
}
<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>
71
Рис. 4.5.2. Результат выполнения сценария с переменными,
объявленными оператором let
<script>
let i=1;
if(true)
{
let i=2;
}
alert(i);
</script>
const v=document.getElementById("im").style;
v.width="100px";
v.opacity="0.5";
v.border="2px solid #0000CC";
v.width="100px";
v.opacity="0.5";
v.border="2px solid #0000CC";
}
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";
}
74
Если в программе происходит только однократное обращение к элементу,
проще переписать данный пример так:
document.getElementById("im1").style.width="100px";
document.getElementById("im2").style.width="200px";
4.7. Массивы
mas[n+1]=элемент n+1;
76
Продолжение табл. 4.7.1
Метод Описание
Удаляет последний элемент массива и возвращает
pop()
этот элемент
Добавляет новые элементы в конец массива и возвращает
push()
его новую длину
reverse() Выполняет пересортировку массива в обратном порядке
Удаляет элемент массива с индексом 0 и возвращает
shift()
этот элемент
Производит сортировку элементов массива по заданной
sort()
функции сравнения
Удаляет часть массива, а на его место добавляет новые
splice()
элементы
unshift() Добавляет элементы в начало массива
<!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="";
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;
и сам массив
addEventListener("load", race);
function race()
{
...
setTimeout(race, 80);
}
document.getElementById("loa").innerHTML=t;
let t="";
78
Ее заполнение производится в цикле
for(let a=0; a<=d[i]; a++)
t+="<div class='tim'></div>";
79
Поскольку значения элементов массива сначала идут по возрастающей, а
достигнув числа 15, начинают убывать, индикатор выглядит как непрерывно
бегущие от минимума к максимуму и обратно штрихи (рис. 4.7.1).
Как видите, массив оказался полезен даже в таком необычном качестве.
4.8. Операторы
let z=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>
<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
82
В этом примере диалоговое окно с сообщением о типе переменной не по-
явится, так как условие строгого равенства не выполнено.
Пример 4 (файл 4.8_7.html в папке «Глава4» zip-архива):
<script>
let a=0;
if(a===0)
alert(typeof a);
</script>
if(условие)
{
инструкции
}
83
Здесь инструкции будут выполняться в случае истинности условия.
Если необходимо предусмотреть альтернативные инструкции на случай
ложности условия, то используют следующую форму записи:
if(условие)
{
инструкции 1
}
else
{
инструкции 2
}
84
и даже так:
if(t==1)
{
инструкции 1
}
else if(t==2)
{
инструкции 2
}
else if(t==3)
{
инструкции 3
}
switch (выражение)
{
case значение 1:
инструкции 1
break;
case значение 2:
инструкции 2
break;
...
case значение n:
инструкции n
break;
default:
инструкции на случай отсутствия совпадений
}
switch (t)
{
case 1:
инструкции 1
break;
case 2:
инструкции 2
break;
case 3:
инструкции 3
}
85
switch (t)
{
case 1:
инструкции 1
break;
case 2:
case 3:
инструкции 2
}
или
if(условие)
инструкция 1
else
инструкция 2
или
if(условие 1)
инструкция 1
else if(условие 2)
инструкция 2
...
else if(условие n)
инструкция n
else
инструкция на случай ложности всех условий
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>
document.getElementById("bu1").addEventListener("click", but);
document.getElementById("bu2").addEventListener("click", but);
let t=parseFloat(ev.target.id.substr(2));
if(b(t))
function b(a)
{
If(a==1)
return true;
if(a==2)
return false;
}
87
с полученным числом в качестве аргумента:
b(t)
88
Кроме того, при необходимости легко применить множественные условия
(файл 4.10_2.html в папке «Глава4» zip-архива):
while(условие)
{
инструкции
}
let a=1;
while (a<7)
{
let b="im"+a;
document.getElementById(b).style.width=500+"px";
a++;
}
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-архива):
let a=1;
let b=function()
{
a++;
}
function but()
{
for(a=1; a<7; b())
alert(a);
}
</script>
Если нажать кнопку «Пуск» шесть раз подряд, откроются диалоговые ок-
на, поочередно демонстрирующие цифры от 1 до 6.
Этим процессом управляет функция but
function but()
{
...
}
let b=function()
{
a++;
}
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>
91
Первая инструкция функции определяет id нажатой кнопки и помещает
его значение в глобальную переменную c:
c=ev.target.id;
let b=function()
{
if(c=="bup")
a++;
if(c=="bum")
a--;
};
if(a==7)
a=6;
if(a==0)
a=1;
4.11. Функции
92
Например, по назначению их можно разделить на два основных вида.
Первый — функции, выполняющие действия, связанные с изменением со-
стояния и параметров различных элементов документа. Пример:
function func()
{
document.getElementById("flo").style.width="200px";
document.getElementById("photo").src="im.jpg";
}
let a=0;
function func()
{
a++;
}
// Описание функции
function func()
{
...
}
// Вызов функции
func();
или
let y=(g)=>
{
h++;
let f=g+" - переданное число<br>"+h+" - количество кликов";
return f;
};
<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>
let k=()=>25**3;
document.getElementById("p1").addEventListener("click", function()
{
document.getElementById("p2").innerHTML=k();
});
let x=1;
let t=0;
func(5);
function func(i)
{
t+=x**2;
x++;
if(x<=i)
func(i);
}
if(x<=i)
func(i);
95
Результаты вычислений можно вывести на страницу:
document.getElementById("kv").insertAdjacentHTML("beforeend", t);
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;
}
}
func(15);
function func(i)
{
// инструкции, выполняемые в зависимости от значения аргумента i
}
96
2. Использовать для передачи данных значение глобальной переменной:
let a=15;
function func()
{
// инструкции, выполняемые в зависимости от значения переменной a
}
ev.target.id
let p=ev.target.id.substr(1);
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)
{
инструкции
}
}
}
В этом примере кроме кода JavaScript у нас есть еще текстовая строка, ко-
торая нужна для полноценной демонстрации работы функции.
Сразу после загрузки страницы функция получает в качестве аргумента
число, которое выводится в диалоговом окне. Если после его закрытия щелк-
нуть мышью на текст, то откроется новое окно с сообщением, что событие про-
изошло на элементе, id значение которого — p1.
Раз в функцию можно передавать какие-то параметры, значит, можно и
получать результаты работы функции. Покажем два способа.
1. Прямое указание в теле функции возвращаемого значения. Для этого
используется оператор return. Выше мы говорили о стрелочных функциях и
проиллюстрировали рассказ таким примером:
let y=(g)=>
{
h++;
let f=g+" - переданное число<br>"+h+" - количество кликов";
return f;
};
98
Как видите, здесь прямо указано, что функция возвращает значение
return f;
let a=0;
function func()
{
// операции с переменной a
}
99
И только создание технологии Ajax позволило делать сайты, где инфор-
мация добавлялась на страницу без лишних выкрутасов и очень быстро. А учи-
тывая, что современные посетители сайтов любят, когда все происходит без
лишних задержек, автор советует web-программистам использовать Ajax при
любой возможности.
Рассмотрим некоторые варианты применения Ajax в web-разработках.
Один из наиболее типичных примеров — отправка клиентом заполненной
формы. В этом случае результаты проверки данных отображаются прямо на те-
кущей странице. Нажал кнопку «Отправить» — и тут же получил ответ «Ваша
заявка зарегистрирована» или «Вы указали некорректные сведения».
Рассмотрим конкретную программу. Пусть у нас имеется форма с не-
сколькими полями, в том числе и для загрузки файлов. Присвоим форме имя
dat (файл 4.12_1.html в папке «Глава4» zip-архива):
<div id="att"></div>
window.addEventListener("load", function()
{
document.getElementById("bu").addEventListener("click", 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);
}
100
re.addEventListener("load", show);
function show()
{
let ans=this.responseText;
if(ans==1)
document.getElementById("att").innerHTML="СООБЩЕНИЕ ДОСТАВЛЕНО";
else
document.getElementById("att").innerHTML="НЕКОРРЕКТНЫЕ ДАННЫЕ";
}
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";
}
function show()
{
let ans=this.responseText;
if(ans==1)
document.getElementById("bu").value="СООБЩЕНИЕ ДОСТАВЛЕНО";
else
document.getElementById("bu").value="НЕКОРРЕКТНЫЕ ДАННЫЕ";
}
102
Рис. 4.12.1. Варианты вывода предупреждений от сервера:
исходный вид формы; сообщение в строке ниже формы;
сообщение на специально оформленном слое;
сообщение на кнопке отправки данных
103
Сначала мы проверяем, не достиг ли ползунок конца документа:
if(window.pageYOffset+window.innerHeight>=document.body.
clientHeight)
function demo()
{
document.getElementById("pic").insertAdjacentHTML("beforeend",
this.responseText);
}
insertAdjacentHTML("beforeend", this.responseText);
<div id="pic">
...
</div>
function demo()
{
document.getElementById("pic").innerHTML=this.responseText;
}
104
Наконец, завершая рассказ, хочу отметить, что с помощью технологии
Ajax можно не только взаимодействовать с серверными программами, но и
просто загружать файлы, например текстовые. Сделать это можно так (файл
4.12_6.html в папке «Глава4» zip-архива):
re.open("GET", "text.txt", true);
или
document.getElementById("pic").innerHTML=this.responseText;
105
const a=document.getElementById("att");
a.innerHTML="Осталось: "+t;
if(t<=20)
a.style.color="#CC0000";
else
a.style.color="#006600";
}
<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);
});
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("");
}
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>
Знак + означает, что символов может быть более одного. Наконец, завер-
шающий символ — буква или цифра:
[a-z\d]
109
Имя домена второго уровня отделяется точкой от имени домена первого
уровня:
\.ru
/^...$/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("");
}
rv.test(na.value)==false
110
Хочу обратить внимание, что, хотя описанный выше сценарий довольно
неплохо проверяет адреса электронной почты, но это все-таки упрощенный ва-
риант. Сделан он таковым для «облегчения» примера. В идеале необходимо до-
бавить проверку:
длины адреса — а она, как вы знаете, ограничена;
наличие таких нарушений, как два дефиса или две точки подряд (а так-
же дефиса и точки рядом в любом порядке).
Пойдем дальше. Применение регулярных выражений, конечно, не огра-
ничено только проверкой почты. На самом деле вариантов использования регу-
лярных выражений очень много. Рассмотрим еще один пример — подсвечива-
ние ссылок.
Допустим, на нашу страницу загружаются данные из файла, где в виде
простого текста есть адрес ссылки, не «обрамленный» соответствующими те-
гами a. Наша задача «подсветить» ссылку, так чтобы она стала рабочей, веду-
щей на конкретную страницу некоего сайта.
Задача эта не совсем простая — и вот по какой причине. Хорошо, если
ссылка отделена пробелами от других слов или знаков загружаемого текста.
Тогда «подсветить» ее довольно просто. Но она может быть расположена в
конце предложения, допустим, вот так: «Вы были на сайте https://testjs.ru?».
Или так: «Есть много ресурсов с примерами кода (один из них — сайт
https://testjs.ru)». Более того, по ошибке ссылка может вообще не иметь пробе-
лов между соседними словами и выглядеть так: «В тексте есть вот та-
каяhttps://testjs.ruссылка». Подобное нередко случается при наборе текста со
смартфона. Как в таком случае отделить ссылку?
Для этого существуют разные способы. Я хочу продемонстрировать вам
довольно оригинальный вариант, в котором проверяется не сама ссылка, а зна-
ки в начале и в конце нее. Сценарий такой проверки содержит всего одно ко-
роткое регулярное выражение (файл link.html zip-архива):
/[а-яА-Я\(\),!;\.:\?]/
split('https://')
/[а-яА-Я\(\),!;\.:\?]/
112
ошибку. Однако в качестве заготовки для серьезной программы он вполне под-
ходит.
Кстати, если вы хотите расширить рамки действия сценария, то можете
добавить в него массив существующих доменных имен первого уровня и срав-
нивать его элементы с доменным именем из ссылки.
Поскольку сценарий довольно объемный, я не стал приводить его код. Вы
можете посмотреть действующую программу здесь: https://testjs.ru/kpp
/link.html. Зайдите на страницу. В исходном состоянии вы увидите текстовое
поле с надписью, содержащей ссылку, «застрявшую» в словах без пробелов.
Кликните мышью по текстовому полю — и увидите, что ссылка будет опреде-
лена правильно (рис. 4.14.2). Чтобы посмотреть код сценария, щелкните правой
кнопкой мыши на любом свободном участке страницы с примером и в открыв-
шемся меню выберите пункт «Посмотреть исходный код» (Microsoft Edge), или
«Просмотр кода страницы» (Google Chrome), или «Исходный код страницы»
(Opera и Mozilla Firefox), или «Посмотреть код страницы» (Яндекс.Браузер).
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));
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>
4.16. Комментарии
В языке JavaScript существуют два способа размещать комментарии непо-
средственно в теле сценария. Однострочный комментарий выделяется двумя
наклонными чертами — слешами:
// Это комментарий
115
Например:
function func()
{
// Объявляем счетчик кликов на изображении
let d=0;
...
}
/* Это комментарий,
/* который состоит
/* из нескольких строк */
Например:
function func(t)
{
let m=0;
function func(t)
{
let m=0;
for(let d=0; d<5; d++)
{
if(t<d)
{
m++;
}
}
}
117
118
5. Тестирование сценариев
Сомнительные практики
Очень придирчиво относитесь к выбору популярных платформ,
движков и CMS для создания сайтов. Да, такие системы очень облегчают
труд дизайнера и верстальщика. Но почти любая из них содержит в сво-
ем «ядре» многочисленные ошибки, которые потом перекочевывают в
ваши разработки. Если вам необходимо выполнить крупный проект —
смиритесь с неизбежным: без платформы, движка или CMS, скорее все-
го, не обойтись. Если проект небольшой и не нуждается в системе
управления контентом, попробуйте самостоятельно написать его «от
корки до корки». Так вы можете создать ресурс с абсолютно чистым ко-
дом. Есть еще один вариант действий: выберите CMS, которая вам нра-
вится больше всего, скачайте дистрибутив на свой компьютер и попро-
буйте исправить его исходный код. Работа большая и трудоемкая, зато у
вас появится исправленный экземпляр CMS, на копиях которого вы в
дальнейшем станете делать «чистые» сайты.
119
Надеюсь, вы не хотите оказаться в рядах горе-программистов? Тогда про-
веряйте разметку, стили и сценарии ваших проектов самым тщательным обра-
зом. Вот перечень самых необходимых действий:
оптимизация кода;
проверка кода с помощью валидаторов и устранение ошибок;
проверка работоспособности сценариев в различных браузерах, в том
числе достижение идентичности результатов работы сценариев в различных
браузерах;
устранение логических ошибок (если они обнаружены в процессе тести-
рования) в скриптах.
Рассмотрим этот перечень подробнее.
a=a+1;
А можно иначе:
a++;
let i;
for(i=0; i<5; i++)
{
...
}
120
То есть сначала объявляем переменную i, а потом в описании условий
цикла присваиваем ей начальное значение.
Ясно, что следующий вариант данного цикла более краток:
for(let i=0; i<5; i++)
{
...
}
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++)
{
...
}
...
}
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;
можно написать
<!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>
<body>
<div id="di"></div>
</body>
</html>
t+="</table>";
document.getElementById("di").innerHTML="Квадраты чисел"+t+
"</table>";
125
Рис. 5.2.2. Валидатор CSS3
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).
127
ные и т. д. Понятно, что задача хорошего программиста — исправить все ошиб-
ки и получить идеально чистый и правильный код.
Вообще код, полностью отвечающий всем стандартам — очень большая
редкость на просторах Интернета. Даже такие монстры, как Яндекс, Google и
YouTube, имеют множество ошибок в своих страницах. Хотите убедиться —
проверьте.
128
Мне осталось, как и обещал, привести случай некачественной разметки.
Я проверил код случайно выбранного сайта первой попавшейся мне студии
web-дизайна (рис. 5.2.5). Как видите, валидатор Консорциума Всемирной Пау-
тины обнаружил на одной странице ровно 100 нарушений стандартов HTML5.
5.3. Браузеры
129
5. Mozilla Firefox. В этом браузере сценарии необходимо проверять в обя-
зательном порядке. У него достаточно высокий уровень популярности. При
этом есть ряд отличий в обработке кода по сравнению с четырьмя перечислен-
ными выше браузерами. Что-то Mozilla Firefox обрабатывает аналогично
остальным браузерам, а что-то по-своему. Во всяком случае, я неоднократно
сталкивался с ситуациями, когда код, отлично работавший в других браузерах,
начинал «капризничать» в Mozilla Firefox. Скачать браузер можно здесь:
https://www.mozilla.org/ru/firefox/.
Неплохо установить на свой смартфон мобильные версии перечисленных
браузеров. Тогда вы после размещения сайта на удаленном хостинге сможете
проверить, как работают ваши сценарии на мобильных устройствах.
И уж совсем идеальной можно считать проверку, которую, кроме всего
прочего, удалось провести на устройствах Apple с операционными системами
macOS и iOS и браузером Safari.
Думаю, что цель таких масштабных испытаний с использованием группы
разных браузеров понятна: необходимо добиться совершенно одинаковой рабо-
ты ваших программ во всех браузерах. Только получив этот результат, можно
считать свои программы вполне качественными.
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;
}
...
let t=v/d;
...
Потом так:
...
let t=v/d;
alert(t);
if(c<t/2)
...
131
document.getElementById("pict").style.left=n+"px";
}
...
...
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;
}
...
132
Итак, зайдем на первую из них: https://testjs.ru/e_p11.html. После загруз-
ки документа мы видим на странице только рамку. Пока указатель мыши не
попадает внутрь рамки, ничего не происходит.
Теперь медленно проведите курсором внутри рамки слева направо. Вы
обнаружите, что на белом фоне плавно возникнет фотография старинного ав-
томобиля (рис. 5.4.1).
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
<body>
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;
}
}
135
Давайте считать, что сейчас курсор двигается слева направо. Первым де-
лом проверяется условие if(h-d>3). В начальный момент d имеет значение 0,
поэтому разность между h и d удовлетворяет условию. Таким образом, при по-
падании указателя мыши на фото будут выполняться инструкции первого бло-
ка if:
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;
}
if(d-h>3)
i-=0.01;
document.getElementById("car").style.opacity=i;
d=h;
136
Чтобы разобраться в проблеме, давайте используем метод поиска логиче-
ских ошибок, о котором говорилось выше. Так как за показатель непрозрачно-
сти отвечает переменная i, проверим, что с ней происходит «за кадром». Лучше
всего внедрить в страницу элемент визуального отображения значений этой пе-
ременной. Например, вот так:
function opa()
{
let h=event.pageX;
document.getElementById("ind").innerHTML=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>=0)
138
Или такой:
else
{
i=0;
d=0;
}
// document.getElementById("im1").addEventListener("click", func);
Или так:
/* document.getElementById("im1").addEventListener("click",
func); */
139
и так
/* document.getElementById("im1").addEventListener("click", func);
/* document.getElementById("im2").addEventListener("click", func);
/* document.getElementById("im3").addEventListener("click",
/* func); */
5.5. Кстати
140
Рис. 5.5.1. Результат проверки сценария, «проявляющего» фотографию
141
Рис. 5.5.3. Результат проверки таблицы стилей
142
6. Примеры сценариев
Итак, приступим.
Самым первым рассмотрим в действии сценарий, который при выделении
одного рисунка меняет вид остальных.
Запустите в своем браузере страницу https://testjs.ru/kpp/k1.html. Вы
увидите 6 изображений различных автомобилей — современных и ретро (рис.
6.1.1).
Наведите указатель мыши на одну из картинок. Ее вид не изменится.
А вот остальные автомобили плавно «растворятся», да так, что будут еле видны
(рис. 6.1.2). Тем самым мы получили желаемый результат, выделив одно изоб-
ражение на фоне остальных.
143
Рис. 6.1.1. Так выглядит страница после загрузки
<body>
144
</body>
</html>
<div class="bas">
</div>
<style>
</style>
<script>
</script>
addEventListener("mouseover", ...);
145
Для сокращения программы используем в качестве обработчика стрелоч-
ную функцию:
addEventListener("mouseover", (ev)=>
{
...
});
let e=ev.target;
if(e.tagName=="IMG")
if(e.tagName=="IMG")
{
...
}
else
{
...
}
Определим id рисунка:
e.id
if(e.tagName=="IMG")
{
...
}
146
напишем цикл, в котором все картинки, кроме выделенной, будут «мутнеть»:
let b="i"+i;
e.id!=b
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;
}
}
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>
<div class="bas">
</div>
149
Рис. 6.2.1. Страница в исходном состоянии
150
Рис. 6.2.3. Изображение заняло центральное положение,
остальные картинки в это время не видны
Перейдем к сценарию.
Объявим 3 глобальные переменные:
let y=1;
let w=0;
let h=0;
151
addEventListener("click", ...);
addEventListener("click", (ev)=>
{
...
});
let e=ev.target;
if(e.tagName=="IMG")
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
Дальше стартует уже знакомый нам цикл, в котором выполняются опера-
ции «растворения» остальных изображений:
t.zIndex=2;
else
{
...
}
y=1;
153
p.transition="opacity 2s";
p.opacity=1;
}
<!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;
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">
155
6.3. Галерея
Сценарий, который мы сейчас разберем, пожалуй, самый простой.
Зайдите на страницу https://testjs.ru/kpp/k3.html. Вы увидите, что
начальное расположение элементов изменилось. Теперь документ содержит
всего одно, но более крупное изображение автомобиля (рис. 6.3.1). Справа от
него находится указатель-стрелка. Нажмем ее. Изображение поменяется и од-
новременно слева появится еще один указатель (рис. 6.3.2). Продолжаем нажи-
мать правую стрелку до тех пор, пока не загрузится последний, шестой рисунок.
В этот момент правая стрелка исчезнет (рис. 6.3.3).
Как вы уже, наверное, поняли, стрелки-указатели выполняют роль кнопок
перемотки вперед или назад. Когда загружается последний рисунок, выключа-
ется перемотка вперед. После загрузки самого первого рисунка выключается
перемотка назад. Вот такая простая галерея картинок.
156
Рис. 6.3.3. Когда галерея промотана вправо до конца,
видна только левая кнопка
<div class="bas">
</div>
text-align: center;
157
3. Кнопка перемотки вперед:
Их настройки:
addEventListener("load", function()
{
document.getElementById("nz").addEventListener("click", nz);
document.getElementById("vp").addEventListener("click", vp);
});
let i=1;
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++;
159
Теперь вспомним, что при достижении конца галереи кнопка перемотки
вперед будет скрыта. Поскольку сценарий «не знает», произошло данное собы-
тие или нет, лучше на каждом проходе функции выполнять вот такую инструк-
цию:
document.getElementById("vp").style.visibility="visible";
document.getElementById("i1").src="img/p"+i+".png";
if(i==1)
document.getElementById("nz").style.visibility="hidden";
<!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. Слайдер
161
И опять базой для элементов страницы служит контейнер
<div class="bas">
</div>
с настройками стилей
.bas {position: relative; width: 1000px; margin: auto;}
В контейнере 6 рисунков.
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="Фото">
Их положение и оформление:
.im {position: absolute; top: 10px; left: 200px;
width: 600px; border: 1px solid #000000;}
163
Зарегистрируем обработчик нажатия кнопки:
addEventListener("load", function()
{
document.getElementById("bu").addEventListener("click", but);
});
let k=10;
let s=0;
s++;
k=10;
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)
document.getElementById("i"+s).style.top=k+"px";
k-=10;
setTimeout(mov, 10);
if(s==6)
if(s==6)
{
...
}
mov();
165
Тем самым сценарий подготавливается к новому запуску слайд-шоу с са-
мого начала.
Далее объявляем переменную h
let h=document.getElementById("i1").style;
Первый таймер сработает через одну секунду. Второй будет запущен че-
рез одну секунду после первого.
Инструкции первого таймера возвращают первый рисунок в исходное по-
ложение
h.top="10px";
if(h.opacity==1)
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">
</div>
</body>
</html>
<div class="bas">
</div>
168
169
В этом сценарии на начальном этапе все изображения сделаны невиди-
мыми, так как их свойствам непрозрачности присвоено значение 0:
addEventListener("load", function()
{
setTimeout(diss, 2000);
});
function diss()
{
if(s<6)
{
...
}
else
{
...
}
}
Рассмотрим их по порядку.
Перед запуском функции у нас объявляется глобальная переменная s, ко-
торая выполняет роль счетчика изображений:
let s=1;
let a="i"+s;
170
let t=document.getElementById(a).style;
t.transition="opacity 2s";
t.opacity=0;
if(s<6)
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;
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">
<div class="bas">
</div>
с привычными настройками:
173
Контейнер содержит 6 рисунков:
addEventListener("load", function()
{
document.getElementById("bup").addEventListener("click", but);
document.getElementById("bum").addEventListener("click", but);
});
174
Еще до регистрации обработчика мы объявляем две переменные:
let a=1;
let b;
opas();
if(b=="bup")
{
...
}
if(b=="bum")
{
...
}
175
сделает видимой первую картинку. После этого значение счетчика увеличится
на 1:
a++;
if(a<7)
setTimeout(opas, 500);
if(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;
}
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">
</div>
</body>
</html>
Помните, в разделе 4.10 мы говорили о том, что один и тот же цикл мож-
но «заставить» работать в любом направлении: как увеличивать значение счет-
чика, так и уменьшать? Сейчас мы займемся сценарием, в котором проделаем
такие «фокусы» с циклом while.
Страница находится по адресу https://testjs.ru/kpp/k7.html. Если зайти на
нее, то вы обнаружите то же самое, что и в предыдущем примере: две кноп-
ки — «Показать» и «Скрыть». А все потому, что новая программа — это твор-
ческое развитие предыдущей.
В чем недостаток примера из раздела 6.6? В резком появлении изображе-
ний. Давайте избавимся от этого эффекта. Пусть картинки появляются и скры-
ваются более плавно. И, как уже было неоднократно, в этом нам поможет свой-
ство opacity.
Нажмем кнопку «Показать». На странице поочередно и, главное, плавно
возникнут все шесть картинок (рис. 6.7.1). Щелкнем на кнопке «Скрыть». Ав-
томобили также поочередно и плавно «растворятся».
Начинка документа точно такая же, как и предыдущем случае.
Слой-контейнер
<div class="bas">
</div>
с настройками:
178
6 рисунков:
Две кнопки:
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;
a++;
if(a==7)
{
a=6;
break;
}
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;
}
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=1;
b=1;
<!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>
с привычными свойствами:
.bas {position: relative; width: 1000px; margin: auto;
text-align: center;}
184
В центре контейнера — рисунок номер один:
<img id="im" src="img/p1.png" alt="Фото">
с крупными цифрами:
p {font-size: 30px;}
не активна:
let a;
addEventListener("load", function()
{
a=document.getElementById("bu");
a.addEventListener("click", but);
});
function but(ev)
{
let h=ev.target.href;
185
if(h.indexOf("img"))
{
document.getElementById("im").src=h;
ev.preventDefault();
}
let h=ev.target.href;
if(h.indexOf("img"))
{
...
}
document.getElementById("im").src=h;
n<a.children.length
let b=a.children[n];
186
2. Посредством тернарного оператора выясняем, какие ссылки должны
стать активными, а какая ссылка, наоборот, неактивной:
b.className=(b!=ev.target)?"":"curs";
b!=ev.target
ev.preventDefault();
<!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>
188
<div class="bas">
</div>
с настройками:
<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>
addEventListener("load", function()
{
let t=document.getElementsByTagName("P");
for(let k=0; k<t.length; k++)
t[k].addEventListener("click", but);
});
190
А затем для каждого узла регистрируем обработчик, который вызывает
одну и ту же функцию but:
for(let k=0; k<t.length; k++)
t[k].addEventListener("click", but);
Здесь
document.getElementsByTagName("IMG")
191
Как и в предыдущем примере, вычисляем ссылку, которая должна стать
неактивной:
n<this.children.length
let b=this.children[n];
b.className=(b!=ev.target)?"":"curs";
ev.preventDefault();
<!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");
193
Обратите внимание: мы написали программу, которая совершенно не за-
висит от количества галерей на странице. У нас их три. А могло быть две, или
пять, или любое другое количество. Сколько бы фотогалерей мы ни добавили,
сценарий останется неизменным. Данную программу легко применить в своих
разработках. Главное, чтобы контейнеры p для фото имели в id латинские бук-
вы, bu и порядковый номер, а таблица — необходимое количество ячеек.
194
7. Подведем итоги
195
Наконец, в главе 6 мы рассмотрели несколько действующих сценариев,
что полезно с практической точки зрения.
Теперь вы вооружены ценными знаниями и опытом. А это залог ваших
будущих успехов в web-программировании.
196
8. Об авторе
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