Урок 7. JavaScript в веб-разработке

Модели BOM и DOM. Навигация по элементам документа. Доступ к узлам и атрибутам страницы. Изменение структуры DOM. Основные браузерные события.

Урок 7. JavaScript в веб-разработке

Модели BOM и DOM. Навигация по элементам документа. Доступ к узлам и атрибутам страницы. Изменение структуры DOM. Основные браузерные события.
Smartiqa Automation web sm

Оглавление

1
Теоретический блок

1. Браузерное окружение
  1. Объект window
  2. Объект document - модель DOM
  3. Объектная модель браузера - BOM
  4. Подключение скриптов к сайт
2. Навигация по DOM
3. Свойства DOM-узлов
4. Поиск по дереву документ
5. Внесение изменений в DOM-дерево

  1. Создадим текстовый элемент и заголовок первого уровня в начале страницы.
  2. Копирование абзаца и добавление его после секции с заменой текст.
  3. Замена всех тегов на странице заголовками второго уровня.
  4. Добавление текста со специальными символами
6. Управление стилями элементов
  1. Вывод содержимого атрибута «class».
  2. Изменение классов
  3. Изменение атрибутов тегов
  4. Сброс стилей
7. Браузерные события
  1. Обработка события через свойство тэга.
  2. Обработка события через выбор элемента в js-файле.
  3. Обработка события через специальные методы.
  4. Параметр event функции-обработчика.
  5. Этапы события (погружение, достижение цели, всплытие).
  6. Делегирование событий
Перейти
2
Практический блок
1. Вопросы
2. Задачи
3. Решения
Перейти

ТЕОРЕТИЧЕСКИЙ БЛОК

1

Браузерное окружение

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

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

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

Чтобы JavaScript получил возможность запуска (в браузере, операционной системе), необходимо предоставить ему определенную функциональность. Ее называют окружением. Окружение позволяет языку программирования получить доступ к своим объектам, функциям, переменным в дополнение к базовым инструментам JavaScript. В частности, браузер «разрешает» управлять web-страницами. Схема браузерного окружения приведена ниже:
Объекты браузерного окружения, доступные языку JavaScript
Объекты браузерного окружения, доступные языку JavaScript
Помимо возможностей самого языка, мы получаем доступ к ряду объектов:
  1. Объект window (корневой элемент, представляющий собой окно браузера и включающий методы управления им);
  2. Объект DOM (Document object model, объектная модель документа) – совокупность всех сущностей веб-страницы в виде дерева;
  3. Объект BOM (Browser object model, объектная модель браузера) – дополнительные инструменты для непосредственной работы с просмотрщиком.

1.1. Объект window

Позволяет обращаться к переменным и функциям в любом месте программы. Ключевое слово window можно опускать.
Пример – Консоль браузера

> window.prompt('Готовы к JS?')
< да
> prompt('Готовы к JS?')
< null
Еcли в Google Chrome открыть Панель Инструментов разработчика и выбрать вкладку Console, можно писать js-код и оперировать с объектами. В примере выше в первом случае мы обратились к функции prompt() и ввели да, во втором варианте мы нажали отмену, что вернуло null.
Вызов метода prompt() из консоли браузера
Вызов метода prompt() из консоли браузера
Так как у окон, возникающих в результате использования методов prompt(), alert(), confirm() нет возможностей модифицирования, их практическое применение не приветствуется (тем более, раньше ими «баловались» спамеры).

Перечень всех свойств и методов объекта window огромен (увидеть можно, если ввести в консоли window) - свойства performance, screen, методы back(), find() и другие.

1.2. Объект document - модель DOM

Нужен для создания и изменения элементов на странице. Порождает так называемую модель DOM. Все теги, комментарии и текст web-ресурса становятся объектами, с которыми можно работать.
Пример – Консоль браузера

> document.body.style.fontSize = "30px";

При помощи данной команды мы меняем размер шрифта конкретной страницы.

1.3. Объектная модель браузера - BOM

Для доступа к инструментам самого браузера используется BOM. Из этого окружения можно получить данные о просмотрщике, операционной системе, текущем адресе ресурса, истории посещения страниц и пр.
Пример – Консоль браузера

// Получаем адрес текущей страницы
> location.hostname
< www.google.com
// Получаем тип операционной системы
> navigator.platform
< "Win32"
// Получаем высоту экрана
> screen.height
< 1080
Работа с объектами BOM
Работа с объектами BOM

1.4. Подключение скриптов к сайту

Внедрять JavaScript на страницу можно несколькими способами:
  1. Напрямую (непосредственно в HTML-код);
  2. Из внешних файлов (в заголовочной части документа или перед закрывающим тегом <body>).
Удобнее всего создавать внешние скрипты, которые наподобие подключаемых таблиц стилей прикрепляются к документу. Чтобы js-код сработал после полной прогрузки всех элементов страницы, его принято размещать перед закрывающим тегом <body>. Однако, никто не запрещает давать ссылку на него и в заголовке с использованием ключевого слова defer.
Пример - HTML

<!DOCTYPE html>
<html lang="ru">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Практика JS</title>
	<script src="learning.js" defer></script>
</head>
<body>
	<h1>Изучаем JS</h1>
</body>
</html>
Атрибут defer подразумевает, что сначала загрузится всё содержимое ресурса и его стили, а лишь затем будет применен скрипт.
2

Навигация по DOM

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

HTML
├ HEAD
├─ #text
├─ META
├─ #text
├─ META
├─ #text
├─ TITLE
├── #text Практика JS
├─ #text
├─ SCRIPT
└── #text
├ BODY
├─ #text
├─ H1
└── #text Изучаем JS
Помимо непосредственно самих тегов в качестве объектов выступают текстовые данные и комментарии. Даже переносы строк (когда мы отделяем блоки друг от друга) будут отображаться в виде текстового объекта.

Для навигации по DOM-дереву следует иметь представление о следующих понятиях:

1. Корневые элементы:
а) document.documentElement (самый верхний узел, соответствует тегу <html>, включает в себя всё содержимое документа);
б) document.head (заголовочная часть web-страницы);
в) document.body (тело документа, содержимое тега <body>).

2. Узел-родитель (parentNode – непосредственный потомок конкретного объекта)

3. Дети, потомки:
а) childNodes (коллекция потомков определенного объекта);
б) firstChild (первый ребенок);
в) lastChild (последний дочерний элемент).

4. Соседи (один уровень иерархии):
а) nextSibling (следующий сосед того же родителя);
б) previousSibling (предыдущий элемент внутри родителя, находящийся на том же уровне, что и изначальный).

Откроем представленную выше HTML-страницу в браузере и в консоли поработаем с изученным материалом.
Пример – Консоль браузера

// Выводим содержимое всей страницы
> document.documentElement
< html

// Выводим потомков узла head
> document.head.childNodes
< NodeList(9) [text, meta, text, meta, text, title, text, script, text]

//  Получаем первую дочернюю ноду узла head и выводим ее первого соседа
> document.head.childNodes[0].nextSibling
< meta
Работа с нодами DOM-дерева
Работа с нодами DOM-дерева
Перечень узлов внешне напоминаем массив (список), но имеет свои особенности:
  1. Не изменяется (нельзя добавить или удалить из него объект);
  2. Не поддерживает методы массивов;
  3. Перебирается циклом for…of;
  4. Является «живым» (удаление или добавление на страницу элементов автоматически отразится на размере коллекции).
Для работы с JavaScript создадим файл learning.js, который прикрепим к странице. Теперь в консоли мы получим перечень всех девяти узлов, находящихся внутри тега <head>.
Пример – JavaScript (здесь и далее - файл learning.js)

for (let node of document.head.childNodes) {
	console.log(node);
}
Результат выполнения

#text
  <meta charset=​"UTF-8">​
#text
  <meta name=​"viewport" content=​"width=device-width, initial-scale=1.0">​
#text
  <title>​Практика CSS​</title>​
#text
  <link rel=​"stylesheet" href=​"css/​style.css">​
#text

В большинстве случаев нам не особо нужны дополнительные текстовые объекты DOM-дерева, а требуется лишь список тегов. Для этого имеются соответствующие инструменты (свойства):
  1. Свойство children - список детей HTML-элемента, которые сами являются тегами;
  2. Свойство firstElementChild – первый потомок;
  3. Свойство lastElementChild – последний ребенок;
  4. Свойство previousElementSibling – предыдущий сосед элемента;
  5. Свойство nextElementSibling – следующий сосед элемента;
  6. Свойство parentElement – родительский тег.
Пример – Консоль браузера

// Получаем потомков узла body
> document.body.children
< HTMLCollection(1) [h1]

// Получаем родителя узла body
> document.body.parentElement
< html

// Получаем верхнего соседа узла body
> document.body.previousElementSibling
< head

Работа с тэгами внутри DOM структуры
Работа с тэгами внутри DOM структуры
Если при вызове свойства childNodes получаем коллекцию узлов, то children показывает HTML-коллекцию.
3

Свойства DOM-узлов

Узлы в зависимости от класса могут иметь собственный перечень свойств и методов. В любом случае, они являются объектами JavaScript, что разрешает задавать им пользовательские атрибуты и методы.

DOM-узлы принадлежат к определенным классам, имеющим иерархию:
  1. Класс EventTarget – класс-основа, позволяющий объектам поддерживать события;
  2. Класс Node – ключевой класс для узлов, позволяющий им быть обработанными при помощи свойств и методов: childNodes, nextSibling и др.;
  3. Класс Element – отвечает за навигацию на уровне элементов и снабжает их методами поиска. Является базой для SVGElement, XMLElement и HTMLElement;
  4. HTMLElement – позволяет наследоваться остальным тегам (HTMLInputElement, HTMLAnchorElement, HTMLBodyElement) и наделяет их свойствами (click(), focus() и др.).
Иерархия классов DOM-узлов
Иерархия классов DOM-узлов
Чтобы понять принадлежность объекта к классу имеется специальная инструкция: instanceof. Узнать принадлежность можно и через console.dir().
Пример – Консоль браузера

> document.head instanceof HTMLElement
< true
> document.head instanceof Node
< true
> console.dir(document.body)
< … __proto__: HTMLBodyElement
Ранее применялся еще один способ: свойство nodeType, но сегодня он не столь информативен.
Пример – Консоль браузера

> document.head.nodeType
< 1
Число 1 говорит о том, что мы имеем дело с узлом-элементом, а число 3 – текстовым.

На основании узла DOM возможно выяснить имя тега элемента: nodeName и tagName. Второе свойство имеется только у объектов класса Element, а первое доступно всем.
Пример – Консоль браузера

> document.TagName
< undefined
> document.nodeName
< #document
Рассмотрим некоторые другие свойства, часто используемые на практике.
Данные свойства можно не только получать, но и задавать.
Пример – Консоль браузера

// Содержимое тела документа
> document.body.innerHTML
< <h1>Основные свойства узлов</h1>…

// Содержимое тега <body> вместе с ним самим
> document.body.outerHTML
< "<body>↵	<h1>Осн…

// Внутренний текст всех входящих элементов
> document.body.textContent
< Основные свойства узлов

// Определяем, спрятан ли тег <h1>
> document.body.firstElementChild.hidden
< false

// Прячем тег <h1>
> document.body.firstElementChild.hidden = true
< true

// Получаем содержимое текстового узла (в нашем случае – это перенос строки)
> document.body.firstChild.data
< ↵

// Меняем перенос строки на произвольный текст
> document.body.firstChild.data = 'Текст'
< "Текст"
Имеются и другие свойства, характерные как для отдельных тегов, так и общие у всех:
Важно
Свойства DOM-объектов не следует путать с атрибутами HTML-элементов. Важно запомнить, что свойства способны принимать любые значения, а атрибутами могут быть только строки.
    Встроенные свойства уже были рассмотрены, но никто не мешает добавлять собственные, в том числе и методы.
    Пример – Консоль браузера
    
    // Создаем новое свойство
    > document.body.myProperty = 'Созданное свойство'
    > document.body.myProperty
    < "Созданное свойство"
    
    Взаимодействие с нестандартными, пользовательскими атрибутами осуществляется с помощью четырех методов:
    Пример – Консоль браузера
    
    // Проверяем наличие атрибута
    > document.body.hasAttribute('layout')
    < false
    
    // Задаем новый атрибут
    > document.body.setAttribute('layout', 'flexible')
    
    // Получаем свежесозданный атрибут
    > document.body.getAttribute('layout')
    < "flexible"
    
    // Удаляем атрибут
    > document.body.removeAttribute('layout')
    > document.body.hasAttribute('layout')
    < false
    
    Изначально у тега <body> не было свойства layout. Мы его создали, присвоили значение, а потом удалили. С помощью продемонстрированных выше методов, естественно, можно обращаться и к стандартными атрибутам DOM-объектов.
    Немного про Data-атрибуты
    Многие из вас могли видеть на ряде сайтов некие странные свойства, начинающиеся с data-. Оказывается, они удобны и разработаны специально для программистов. К ним можно получить доступ через точечную нотацию.

    Пример – HTML
    ---
    <body data-site="https://yandex.ru/" data-name="Yandex"></body>

    Пример – Консоль браузера
    ---
    document.body.dataset.site "https://yandex.ru/" document.body.dataset.name "Yandex"

    Как видно, чтобы обратиться к таким атрибутам требуется указать свойство dataset, а потом все то, что идет после data-.
    Пример – HTML
    
    <body data-site="https://yandex.ru/" data-name="Yandex"></body>
    
    Пример – Консоль браузера
    
    document.body.dataset.site
    "https://yandex.ru/"
    document.body.dataset.name
    "Yandex"
    
    Как видно, чтобы обратиться к таким атрибутам требуется указать свойство dataset, а потом все то, что идет после data-.
    4

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

    Все рассмотренные до этого способы доступа к тегам основывались либо на их базовости (для них зарезервированы специальные имена), либо при помощи родства-соседства. Это не всегда удобно, так как может порождать длинные цепочки, а также не нести никакой информативности. Именно для этого в браузерном окружении имеются другие способы поиска элементов:
    Для практического примера создадим следующий набор тегов внутри <body>.
    Пример - HTML
    
    <h1 id="first_header">Поиск в дереве документа</h1>
    <article class="main_article">Первая статья</article>
    <article class="main_article">Вторая статья</article>
    <article class="main_article" name="last">Последняя статья</article>
    <h1>Основы DOM</h1>
    
    Пример – Консоль браузера
    
    // Выберем заголовок и получим все его содержимое
    > document.getElementById('first_header')
    < <h1 id="first_header">Поиск в дереве документа</h1>
    
    // Выберем последнюю статью на основании свойства «name»
    > document.getElementsByName('last')
    < NodeList [article.main_article]
    
    // Найдем все статьи на странице по тегу
    > document.getElementsByTagName('article')
    < HTMLCollection(3) [article.main_article, article.main_article, article.main_article, last: article.main_article]
    
    // Выведем список статей с классом «main_article»
    > document.getElementsByClassName('main_article')
    < HTMLCollection(3) [article.main_article, article.main_article, article.main_article, last: article.main_article]
    
    // Получим доступ к первой статье
    > document.querySelector('.main_article')
    < <article class="main_article">Первая статья</article>
    
    // Представим список всех заголовков первого уровня
    > document.querySelectorAll('.main_article')
    < NodeList(3) [article.main_article, article.main_article, article.main_article]
     
    
    Практически во всех случаях, предполагающих множественный поиск по странице, мы получаем живую коллекцию объектов. Есть одно исключение: функция querySelectorAll(). Функции querySelectorAll() и querySelector() позволяют применять CSS-селекторы любой сложности (о них говорится в разделе по CSS).
    5

    Внесение изменений в DOM-дерево

    При помощи JavaScript мы можем не только находить элементы в web-документе или менять их свойства, но и вносить изменения в структуру DOM-дерева:
    1. Создавать новые теги
    2. Удалять имеющиеся
    3. Заменять их на другие
    Для этого применяются следующие инструменты:
    Для практики будем пользоваться следующим HTML-шаблоном.
    Пример – HTML
    
    <article class="main-article">Основная статья</article>
    <p>Некоторый абзац</p>
    <section>Дополнительная секция</section>
    
    Страница до изменений
    Страница до изменений

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

    JavaScript - Файл learning.js
    
    const text = document.createTextNode('Некоторый текст');
    const h1 = document.createElement('h1');
    document.body.prepend(text);
    document.body.prepend(h1);
    h1.append('Заголовок страницы')
    
    Мы сформировали текстовый объект и тег <h1>, которые разместили в самом начале страницы. Внутрь заголовка дополнительно добавлено содержание.
    Страница после добавления текста и заголовка
    Страница после добавления текста и заголовка

    5.2. Копирование абзаца и добавление его после секции с заменой текста

    JavaScript - Файл learning.js
    
    const p = document.querySelector('p');
    const pModified = p.cloneNode(true);
    pModified.innerHTML = '<strong>Новый абзац</strong>'
    const section = document.getElementsByTagName('section')[0];
    section.after(pModified)
    
    Проведенные манипуляции позволили скопировать абзац, заменить его содержимое и вставить в конец документа.
    Страница после копирования и модификации абзаца
    Страница после копирования и модификации абзаца

    5.3. Замена всех тегов на странице заголовками второго уровня

    JavaScript - Файл learning.js
    
    const tags = document.body.children
     
    for (let tag of tags) {
    	const h2 = document.createElement('h2')
    	h2.innerText = 'Останутся только заголовки!'
    	tag.replaceWith(h2)
    }
    
    Таким способом произведена замена абзаца, статьи и секции на заголовки <h2> с текстом Останутся только заголовки!.
    Замена всех элементов страницы заголовками
    Замена всех элементов страницы заголовками

    5.4. Добавление текста со специальными символами

    Приведенные выше способы добавляют текст только безопасным способом (все специальные символы преобразуются. Например, знак > переведется в &gt;). Чтобы получить возможность внедрять в документ строки с тегами, применяется метод insertAdjacentHTML(). Добавим до и после абзаца ссылку с выделенным жирным шрифтом.
    JavaScript - Файл learning.js
    
    const p = document.querySelector('p');
    p.insertAdjacentHTML('beforebegin',
    '<a href="https://www.google.com/"><strong>Google</strong></a>');
    p.insertAdjacentHTML('afterend',
    '<a href="https://yandex.ru/"><strong>Yandex</strong></a>');
    
    Добавление текста со спецсимволами с помощью метода insertAdjacentHTML()
    Добавление текста со спецсимволами с помощью метода insertAdjacentHTML()
    6

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

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

    Весь список доступных CSS-атрибутов применяется в JavaScript для задания требуемых значений. В первую очередь ознакомимся с методами добавления, удаления, смены классов, а также проверки их наличия. В качестве шаблона используем следующий HTML-документ.
    Пример - HTML
    
    <!DOCTYPE html>
    <html lang="ru">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Свойства узлов</title>
    	<script src="learning.js" defer></script>
    	<style>
        	.colored{
            	color:darkorange;
        	}
    	</style>
    </head>
    <body>
    	<ul class="colored">
        	<li>1</li>
        	<li>2</li>
        	<li>3</li>
        	<li>4</li>
        	<li>5</li>
    	</ul>
    	<ul>
        	<li>А</li>
        	<li>Б</li>
        	<li>В</li>
        	<li>Г</li>
        	<li>Д</li>
    	</ul>
    </body>
    </html>
    
    Имеем 2 списка, первому из которых присвоен класс colored, делающий текст темно-оранжевым.
    Внешний вид шаблона страницы
    Внешний вид шаблона страницы

    6.1. Вывод содержимого атрибута «class»

    Пример – Консоль браузера
    
    // Получаем элементы с тегом <ul>
    > const uls = document.querySelectorAll('ul')
    
    // Для каждого элемента получаем имя класса
    > for (let tag of uls) {
    	console.log(tag.className)
    }
    < colored
    < ‘’
    
    У первого списка мы обнаружили класс colored, а у второго нет ни одного класса.

    6.2. Изменение классов

    Объект classList позволяет менять, удалять или добавлять классы к элементам. Здесь имеется 4 метода:

    1. Метод add() – добавить новый класс;
    2. Метод remove() – удалить класс у тега;
    3. Метод toggle() – добавить класс при отсутствии, а иначе - удалить;
    4. Метод contains() – проверяет наличие класса.
    Пример – Консоль браузера
    
    // Получаем элементы с тегом <ul>
    > const uls = document.querySelectorAll('ul');
    > for (let tag of uls) {
    	console.log(tag.classList.contains('colored'));
    
    // Удаляем класс colored для первого списка и наоборот добавляем его для второго
        tag.classList.toggle('colored');
        console.log(tag.classList.contains('colored'));
    }
    < true
    < false
    < false
    < true
    
    Первый элемент <ul> изначально имеет искомый класс, а потом мы его удаляем. Для второго элемента ситуация обратна.
    Изменение внешнего вида списков после добавления/удаления класса colored
    Изменение внешнего вида списков после добавления/удаления класса colored

    6.3. Изменение атрибутов тегов

    Изменять и определять конкретные атрибуты тегов достаточно просто: нужно лишь к ним обратиться при помощи соответствующих методов. Важно запомнить, что в JavaScript не используются дефисы, поэтому составные свойства пишутся в верблюжьей нотации:
    CSS свойство -> JS свойство
    
    font-size (CSS) => fontSize (JS).
    
    
    Важно
    Не рекомендуется использовать сокращенные наименования атрибутов (например, padding). Лучше конкретно указывать требуемый атрибут (padding-left).
    В качестве иллюстрации изменения атрибутов поработаем со списками.
    Пример – Консоль браузера
    
    // Поменяем маркеры на квадраты
    > document.querySelector('.colored').style.listStyleType = 'square'
    // Увеличим шрифт
    > document.querySelector('.colored').style.fontSize = '2rem'
    
    Страница после изменения маркеров и увеличения шрифта
    Страница после изменения маркеров и увеличения шрифта

    6.4. Сброс стилей

    Для сброса стилей применяется свойство cssText. Оно используется не часто, так как полностью переписывает стилистику, что может привести к непредусмотренным результатам.
    Пример – Консоль браузера
    
    > document.querySelector('.colored').style.cssText = `
    	list-style-type: "-=- ";
    	list-style-position: inside;
    	text-align: center;
    
    Страница после изменения стиля списка с помощью свойства cssText
    Страница после изменения стиля списка с помощью свойства cssText

    6.5. Получение стилей элементов

    Немаловажная задача – выяснить стили элемента. Метод getComputedStyle() позволяет решить эту проблему.
    Пример – Консоль браузера
    
    > getComputedStyle(document.querySelector('.colored')).display
    < block
    > getComputedStyle(document.querySelector('.colored')).marginBottom
    < 19.2px
    > getComputedStyle(document.querySelector('.colored')).backgroundColor
    < rgba(0, 0, 0, 0)
    
    Приведенный перечень свойств далеко не полный. Для работы с системой координат, размерами окна, прокруткой имеются свои инструменты. Ознакомиться с ними можно в официальной документации (на сайте MDN).
    7

    Браузерные события

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

    7.1. Обработка события через свойство тэга

    Обработчики прописываются в HTML-коде:
    Пример - HTML
    
    <body onclick="console.log('Нажали!')"></body>
    
    
    При нажатии в любом месте веб-страницы в консоли выведется сообщение Нажали!.

    7.2. Обработка события через выбор элемента в js-файле

    Данный способ является более удобным и наглядным:
    Пример – JavaScript
    
    function clicked() {
    	console.log('Нажали!');
      }
     
    document.body.onclick = clicked;
    
    В данном случае нам не нужно задавать свойство onclick для тега <body>, достаточно лишь присвоить ему обработчик в скрипте.

    7.3. Обработка события через специальные методы

    Сюда относятся следующие методы:
    1. addEventListener()
    2. removeEventListener()
    Функция addEventListener() позволяет присваивать элементам более одного обработчика событий, а также работает не только с тегами, но и любыми DOM-объектами.
    Пример – JavaScript
    
    function one() {
    	console.log('Нас тут много');
    }
     
    function two() {
    	console.log('Поверьте!');
    }
     
    function three() {
    	console.log('Спасибо методу addEventListener');
    }
     
    main = document.documentElement;
    main.addEventListener('click', one);
    main.addEventListener('click', two);
    main.addEventListener('click', three);
    
    Одного нажатия кнопки мыши достаточно, чтобы сработало 3 обработчика событий.

    7.4. Параметр event функции-обработчика

    Зачастую требуется не просто обработать некое событие, но и понять детали того, что именно произошло. В этом случае в функции-обработчики передается параметр event, имеющий массу полезных свойств.
    Пример – JavaScript
    
    function getCoords(event) {
    	console.log(event.type);
    	console.log(event.clientX + 'x' + event.clientY)
    }
     
    main = document.documentElement;
    main.addEventListener('click', getCoords);
    
    При помощи функции выше мы узнали тип события, а также координаты мыши в момент нажатия.

    7.5. Этапы события (погружение, достижение цели, всплытие)

    Когда на странице расположено большое количество объектов, то нажатие на одном из них (если у него есть ряд предков) вызовет срабатывание обработчиков (если они присутствуют) у его родителей. Так, если сделан клик по абзацу внутри тега <article>, то и он это заметит. Данный принцип называется всплытием.

    С другой стороны, мы не всегда приветствуем такое поведение. Может потребоваться реакция браузера только на конкретное событие, а не на всю цепочку родителей. Чтобы узнать, какой элемент вызвал изначальное срабатывание события, используют свойство event.target. Даже если ивент присвоен верхнему элементу, то выявится конкретный дочерний объект, который был задействован.
    Пример – HTML
    
    <div>
      <h4>Всплытия</h4>
      <p><span>Некий текст</span></p>	
    </div>
     
    
    Пример – JavaScript
    
    function who(event) {
        console.log(event.target.tagName);
    }
     
    main = document.documentElement;
    main.addEventListener('click', who);
     
    
    Когда мы будем кликать по области документа в разных местах, то будем получать имена различных тегов: <h4>, <html>, <p> и т.д.

    Чтобы всплытие прекратилось, применяется метод stopPropagation(). Важно осознать, что всплытие – вещь полезная, и прекращать его по незнанию - неразумно.

    Говоря в целом о цикле события стоит отметить, что он состоит из трех стадий:
    1. Погружение (capturing) – от самого верхнего родителя до целевого объекта;
    2. Достижение цели (target) – достигается элемент, на котором случилось событие;
    3. Всплытие (bubbling) – перемещение от дочернего элемента к родителям.
    Погружение используется редко, но о нем следует знать. Чтобы его увидеть, для addEventListener() передают третий параметр.
    Пример – JavaScript
    
    for(let tag of document.querySelectorAll('*')) {
        tag.addEventListener("click", event => console.log(`Погружаемся вглубь: ${tag.tagName}`), true);
        tag.addEventListener("click", event => console.log(`Поднимаемся пузырьком: ${tag.tagName}`));
      }
    
    Результат выполнения (при нажатии на <h4>)
    
    Погружаемся вглубь: HTML
    Погружаемся вглубь: BODY
    Погружаемся вглубь: DIV
    Погружаемся вглубь: H4
    Поднимаемся пузырьком: H4
    Поднимаемся пузырьком: DIV
    Поднимаемся пузырьком: BODY
    Поднимаемся пузырьком: HTML
    
    Как видно, процесс затрагивает корневой элемент и доходит до целевого, а затем идет в обратном порядке.

    7.6. Делегирование событий

    В завершение поговорим о делегировании событий. Оно нужно для того, чтобы уменьшить количество кода. Предположим, у родительского элемента в наличии большое количество потомков. Каждому из них мы бы хотели присвоить одно и то же событие. Удобнее всего задать его предку, а в зависимости от положения мыши, например, передавать его только дочернему объекту.
    Пример – HTML
    
    <!DOCTYPE html>
    <html lang="ru">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Обработчик событий</title>
    	<script src="learning.js" defer></script>
    	<style>
      	  table {
            	border: 2px solid blue;
        	}
     
        	td {
            	background-color: mediumturquoise;
            	border: 1px solid black;
            	height: 50px;
            	width: 200px;
            	text-align: center;
        	}
     
       	 .colored {
            	background-color:greenyellow;
        	}
    	</style>
    </head>
    <body>
    	<table>
        	<tr>
            	<td>Выбери меня</td>
            	<td>Или меня</td>
            	<td>Можно и меня</td>
            	<td>Лучше меня</td>
        	</tr>
        	<tr>
            	<td>Выбери меня</td>
            	<td>Или меня</td>
            	<td>Можно и меня</td>
            	<td>Лучше меня</td>
        	</tr>
        	<tr>
            	<td>Никого не слушай</td>
            	<td>Лучше сюда</td>
                <td>Я тут главный</td>
            	<td>Готов?</td>
        	</tr>
        	<tr>
            	<td>Никого не слушай</td>
            	<td>Лучше сюда</td>
            	<td>Я тут главный</td>
            	<td>Готов?</td>
        	</tr>
    	</table>
    </body>
    </html>
     
    
    JavaScript - Файл learning.js
    
    let selected;
    table = document.querySelector('table')
     
    table.onmouseover = function(event) {
      let target = event.target;
      if (target.tagName === 'TD') {
    	highlight(target);
    	return;
      }
    };
     
    function highlight(td) {
      if (selected) {
        selected.classList.remove('colored');
      }
      selected = td;
      selected.classList.add('colored');
    }
    
    Теперь при наведении мыши на отдельную ячейку таблицы мы меняем ее фоновый цвет путем добавления нового класса, а когда ячейка вне фокуса, то класс удаляется.
    Изменение цвета ячейки при наведении мыши
    Изменение цвета ячейки при наведении мыши
    ПОДВЕДЕМ ИТОГИ
    Без JavaScript не обходится современный frontеnd. Этот язык программирования позволяет управлять DOM-деревом при помощи браузерного окружения. Мы можем выбирать необходимые элементы, модифицировать их, изменять количество. В дополнение, к каждому объекту не сложно применить определённые обработчики событий, которые существенно расширят функционал сайта, да и пользователям станет приятнее взаимодействовать с ресурсом.

    ПРАКТИЧЕСКИЙ БЛОК

    1

    Вопросы

    2

    Задачи

    Задача 1
    Имеется список ссылок, как внутренних, так и ведущих на сторонние ресурсы. Сделайте для всех сторонних ссылок верхнее подчеркивание и зеленый цвет, а для сайта Google задайте только цвет красного цвета. Ссылки придумайте сами с учетом условия.

    Задача 2
    Внутри тега <div> размером 700 х 500 px расположен другой элемент <div> размера 40 х 40 px. Добейтесь следующего эффекта: когда пользователь нажимает левую кнопку мыши в любой области внешнего тега, внутренний элемент перемещается в это место.
    
    <!DOCTYPE html>
    <html lang="ru">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Обработчик событий</title>
      <script src="learning.js" defer></script>
    
      <style>
        	#outer {
          	  width: 700px;
          	  height: 500px;
          	  border: 2px solid gold;
          	  background-color:khaki;
          	  position: relative;
          	  overflow: hidden;
          	  cursor: pointer;
        	}
    	
        	#inner {
          	  position: absolute;
          	  left: 25px;
          	  top: 30px;
          	  width: 40px;
          	  height: 40px;
          	  transition: all 1.5s;
          	  background-color: mediumblue;
          	  border-radius: 50%;
        	}
    </style>
    
    </head>
    
    <body>
      <div id="outer">
        <div id="inner"></div>           
      </div>
    </body>
    
    
    </html>
     
    
    3

    Решения

    Задача 1

    Подходов к решению может быть несколько. Предположим, что все ссылки на внутренние страницы представлены относительными путями.
    Решение – HTML
    
    <a href="e.html">Внутренняя</a>
    <a href="https://www.google.com/">Гугл</a>
    <a href="https://www.ya.com/">Внешняя</a>
    
    Решение – JavaScript
    
    let urls = document.querySelectorAll('a');
     
    for (let url of urls) {
      let href = url.getAttribute('href');
      if (href.includes('http')) {
    	url.style.color = 'green';
    	url.style.textDecoration = 'overline';
      }
      if (href.includes('google.com')) {
    	url.style.color = 'red';
    	url.style.textDecoration = 'underline';
      }
    }
    

    Задача 2

    Так как внутренний <div> имеет абсолютное позиционирование, а внешний – относительное, координаты меньшего блока будут рассчитываться относительно верхнего левого угла родителя. Необходимо также добиться того, чтобы дочерний элемент не выезжал за рамки своего предка. Для плавности перемещения задан transition в размере 1,5 секунды.
    Решение – JavaScript
    
    outer.onclick = function(event) {
    	let outerCoords = this.getBoundingClientRect();
    	let innerCoords = {
      	top: event.clientY - outerCoords.top - outer.clientTop - inner.clientHeight / 2,
      	left: event.clientX - outerCoords.left - outer.clientLeft - inner.clientWidth / 2
    	};
     
    	if (innerCoords.top < 0) innerCoords.top = 0;
    	if (innerCoords.left < 0) innerCoords.left = 0;
    	if (innerCoords.left + inner.clientWidth > outer.clientWidth) {
        	innerCoords.left = outer.clientWidth - inner.clientWidth;
    	}
    	if (innerCoords.top + inner.clientHeight > outer.clientHeight) {
        	innerCoords.top = outer.clientHeight - inner.clientHeight;
    	}
    	inner.style.left = innerCoords.left + 'px';
    	inner.style.top = innerCoords.top + 'px';
    }
    
    Как вам материал?

    Читайте также