Элементы DOM могут быть вложены друг в друга. И каким-то образом, обработчик, привязанный к родителю срабатывает, даже есть вы кликнули по потомку.
Например, обработчик для DIV сработает, если вы кликните по вложенному тегу EM или CODE:
<div onclick="alert('Обработчик для Div сработал!')">
<em>Кликните на <code>EM</code>, сработает обработчик на <code>DIV</code></em>
</div>
Это происходит потому, что событие всплывает.
Всплытие
Основной принцип всплытия:
После того, как событие сработает на самом вложенном элементе, оно также сработает на родителях, вверх по цепочке вложенности.
Например, есть 3 вложенных блока:
<!DOCTYPE HTML>
<html>
<body>
<link type="text/css" rel="stylesheet" href="example.css">
<div class="d1">1 <!-- внешний (topmost) -->
<div class="d2">2
<div class="d3">3 <!-- внутренний (innermost) -->
</div>
</div>
</div>
</body>
</html>
Всплытие гарантирует, что клик по внутреннем div 3 вызовет событие onclick сначала на внутреннем элементе 3, затем на элементе 2 и в конце концов на элементе 1.

Этот процесс называется всплытием, потому что события «всплывают» от внутреннего элемента вверх через родителей, подобно тому, как всплывает пузырек воздуха в воде.
Текущий элемент, this
Элемент, на котором сработал обработчик, доступен через this.
Например, повесим клик по каждому DIV функцию highlight, которая подсвечивает текущий элемент:
<!DOCTYPE HTML>
<html>
<body>
<link type="text/css" rel="stylesheet" href="example.css">
<div class="d1" onclick="highlight(this)">1
<div class="d2" onclick="highlight(this)">2
<div class="d3" onclick="highlight(this)">3
</div>
</div>
</div>
<script>
function highlight(elem) {
elem.style.backgroundColor='yellow'
alert(elem.className)
elem.style.backgroundColor = ''
}
</script>
</body>
</html>
Показать в отдельном окне
Кликните по внутреннему DIV‘у, чтобы увидеть всплытие:
Во всех браузерах, кроме IE<9, существует свойство event.currentTarget, аналогичное this.
В IE<9 это свойство отсутствует. Также IE<9 не предоставляет this при назначении через attachEvent.
Целевой элемент, event.target
Самый глубокий элемент, который вызывает событие называется целевым, или исходным элементом.
В IE<9 он доступен как event.srcElement, остальные браузеры используют event.target. Кроссбраузерное решение выглядит так:
var target = event.target || event.srcElement;
event.target/srcElement- означает исходный элемент, на котором произошло событие.this- текущий элемент, до которого дошло всплытие и который запускает обработчик.

В примере каждый DIV имеет обработчик onclick, который выводит и target и this.
Кликните по какому-нибудь блоку, например самому вложенному:
Обратите внимание:
event.targetне изменяется по мере всплытия события,- ..а вот
thisизменяется и подсвечивается.
<!DOCTYPE HTML>
<html>
<body>
<link type="text/css" rel="stylesheet" href="example.css">
<div class="d1">1
<div class="d2">2
<div class="d3">3
</div>
</div>
</div>
<script>
var divs = document.getElementsByTagName('div')
for(var i=0; i<divs.length; i++) {
divs[i].onclick = function(e) {
e = e || event
var target = e.target || e.srcElement
this.style.backgroundColor='yellow'
alert("target = "+target.className+", this="+this.className)
this.style.backgroundColor = ''
}
}
</script>
</body>
</html>
Показать в отдельном окне
Прекращения всплытия
Всплытие идет прямо наверх. Обычно оно будет всплывать до <HTML>, вызывая все обработчики на своем пути.
Но любой промежуточный обработчик может решить, что событие полностью обработано, и остановить всплытие.
Сценарий, при котором это может быть нужно:
- На странице по правому клику мышью показывается, при помощи JavaScript, специальное контекстное меню
- На странице также есть таблица, которая показывает меню, но другое, своё.
- В случае правого клика по таблице, её обработчик покажет меню и остановит всплытие, чтобы меню уровня страницы не показалось.
Код для остановки всплытия различается между IE<9 и остальными браузерами:
- Стандартный код — это вызов метода:
event.stopPropagation()
- Для IE<9 — это назначение свойства:
event.cancelBubble = true
Кросс-браузерное решение:
element.onclick = function(event) {
event = event || window.event; // Кроссбраузерно получить событие
if (event.stopPropagation) { // существует ли метод?
// Стандартно:
event.stopPropagation();
} else {
// Вариант IE
event.cancelBubble = true;
}
}
Есть еще вариант записи в одну строчку:
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true)
Если у элемента есть несколько обработчиков на одно событие, то даже при прекращении всплытия все они будут выполнены.
Например, если на ссылке есть два onclick-обработчика, то остановка всплытия для одного из них никак не скажется на другом. Это логично, учитывая то, что , как мы уже говорили ранее, браузер не гарантирует взаимный порядок выполнения этих обработчиков. Они полностью независимы, и из одного нельзя отменить другой.
Стадия перехвата
Во всех браузерах, кроме IE<9, есть две стадии прохода события.
Перед тем, как всплыть, событие сначала идет сверху вниз. Эта стадия называется стадия перехвата (capturing stage).

В соответствии с этой моделью, событие:
- Может быть перехвачено по дороге вниз - через 1 -> 2 -> 3.
- Всплывает вверх - через 3 -> 2 -> 1.
Все методы обработки событий, кроме addEventListener, игнорируют стадию перехвата.
Единственный способ поймать событие на стадии перехвата — это addEventListener с последним аргументом true.
elem.addEventListener( type, handler, *!*true*/!* );
Смысл последнего аргумента:
true- Обработчик ставится на стадию захвата.
false- Обработчик ставится на стадию всплытия.
Кликните на div ниже, чтобы увидеть захват в действии (не работает в IE<9):
Должно быть 1 -> 2 -> 3.
<!DOCTYPE HTML>
<html>
<body>
<link type="text/css" rel="stylesheet" href="example.css">
<div class="d1">1
<div class="d2">2
<div class="d3">3
</div>
</div>
</div>
<script>
var divs = document.getElementsByTagName('div');
// на каждый DIV повесить обработчик на стадии захвата
for(var i=0; i<divs.length; i++) {
divs[i].addEventListener("click", highlightThis, true);
}
function highlightThis() {
this.style.backgroundColor='yellow';
alert(this.className);
this.style.backgroundColor = '';
}
</script>
</body>
</html>
Показать в отдельном окне
Теперь давайте назначим обработчики для обеих стадий.
Кликните по div, чтобы увидеть порядок исполнения события (не работает IE<9):
Должно быть 1 -> 2 -> 3 -> 3 -> 2 -> 1.
JavaScript-код примера:
var divs = document.getElementsByTagName('div')
for (var i=0; i<divs.length; i++) {
divs[i].addEventListener("click", highlightThis, true);
divs[i].addEventListener("click", highlightThis, false);
}
Посмотреть в песочнице:: tutorial/browser/events/bubbling/both/index.html.
На практике, стадия захвата редко используется. Но есть события, которые можно только захватить.
Например, таково событие фокусировки на элементе.
Итого
- Событие идет сначала сверху вниз к целевому элементу (стадия захвата), затем всплывает снизу вверх. В IE<9 стадия захвата отсутствует.
- Все способы добавления обработчика используют стадию всплытия, кроме
addEventListenerс последним аргументомtrue. - Всплытие/захват можно остановить с помощью вызова
event.stopPropagation(). В IE<9 нужно использовать для этогоevent.cancelBubble=true.
Комментарии
- Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
- Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
- Комментарии без смысла, с рекламой или не о статье вообще - удаляются.