Перейти к основному содержимому

Порядок выполнения событий в DOM

· 2 мин. чтения

Столкнулся с проблемой в своём календарике - есть два элемента, один из которых позиционируется абсолютно на весь экран, полупрозрачная затемняющая занавеска а второй - форма. Вы наверняка видели такие решения при показе картинок в lightbox или аутидентификации на habrahabr'е..

<div id='form_bg' onclick="$('form_bg').hide();$('form').hide();"> <div id='form'></div> </div>

Проблема в том, что при любом нажатии на внутренний элемент, событие автоматом вызывается на родительском элементе. Это заставило меня задуматься как над решением, так и над теорией как браузеры работают.


Две с половиной модели

Как оказывается, существует две модели передачи событий в объектно-ориентированной иерархии

  • Пузырьковый метод (bubbling), когда событие возникает внутри и затем передаётся родительским элементам наружу. MS Internet Explorer, Opera, Firefox
  • Захват события (capturing), событие обрабатывается сначала у родителей, а потом проникает глубже. Opera, Firefox

Консорциум W3C благоразумно решили что разработчикам может быть удобно в любую сторону направлять события (event propagation), поэтому по стандарту две модели объединены - событие сначала захватывается, а потом возвращается как пузырёк.

Таким образом разработчик может привязать вызываемый метод к фазе события:

$('form_bg').addEventListener('click',hideBackground,true); // true - говорит о фазе capturing $('form').addEventListener('click',function(){},false); // false - говорит о фазе bubbling
Получается что при клике на form_bg происходит сразу hideBackground, и form на фазе capturing не вызывается, затем возвращаясь, в фазе bubbling вызывается анонимная функция. 

Традиции по умолчанию

Модель W3C приятна, но по умолчанию по всей видимости из-за IE, обычная регистрация события подразумевает bubbling фазу, т.е. изнутри наружу. А это в свою очередь значит, что если я явно укажу на родительский элемент:

$('form_bg').onclick = hideBackground; // или как выше - в <div id='form_bg' onclick=..

То любое событие без остановки вызывает у всех родительских элементов обработку onclick-события. Есть у них оно или нет, вплоть до корня - document.

Заплыв без пузырьков

В кросс-браузерном варианте для остановки распространения обработки события к родительским элементам, надо поменять параметр cancelBubble на true (для IE) и вызывать функцию stopPropagation (W3C модель):

function hideBackground(e){ if (!e) var e = window.event; e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); }

Теперь мне интересно как устроена модель обработки событий в других языках и платформах - Java, NET, Flash..

Написано по мотивам статьи "Event order".