Захват событий и бабблинг
При использовании обработчиков событий на элементах мы сталкиваемся с проблемой, когда обработчики событий выполняются и для других элементов, которые не должны были быть вызваны в первую очередь. Почему так происходит?
Давайте попробуем разобраться в этом на самом простом примере:
<div class="A" onclick="alert('Paren1')">
Parent 1
<div class="B" onclick="alert('Parent')">
Parent 2
<div class="C" onclick="alert('Child')">Child</div>
</div>
</div>
.A{
height: 200px;
width: 200px;
background: yellow;
}
.B{
height: 150px;
width: 150px;
background: green;
margin: 0 auto;
}
.C{
height: 100px;
width: 100px;
background: blue;
margin: 0 auto;
}
Что мы ожидаем увидеть в оповещении, если мы щелкнем на дочерний элемент, должны ли также срабатывать слушатели событий для родительского элемента? И если они срабатывают, то почему это происходит?
<div class="A" onclick="alert('Paren1')">
Parent 1
<div class="B" onclick="alert('Parent')">
Parent 2
<div class="C" onclick="alert('Child')">Child</div>
</div>
</div>
Оповещения будут отображаться в следующей последовательности:
- Child
- Parent 2
- Parent 1
Почему так происходит?
Изначально, когда происходит щелчок на внутренний дочерний элемент, захват событий начинается от документа к дочернему элементу. (Событие перемещается к элементу).
После фазы захвата наступает целевая фаза, которая начинается после завершения фазы захвата. Как в нашем случае при нажатии на дочерний элемент, дочерний элемент является целью.
После целевой фазы начинается фаза бабблинга. В этой фазе событие поднимается от дочернего элемента к окну.
Обработчики событий, связанные с элементами, выполняются в фазе бабблинга, поэтому последовательность идет от дочернего элемента к родительскому.
Можем ли мы выполнить обработчики в фазе захвата? Для этого мы можем установить второй параметр как true
при добавлении слушателя событий к элементу.
element.addEventListener('click',handler, true);
Итак:
<div class="A">Parent 1
<div class="B">Parent 2
<div class="C">Child</div>
</div>
</div>
.A{
height: 200px;
width: 200px;
background: yellow;
}
.B{
height: 150px;
width: 150px;
background: green;
margin: 0 auto;
}
.C{
height: 100px;
width: 100px;
background: blue;
margin: 0 auto;
}
document.querySelector('.A').addEventListener('click', () => alert('Parent1'), true);
document.querySelector('.B').addEventListener('click', () => alert('Parent2'), true);
document.querySelector('.C').addEventListener('click', () => alert('Child'), true);
Теперь оповещения будут отображаться в следующей последовательности:
- Parent 1
- Parent 2
- Child
Теперь, в зависимости от конкретного случая, мы можем либо использовать логику, основанную на фазе бабблинга, либо использовать фазу захвата соответствующим образом.
- Обработка событий в фазе захвата может быть важна в случае событий, которые не вызывают бабблинг, как фокус. В этом случае мы можем выполнить необходимую обработку в самой фазе захвата.
- Обработка событий в фазе бабблинга может быть использована для делегирования событий. Например, есть большая таблица с большим количеством строк, вместо того чтобы добавлять слушатель событий для всех строк таблицы, можно добавить общий слушатель событий на саму таблицу, что будет гораздо более производительным.
Источник доступен по ссылке.