События и addEventListener()
Содержание- Синтаксис
- removeEventListener()
- Популярные события
- Как добавить обработчик на множество элементов
- addEventListener и forEach
- Делегирование событий
- Всплытие и перехват
- Получаем доступ к элементу через this
- Итого
Метод addEventListener() назначает обработчик события: функцию, которая выполнится, когда с указанным объектом произойдёт нужное действие. События поддерживают Document, DOM-элементы, Window и другие объекты.
addEventListener() - один из трёх способов назначить обработчик события наряду с HTML-атрибутом и присваиванием функции свойству объекта.
<!-- добавление атрибута -->
<button onclick="alert('Терпение и труд всё перетрут')">
Хочу стать программистом!
</button>
<script>
const myFirstEvent = document.querySelector('button');
// обращаемся к свойству напрямую
myFirstEvent.onclick = function () {
alert('Да будет так!');
};
// используем метод addEventListener
myFirstEvent.addEventListener('click', function () {
alert('Дорогу осилит идущий');
});
</script>
addEventListener() имеет важное преимущество перед остальными способами: метод позволяет назначить несколько обработчиков одного события. У объекта только одно свойство, например onclick для клика или onmousemove для движения мыши, поэтому повторное присваивание функции этому свойству перезапишет предыдущий обработчик.
В примере выше это видно на практике: модальное окно с Да будет так! появится, а Терпение и труд всё перетрут - нет.
Некоторые события можно отследить только через addEventListener(): например, для DOMContentLoaded нет свойства-обработчика вроде onDOMContentLoaded.
Синтаксис
element.addEventListener(eventType, handler, options);
element - объект, действие по которому отслеживаем.
eventType - тип события, которое мы хотим отследить: клик, прокрутка мыши, нажатие клавиши и т.д. Написание чувствительно к регистру: click правильно, Click или CLICK неправильно.
handler - имя функции или сама функция, которая будет выполнена после наступления события.
options - необязательный объект. В нём можно указать, например, следующие параметры:
capture: значениеtrueилиfalse, которое задаёт фазу обработки. По умолчаниюfalse: на предках обработчик сработает при всплытии;true- при перехвате, до достижения цели. Если передать третьим аргументом булево значение, оно задаст только этот параметр.once: значение типаboolean; если указатьtrue, обработчик будет удалён после первого вызова.passive: сообщает браузеру, что обработчик не будет отменять стандартное действие. В пассивном обработчике вызовpreventDefault()игнорируется и может вызвать предупреждение в консоли.
<button>В скором времени я стану JavaScript middle</button>
<script>
const clickButton = document.querySelector('button');
clickButton.addEventListener('click', showMessage, {
capture: false,
once: true,
passive: false,
});
function showMessage() {
alert('У тебя всё получится, я в тебя верю!');
}
</script>
При клике на кнопку появится модальное окно с сообщением. При втором клике ничего не произойдёт, потому что once равно true: после первого вызова обработчик удаляется.
removeEventListener()
Удалить обработчик события можно методом removeEventListener() в нужный момент. Чтобы это сработало, нужно передать тот же тип события, ту же ссылку на функцию и такое же значение capture. Анонимную функцию можно удалить, только если заранее сохранить ссылку на неё: заново написанная, даже идентичная функция будет другим объектом.
<button>Удалить обработчик события</button>
<script>
const clickButton = document.querySelector('button');
clickButton.addEventListener('click', showMessage);
function showMessage() {
if (clickButton.innerText === 'Удалить обработчик события') {
alert('Обработчик удален');
clickButton.removeEventListener('click', showMessage);
} else {
alert('Обработчик не удален');
}
}
</script>
Если изменить текст кнопки до клика, условие не выполнится и обработчик не будет удалён.
Популярные события
Рассмотрим события, которые отслеживаются чаще других:
click - активация элемента: обычно кликом или касанием, а для некоторых элементов - и с клавиатуры;
contextmenu - попытка открыть контекстное меню, обычно правой кнопкой мыши;
mouseover / mouseout - наведение на элемент курсора мыши / курсор покидает элемент;
mousemove - движение мыши;
keydown / keyup - клавиша нажата / клавиша отпущена;
DOMContentLoaded - HTML разобран и DOM-дерево построено; изображения и другие ресурсы страницы могут ещё загружаться.
В инструментах разработчика можно посмотреть свойства-обработчики DOM-элемента: выберите элемент, откройте Свойства (Properties) и отфильтруйте их по on. Это не полный список событий, а только доступные свойства-обработчики.
Как добавить обработчик на множество элементов
addEventListener и forEach
<button class="myChoice">JavaScript</button>
<button class="myChoice">Python</button>
<script>
const choice = document.querySelectorAll('.myChoice');
choice.forEach((choiceItem) => {
choiceItem.addEventListener('click', () => answer(choiceItem.innerHTML));
});
function answer(text) {
if (text === 'JavaScript') {
alert('Отличный выбор!');
} else {
alert('Возможно вы ошиблись?');
}
}
</script>
В этом примере мы получили все элементы с классом myChoice через querySelectorAll(), а затем с помощью forEach() назначили обработчик каждой кнопке.
Делегирование событий
Другой вариант - делегирование событий. Оно полезно, когда у однотипных или динамически добавляемых элементов есть общий родитель.
<style>
td {
width: 90px;
height: 90px;
border: 1px solid black;
text-align: center;
}
.hideText {
color: white;
}
</style>
<table>
<tr>
<td class="hideText">JavaScript</td>
<td class="hideText">React</td>
<td class="hideText">HTML</td>
</tr>
</table>
<script>
const getTable = document.querySelector('table');
getTable.addEventListener('click', function (event) {
const cell = event.target.closest('.hideText');
if (cell && getTable.contains(cell)) {
cell.classList.remove('hideText');
}
});
</script>
В этом примере обработчик назначен общему родителю - таблице. С помощью event.target.closest(‘.hideText’) находим ячейку, по которой кликнули, и удаляем у неё класс, делающий текст белым.
Всплытие и перехват
Всплытие - этап, на котором событие после целевого элемента проходит к его родителям. Если событие всплывает и обработчики добавлены без capture, сначала сработает обработчик на целевом элементе, затем - на его предках.
<body> - в четвертую очередь
<span></span> - не сработает
<span> - в третью очередь
<span> - во вторую очередь
<span></span> - кликаем сюда, обработчик сработает на этом элементе в первую очередь
</span>
</span>
</body>
Перехват (capture) - этап, на котором событие идёт от внешних предков к целевому элементу. Обработчик на предке с capture: true сработает до обработчика на цели. Затем, если событие всплывает, оно вернётся от цели к предкам.
<body> - в пятую очередь
<span></span> - не сработает
<span> - в четвертую очередь
<span> - элемент со свойством capture: true, в первую очередь
<span> - в третью очередь
<span></span> - кликаем сюда, обработчик сработает на этом элементе во вторую очередь
</span>
</span>
</span>
</body>
Чтобы лучше понять порядок, поэкспериментируйте с примером: кликайте по разным элементам и смотрите последовательность вызовов в консоли.
Пример с квадратами<style>
.square1 {
display: flex;
width: 600px;
height: 600px;
align-items: center;
justify-content: center;
background: red;
}
.square2 {
display: flex;
width: 450px;
height: 450px;
align-items: center;
justify-content: center;
background: blue;
}
.square3 {
display: flex;
width: 350px;
height: 350px;
align-items: center;
justify-content: center;
background: yellow;
}
.square4 {
width: 250px;
height: 250px;
background: white;
}
</style>
<div class="square1">
<div class="square2">
<div class="square3">
<div class="square4"></div>
</div>
</div>
</div>
<script>
const squareFirst = document.querySelector('.square1');
const squareSecond = document.querySelector('.square2');
const squareThird = document.querySelector('.square3');
const squareFourth = document.querySelector('.square4');
squareFirst.addEventListener('click', () => clickSquare('Квадрат №1'));
squareSecond.addEventListener('click', () => clickSquare('Квадрат №2'));
squareThird.addEventListener('click', () => clickSquare('Квадрат №3'), {
capture: true,
});
squareFourth.addEventListener('click', () => clickSquare('Квадрат №4'));
function clickSquare(text) {
console.log(text);
}
</script>
Получаем доступ к элементу через this
В обычной функции-обработчике, добавленной через addEventListener(), this указывает на элемент, для которого сейчас выполняется обработчик. Это то же значение, что и event.currentTarget. У стрелочной функции нет собственного this, поэтому в ней лучше использовать event.currentTarget.
<button class="btn">После того, как выучу JS, примусь за React</button>
<script>
const getButton = document.querySelector('.btn');
getButton.addEventListener('click', printBtnText);
function printBtnText() {
alert(this.innerHTML);
}
</script>
Итого
Обработчик события позволяет запускать код в ответ на действия пользователя и другие события браузера. Метод addEventListener() - основной способ назначать такие обработчики объектам.
Обработчик можно удалить автоматически с помощью once: true или вручную через removeEventListener(), передав ту же функцию.
Чтобы обрабатывать действия множества элементов, можно назначить слушатель каждому из них через forEach() или использовать делегирование: назначить обработчик общему предку и определить целевой элемент через event.target.