Валерий Шестернин
События браузера обеспечивают взаимодействие между пользователем и веб-страницей, позволяя нам обрабатывать различные действия, от кликов мышью до отправки форм. В данной статье мы рассмотрим основные способы обработки браузерных событий, а также их жизненный цикл и примеры использования.
Виды событий
Событие является уведомлением от браузера о совершении определенного действия или изменении состояния элемента веб-страницы. Их можно условно разделить на:
- События устройств ввода (обычно клавиатура и мышь):
"click"
,"mousedown"
и"mouseup"
, позволяют обрабатывать клики, а"keydown"
,"keyup"
и"keypress"
- отслеживать ввод с клавиатуры.
- События элементов управления(инпуты, формы и т.п.):
"focus"
и"blur"
позволяют контролировать фокус,"change"
- изменение элемента,
“submit”
- отправку формы.
- изменение элемента,
- События CSS:
“aninationstart”
- CSS-анимация началась,
“transitionend"
– CSS-переход завершен и т.д.
- CSS-анимация началась,
- События документа:
DOMContentLoaded
– DOM готов,beforeunload
– пользователь покидает страницу,load
– внешние ресурсы загружены, стили применены и т.д.
Если Вам необходимо получить информацию о конкретном событии и его описание, рекомендую обратиться к подробному справочнику на MDN (https://developer.mozilla.org/ru/docs/Web/Events). В данной статье мы разберем взаимодействие с событиями и начнем с трех вариантов их обработки.
В HTML разметке
Что бы обработать событие мы можем просто добавить нужному тегу специальный атрибут (названия таких атрибуты состоят из “on” и названия события, как onclick, onchange и т.д.) и передать в него функцию, которая должна сработать при активации события. Вместо функции можно передать код, который так же исполнится при активации события, но такое описание чувствительно к формату скобок и сработает только если сам код будет обернут в двойные скобки(”код”
), а строки в нем в одинарные(’строка’
):
<script type="text/javascript">
const toThank = () =>{console.log('Thanks!')}
</script>
<button onclick="console.log('Thanks!')">Click me</button>
<button onclick="toTank()">Click me</button>
//эти две строки кода отработают идетично
<button onclick="console.log("Thanks!")">Click me</button>
//а здесь будет ошибка из-за неправельных скобок
Через DOM-свойство
Обработчик, который мы создали в предыдущем примере, будет храниться в свойстве DOM-объекта. Мы можем обратиться к такому свойству чтобы создать обработчик, а название такого свойства совпадает с названием атрибута и чувствительно к регистру:
<button id="myButton">Click me</button>
<script type="text/javascript">
const button = document.getElementById("myButton");
button.onclick = () => console.log("Thanks!");
</script>
Важно помнить, что невозможно добавить обработчик событий к объекту, используя метод setAttribute
, так как атрибуты являются строками, и переданная функция также будет интерпретирована как строка. Кроме того, если функция была объявлена в другой части кода, необходимо передавать ее по ссылке, а не вызывать.
<button id="myButton">Click me</button>
<script type="text/javascript">
const toThank = () =>{console.log('Thanks!')}
const button = document.getElementById("myButton");
button.onclick = toThank();
//не сработает потому что функция сразу вызывается
button.setAttribute('onclick', toThank);
//тоже не будет работать потому что превратится в строку
</script>
Что бы удалить обработчик, который повесили через атрибут или свойство, можно переназначить его и передать в него функцию, которая возвращает false
. В разметке такой трюк, в основном, применяется для отмены действий по умолчанию (переход по ссылкам, отправка формы и подобное), но лучше использовать специальный метод preventDefault
.
<button onclick="console.log('Thanks!')" id="myButton">Click me</button>
<script type="text/javascript">
const button = document.getElementById("myButton");
button.onclick = () => {return false}
//так мы назначим новый обработчик и отменим действие
</script>
<a href="/" onclick="return false">Click me</a>
<a href="/" onclick="event.preventDefault()">Click me</a>
//а в этих двух примерах - отменили действие по умолчанию
//теперь ссылка не откроется при нажатии
С помощью метода addEventListener
Оба предыдущий способа имеют серьезный недостаток - нельзя повесить несколько обработчиков на одно событие. Для таких случаев есть специальный метод addEventListener
который принимает название события, функцию и необязательные параметры настроек: element.addEventListener(event, handler, [options]);
. В настройках есть следующие опции: once:true
означает что после срабатывания обработчик будет удален, passive:true
сообщает браузеру что слушатель не будет отменять действие по умолчанию и capture
к которому мы вернемся немного позже.
<button id="myButton">Click me</button>
<script type="text/javascript">
const button = document.getElementById("myButton");
const handler1 = () => {console.log('Thanks!')}
const handler2 = () => {console.log('Thanks again!')}
button.onclick = () => {console.log('Hi!')};
button.addEventListener('click', handler1);
button.addEventListener('click', handler2);
</script>
Существуют события, которые могут быть обработаны только с использованием метода addEventListener
. Одним из них является DOMContentLoaded
, которое возникает, когда загрузка и построение DOM документа завершены.
В отличии от объявления в разметке или через DOM-свойство, не предусмотрено возможности получить обработчики созданные с помощью addEventListener
из элемента. Поэтому существует метод removeEventListener
который принимает строго те же параметры что и addEventListener
, с помощью которого был добавлен слушатель. Важно заметить что, если при создании слушателя функция была описана в нем, а не объявлена в другой части кода, такой слушатель невозможно удалить:
button.addEventListener("click", () => {console.log("Thanks")});
button.removeEventListener("click", () => {console.log("Thanks")});
//не сработает т.к. это две разные функции
const toTank = () => {console.log("Thanks")};
button.addEventListener("click", toTank);
button.removeEventListener("click", toTank);
//здесь передана одна и та же функция и все сработает
Жизненный цикл события (погружение и всплытие)
В примерах ранее мы рассматривали довольно простые случаи с нажатием на единичные кнопки и ссылки, а если их много и мы должны отслеживать их все? Или если мы повесим слушатель на элемент, а пользователь нажмет на вложенный, у которого нет слушателя? Придется вешать слушатели на каждый? На самом деле нет потому что почти все события в браузере проходят три фазы: погружение (capturing), фаза цели (target phase) и всплытие (bubbling stage). С их помощью мы можем поймать событие, даже если на целевом элементе (тот на котором произошло событие) нет слушателя.
В основном используется фаза всплытия. Во время этой фазы событие, при наличии обработчика, сначала активирует его на целевом элементе, затем на родителе и так на всех родительских элементах по цепочке. При этом мы можем узнать целевой элемент события с помощью метод target
объекта события, который как аргумент принимает вызываемая функция.
<div id="myDiv">
<button id="button-1">Click me</button>
<button id="button-2">Click me too</button>
</div>
<script type="text/javascript">
const sayHi = (event) => {
setTimeout(() => {
console.log(`Hello I am div, but the target is ${event.target.id}!`);
}, 0);
};
const element = document.getElementById("myDiv");
element.addEventListener("click", sayHi);
</script>
//Несмотря на то что на кнопках нет обработчиков
//собитие нажатия на них всплывет до div и он поздаровается
//сообщив id нажатой кнопки
Всплытие можно остановить вызвав метод event.stopPropagation()
на последнем элементе, обработчик которого нужно запустить или event.stopImmediatePropagation()
что бы кроме остановки всплытия предотвратить и срабатывание обработчика.
Вернемся к остальным фазам жизненного цикла события. Фазу цели нельзя обработать отдельно, а для обработки погружения нужно при объявлении обработчика через addEventListener
передать опциональным параметром {capture: true}
или просто true
, если другие опции слушателя не заданы. Как уже понятно из названия, погружение аналогично всплытию, но в этой фазе событие опускается по цепочке вложенных элементов к целевому.
<div>
<button >Click me</button>
</div>
<script type="text/javascript">
//веваем слушателей на все элементы страницы
for(let element of document.querySelectorAll('*')) {
element.addEventLstener("click", e => console.log(element.tagName), true);
}
</script>
//В консоль будут выведены все теги по цепочке до нашей кнопки:
//HTML
//BODY
//DIV
//BUTTON
Выше я упомянул что не все события проходят фазы жизненного цикла. Например события focus и blur относятся только к элементу на котором установлен или снят фокус, submit - только к отправляемся форме, а load к загруженному ресурсу или странице поэтому они не погружается и не всплывают, но это скорее исключения, потому что большинство событий проходят все фазы.
Заключение
Браузерные события являются важной составляющей веб-разработки, предоставляя возможность создавать отзывчивые пользовательские интерфейсы. Надеюсь что данная статья была полезна для понимания такой важной темы.
Карта развития разработчика
Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile
Комментарии
0