Если есть множество элементов, которые должны схоже реагировать на одно и то же событие, нет необходимости вешать обработчик на каждый элемент. Вместо этого мы можем разместить единый обработчик на родительском контейнере, который содержит все эти элементы.
Событие — например, клик — происходит на целевом элементе, а затем всплывает. Когда оно достигнет родителя, обработчик может его поймать.
Например, если мы хотим обрабатывать клики по ячейкам таблицы, можно повестить обработчик кликов на саму таблицу.
Элемент, на котором возникло событие
В объекте события хранится элемент, на котором оно изначально возникло:
let target = event.target;
У родителя могут быть потомки разных типов. Например, внутри одной таблицы могут быть и обычные ячейки TD
, и ячейки заголовка TH
. Если нас интересуют только клики по TD
, нужно убедиться, что событие произошло на нужном элементе, например, проверить имя тега:
if (target.tagName != 'TD') return;
Если клик был сделан по элементу TH
, то мы не будем его обрабатывать.
Вложенные элементы
Интересующий нас целевой элемент может сам быть контейнером для других элементов. Например, в ячейке таблицы могут располагаться другие теги — span
, i
, img
и т.п.
Если пользователь кликнет на вложенный в ячейку span
, то именно он окажется в свойстве event.target
. То есть такое событие не пройдет наш фильтр по имени тега, однако мы все равно хотим его обработать, ведь клик был сделан внутри ячейки.
Для этого нужно немного изменить фильтр. Мы будем проверять не только конкретный элемент event.target
, но и его родителей. Если среди них найдется подходящий, то мы обработаем этот клик.
Для этого можно использовать цикл while
:
let target = event.target;
// Цикл двигается вверх от target к родителям до table
while (target != table) {
if (target.tagName == 'TD') {
// нашли элемент, который нас интересует!
}
// Переходим к родительскому элементу
target = target.parentNode;
}
Важно поставить условие прекращения цикла. Если мы доберемся до самой таблицы, но так и не найдем подходящий элемент, то поиск можно прекратить.
Вместо цикла while
лучше использовать метода element.closest, который делает почти то же самое. Он принимает селектор и начинает последовательно проверять элемент и его родителей на соответствие этому селектору. Если такой элемент не найдется, вернется значение null
.
let target = event.target;
let td = target.closest('td');
if (!td) return;
Полный код
table.onclick = function(event) {
let target = event.target;
let td = target.closest('td');
if (!td) return; // клик вне td, не обрабатываем
// делаем что-то с найденным элементом td
highlight(td);
}
0 комментариев