Мастер-классы по Javascript Екатеринбург Ростов-на-Дону Москва Узнать больше...
Содержание (скрыть) Содержание (показать)

Всплытие и перехват

  1. Всплытие
    1. Текущий элемент, this
    2. Целевой элемент, event.target
    3. Прекращения всплытия
  2. Стадия перехвата
  3. Итого

Элементы 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>, вызывая все обработчики на своем пути.

Но любой промежуточный обработчик может решить, что событие полностью обработано, и остановить всплытие.

Сценарий, при котором это может быть нужно:

  1. На странице по правому клику мышью показывается, при помощи JavaScript, специальное контекстное меню
  2. На странице также есть таблица, которая показывает меню, но другое, своё.
  3. В случае правого клика по таблице, её обработчик покажет меню и остановит всплытие, чтобы меню уровня страницы не показалось.

Код для остановки всплытия различается между 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).

W3C events order

В соответствии с этой моделью, событие:

  1. Может быть перехвачено по дороге вниз - через 1 -> 2 -> 3.
  2. Всплывает вверх - через 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.

Комментарии

  1. Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  2. Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
  3. Комментарии без смысла, с рекламой или не о статье вообще - удаляются.
Наверх

Содержание

Реклама

Нашли опечатку?

Нашли опечатку на сайте? Что-то кажется странным?
Выделите соответствующий текст и нажмите Ctrl+Enter!

Последние Комментарии

Помоги другим!

Помоги другим узнать о хорошей статье!