Ошибки

JavaScript - Всплытие события. Продвинутая работа с объектом Event на JavaScript Как прервать всплытие события

На этом уроке мы познакомимся с таким понятием как всплытие события, а также рассмотрим, как его можно прервать. Кроме этого выясним, какие ещё этапы (фазы) проходит событие, перед тем как начать всплывать.

Всплытие события

Если у некоторого элемента возникает событие, то оно начинает "всплывать", т.е. возникает у родителя, потом у прародителя и т.д.

Из этого следует, что событие, которое сгенерировал некоторый элемент, можно перехватить с помощью обработчика на родителе, прародителе и т.д.

Всплытие события (пузырька) продемонстрируем на следующем примере:

Заголовок

Некоторый очень важный текст

Раздел

Некоторый текст

Остальной текст

Напишем небольшой скрипт, с помощью которого добавим обработчик события " click " для всех элементов страницы, а также для объектов document и window .

document.addEventListener("DOMContentLoaded", function() { var allElements = document.getElementsByTagName("*"); for (var i=0; i < allElements.length; i++) { allElements[i].addEventListener("click",function() {console.log(this.tagName);},false); }; document.addEventListener("click",function() {console.log(this);},false); window.addEventListener("click",function() {console.log(this);},false); });

Создадим HTML-страницу и вставим в неё вышеприведённый HTML код. Сценарий, написанный на языке JavaScript, вставим перед закрывающим тегом body . После этого откроем только что созданную страницу в веб-браузере, нажмём клавишу F12 и перейдём в консоль. Теперь нажмём левой кнопкой мышкой в области, принадлежащей элементу strong , и посмотрим, как событие будет всплывать.

Как прервать всплытие события

Всплытие события (пузырька) можно прервать. В этом случае у вышестоящих (родительских) элементов, данное событие вызвано не будет. Метод, который предназначен для прекращения всплытия события (пузрька) называется stopPropagation() .

Например, изменим наш вышеприведённый пример таким образом, чтобы событие не всплывало выше body: document.addEventListener("DOMContentLoaded", function() { var allElements = document.getElementsByTagName("*"); for (var i=0; i

Бесспорно всплытие - это очень удобно и архитектурно прозрачно. Не прекращайте его без явной нужды.

Получение элемента, который вызвал обработчик

Для того чтобы получить DOM-элемент (объект), который вызвал обработчик события, необходимо использовать ключевое слово this . Данное ключевое слово (this) доступно в обработчике только в том случае, если Вы подписались на событие с помощью JavaScript.

Например, выведем в консоль id элемента, который вызвал обработчик события:

Var myP = document.getElementById("myP"); myP.addEventListener("click",function(){ //получим DOM-элемент, который вызвал обработчик события - this //получим его id и выведем его в консоль console.log(this.id); });

Для получения текущего элемента также можно использовать свойство currentTarget (event.currentTarget).

Этапы (фазы) прохода события

Перед тем как события начинает всплывать (этап всплытия), оно предварительно проходит ещё 2 этапа:

  • 1 этап - это этап погружения до элемента, сгенерировавшего событие. Т.е. на данном этапе происходит движение сверху вниз, т.е. от объекта window до элемента. Также данный этап ещё называют этапом перехвата.
  • 2 этап - это этап достижение цели, т.е. элемента (объекта), сгенерировавшего событие.

С учётом всех этапов, которые проходит событие, получается следующая картина:

Изменим сценарий вышеприведённого примера следующим образом:

Document.addEventListener("DOMContentLoaded", function() { var allElements = document.getElementsByTagName("*"); for (var i=0; i

Третий параметр методов addEventListener и removeEventListener определяет этап, на котором будет поймано событие. Если данный параметр имеет значение true , то событие будет перехватываться на стадии погружения (перехвата) события. А если параметр имеет значение false , то событие будет перехватываться на этапе всплытия. Для обработки события на самой цели, можно использовать метод addEventListener как со значением false , так и со значением true .

Внимание: на стадии погружения (перехвата), события могут перехватывать только обработчики, добавленные с помощью метода addEventListener() . Обработчики, добавленные с помощью других способов (атрибута HTML или через JavaScript с помощью свойства on[событие]) могут перехватывать события только на стадии всплытия.

Получение элемента, который сгенерировал событие

Для того чтобы получить целевой элемент, т.е. элемент, который сгенерировал событие, необходимо использовать свойство target (event.target).

Рассмотрим вышеприведённый пример, в котором изменим содержимое элемента script на следующее:

Document.addEventListener("DOMContentLoaded", function() { var elementBody = document.body; elementBody.addEventListener("click",function(){ console.log(this.tagName + " - элемент, который вызвал обработчик"); console.log(event.currentTarget.tagName + " - элемент, который вызвал обработчик"); console.log(event.target.tagName + " - элемент, который сгенерировал событие"); },false); });

Продемонстрируем наш пример, кликнув левой кнопкой мыши в области, принадлежащей элементу strong:

Здравствуйте! В этом уроке я хочу рассказать о таком важном понятии как всплытие и перехват событий. Всплытие это такое явление при котором, если вы щелкаете по дочернему элементу, то событие распространяется и на его родитель.

Бывает очень полезно при обработке больших вложенных списков или таблиц, чтобы не назначать каждому элементу обработчик события можно назначить один обработчик на родительский элемент, а событие уже будет распространяться на все вложенные элементы в родитель. Давайте рассмотрим на примере.

Этот обработчик для сработает, если вы кликните по вложенному тегу или :

Кликните на EM, сработает обработчик на DIV

Как видите при клике на вложенном элементе em срабатывает обработчик на div. Почему так происходит? Читайте дальше и узнаете.

Всплытие

Итак основной принцип всплытия:

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

Например, пусть имеется 3 вложенных элемента FORM > DIV > P, с обработчиком собьытия на каждом:

body * { margin: 10px; border: 1px solid blue; } FORM DIV

Всплытие гарантирует, что клик по внутреннему элементу

Вызовет обработчик click (если он конечно есть) сначала на самом

Такой процесс называется всплытием, потому что события как бы «всплывают» от внутреннего элемента вверх через своих родителей, подобно тому, как всплывает пузырек воздуха в воде, поэтому можно встретить еще определение бабблинг ну это просто от английского слова bubbling — всплывать.

Доступ к целевому элементу event.target

Для того, чтобы узнать на каком именно элементе мы поймали то или иное событие и существует метод event.target. (о объекте event читайте ).

  • event.target – это собственно исходный элемент, на котором и произошло событие.
  • this – это всегда текущий элемент, до которого дошло всплытие, и на нём сейчас выполняется обработчик.

Например, если у вас установлен только один обработчик form.onclick, то он и «поймает» все клики внутри формы. При этом где бы ни был клик внутри – он все равно всплывёт до элемента , на котором и сработает уже обработчик.

При этом:

  • this (=event.currentTarget) всегда будет сама форма, так как обработчик сработал именно на ней.
  • event.target будет содержать ссылку на конкретный элемент внутри формы, самый вложенный, на котором произошёл клик.

В принципе this может совпадать с event.target если кликнули по форме и в форме больше нет никаких элементов.

Прекращение всплытия

Как правило всплытие события идет прямо наверх и доходит до корневого объекта window.

Но есть возможность остановить всплытие на каком-то промежуточном элементе.

Для того, чтобы остановить всплытие надо вызвать метод event.stopPropagation().

Рассмотрим пример, при клике на кнопку обработчик body.onclick не сработает:

Кликни меня

Если у элемента установлено несколько обработчиков на одно и тоже событие, то даже при прекращении всплытия все они будут выполнены.

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

Для того, чтобы остановить обработку на текщем элементе, браузеры поддерживают метод event.stopImmediatePropagation(). Этот метод не только предотвратит всплытие, но и останавит обработку событий на текущем элементе.

Погружение

В стандарте, кроме «всплытия» событий, есть ещё и «погружение».

Погружение в отличие от всплытия менее востребованно, но все же знать о нем будет полезно.

Итак имеются 3 стадии прохода события:

  • Событие идет сверху вниз. Эта стадия называется «стадия перехвата».
  • Событие достигло конкретного элемента. Это – «стадия цели».
  • После всего событие начинает всплывать. Это – «стадия всплытия».
  • В стандарте это продемонстрировано так:

    Таким образомь, при клике на TD событие будет путешествовать по цепочке родителей сначала вниз к элементу («погружается»), а потом наверх («всплывает»), по пути соотвественно задействуя обработчики.

    Выше я писал только о всплытии, потому что собственно другие стадии, не используются и проходят незаметно для нас.

    Обработчики ничего не знают о стадии перехвата, а начинают работать со всплытия.

    А Чтобы поймать событие на стадии перехвата, как раз и нужно использовать :

    • Аргумент true, то событие будет перехвачено по дороге вниз.
    • Аргумент false, то событие будет поймано при всплытии.
    Примеры

    В примере на , ,

    Стоят те же обработчики, что и раньше, но на сей раз – на стадии погружения. Ну а чтобы увидеть перехват в действии, кликните в нём на элементе

    Обработчики сработают в порядке «сверху-вниз»: FORM → DIV → P.

    JS-код здесь такой:

    Var elems = document.querySelectorAll("form,div,p"); // на каждый элемент повесим обработчик на стадии перехвата for (var i = 0; i < elems.length; i++) { elems[i].addEventListener("click", highlightThis, true); }


    Никто вам не мешает назначить обработчики для обеих стадий, вот так:

    Var elems = document.querySelectorAll("form,div,p"); for (var i = 0; i < elems.length; i++) { elems[i].addEventListener("click", highlightThis, true); elems[i].addEventListener("click", highlightThis, false); }

    Кликните по внутреннему элементу

    Чтобы увидеть порядок прохода события:
    Должно быть FORM → DIV → P → P → DIV → FORM. Заметим, что элемент

    Будет участвовать в обоих стадиях.

    Итоги
    • При наступлении события – элемент, на котором произошло событие, помечается как event.target.
    • Событие сначала двигается вниз от корня документа к event.target, по пути вызывая обработчики, поставленные через addEventListener(…., true).
    • Событие двигается от event.target вверх до начала документа, по пути оно вызывает обработчики, поставленные через addEventListener(…., false).

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

    • event.target – самый глубокий элемент, на котором собственно и произошло событие.
    • event.currentTarget (=this) – элемент, на котором в данный момент сработал самобработчик (до которого «дошло» событие).
    • event.eventPhase – на какой фазе сработал обработчик события (погружение =1, всплытие = 3).

    Всплытие можно остановить вызовом метода event.stopPropagation(), но делать это не рекомендуется, поскольку событие может вам понадобится для самых неожиданныъ целей.

    Перехват события

    Одна из важных особенностей языка - перехват события. Если кто-то, к примеру, щелкает на кнопке, то вызывается программа обработки события onClick, соответствующая этой кнопке. С помощью обработки событий Вы можете добиться того, чтобы объект, соответсвующий вашему окну, документу или слою, перехватывал и обрабатывал событие еще до того, как для этой цели объектом указанной кнопки будет вызван обработчик событий. Точно так же объект вашего окна, документа или слоя может обрабатывать сигнал о событии еще до того, как он достигает своего обычного адресата.
    Чтобы увидеть, для чего это может пригодиться, давайте рассмотрим следующий пример:



    window.onclick= handle;

    function handle(e) {­
    alert("Объект window перехватывает это событие!");
    return true; // т.е. проследить ссылку
    }




    Click on this link

    Как видно, мы не указываем программы обработки событий в тэге . Вместо этого мы пишем

    window.captureEvents(Event.CLICK);

    с тем, чтобы перехватить событие Click объектом window. Обычно объект window не работает с событием Click . Однако, перехватив, мы затем его переадресуем в объект window. Заметим, что в Event.CLICK фрагмент CLICK должен писаться заглавными буквами. Если же Вы хотите перехватывать несколько событий, то Вам следует отделить их друг от друга символами |. Например:

    window.captureEvents(Event.CLICK | Event.MOVE);

    Помимо этого в функции handle() , назначенной нами на роль обработчика событий, мы пользуемся инструкцией return true; . В действительности это означает, что браузер должен обработать и саму ссылку, после того, как завершится выполнение функции handle() . Если же Вы напишете вместо этого return false; , то на этом все и закончится.

    Если теперь в тэге Вы зададите программу обработки события onClick , то поймете, что данная программа при возникновении данного события вызвана уже не будет. И это не удивительно, поскольку объект window перехватывает сигнал о событии еще до того, как он достигает объекта link. Если же Вы определите функцию handle() как

    function handle(e) {­
    alert("The window object captured this event!");
    window.routeEvent(e);
    return true;
    }

    то компьютер будет проверять, определены ли другие программы обработки событий для данного объекта. Переменная e - это наш объект Event, передаваемый функции обработки событий в виде аргумента.

    Кроме того, Вы можете непосредственно послать сигнал о событии какому-либо объекту. Для этого Вы можете воспользоваться методом handleEvent() . Это выглядит следующим образом:


    window.captureEvents(Event.CLICK);

    window.onclick= handle;

    function handle(e) {­
    document.links.handleEvent(e);
    }


    "Кликните" по этой ссылке

    Вторая ссылка

    Все сигналы о событиях Click, посылаются на обработку по второй ссылке - даже если Вы вовсе и не щелкнули ни по одной из ссылок!

    Следующий скрипт демонстрирует, как Ваш скрипт может реагировать на сигналы о нажатии клавиш. Нажмите на какую-либо клавишу и посмотрите, как работает этот скрипт.


    window.captureEvents(Event.KEYPRESS);

    window.onkeypress= pressed;

    function pressed(e) {­
    alert("Key pressed! ASCII-value: " + e.which);
    }

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

    Например, есть 3 вложенных элемента FORM > DIV > P , с обработчиком на каждом:

    Код: FORM
    DIV

    Всплытие гарантирует, что клик по внутреннему

    Вызовет обработчик onclick (если есть) сначала на самом

    Поэтому если в примере выше кликнуть на P, то последовательно выведутся alert: p → div → form.

    Этот процесс называется всплытием, потому что события «всплывают» от внутреннего элемента вверх через родителей, подобно тому, как всплывает пузырек воздуха в воде.

    event.target

    На каком бы элементе мы ни поймали событие, всегда можно узнать, где конкретно оно произошло.
    Самый глубокий элемент, который вызывает событие, называется «целевым» или «исходным» элементом и доступен как event.target.

    Отличия от this (=event.currentTarget):

    • event.target – это исходный элемент, на котором произошло событие, в процессе всплытия он неизменен.
    • this – это текущий элемент, до которого дошло всплытие, на нём сейчас выполняется обработчик.

    Например, если стоит только один обработчик form.onclick, то он «поймает» все клики внутри формы. Где бы ни был клик внутри – он всплывёт до элемента , на котором сработает обработчик.

    При этом:

    • this (=event.currentTarget) всегда будет сама форма, так как обработчик сработал на ней.
    • event.target будет содержать ссылку на конкретный элемент внутри формы, самый вложенный, на котором произошёл клик.

    Возможна и ситуация, когда event.target и this – один и тот же элемент, например если в форме нет других тегов и клик был на самом элементе .

    Прекращение всплытия события

    Всплытие идёт прямо наверх. Обычно событие будет всплывать наверх и наверх, до элемента , а затем до document , а иногда даже до window , вызывая все обработчики на своем пути.

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

    Для остановки всплытия нужно вызвать метод event.stopPropagation() .

    Например, здесь при клике на кнопку обработчик body.onclick не сработает:

    Код:
    Кликни меня

    Перехват события. event.stopImmediatePropagation()

    Если у элемента есть несколько обработчиков на одно событие, то даже при прекращении всплытия все они будут выполнены.

    То есть, stopPropagation препятствует продвижению события дальше, но на текущем элементе все обработчики отработают.

    Для того, чтобы полностью остановить обработку, современные браузеры поддерживают метод event.stopImmediatePropagation() . Он не только предотвращает всплытие, но и останавливает обработку событий на текущем элементе.

    Отличия IE8-

    Чтобы было проще ориентироваться, я собрал отличия IE8-, которые имеют отношение ко всплытию, в одну секцию.

    Их знание понадобится, если вы решите писать на чистом JS, без фреймворков и вам понадобится поддержка IE8-.

    Нет свойства event.currentTarget

    Обратим внимание, что при назначении обработчика через on свойство у нас есть this , поэтому event.currentTarget , как правило, не нужно, а вот при назначении через attachEvent обработчик не получает this , так что текущий элемент, если нужен, можно будет взять лишь из замыкания.

    Вместо event.target в IE8- используется event.srcElement

    Если мы пишем обработчик, который будет поддерживать и IE8- и современные браузеры, то можно начать его так:

    Код: elem.onclick = function(event) {
    event = event || window.event;
    var target = event.target || event.srcElement;

    // ... теперь у нас есть объект события и target
    ...
    }

    Для остановки всплытия используется код event.cancelBubble=true

    Кросс-браузерно остановить всплытие можно так:

    Код: event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true);

    Итого
    • При наступлении события – элемент, на котором оно произошло, помечается как «целевой» (event.target).
    • Далее событие сначала двигается вниз от корня документа к event.target, по пути вызывая обработчики, поставленные через addEventListener(...., true).
    • Далее событие двигается от event.target вверх к корню документа, по пути вызывая обработчики, поставленные через on* и addEventListener(...., false).
    • event.target – самый глубокий элемент, на котором произошло событие.
    • event.currentTarget (=this) – элемент, на котором в данный момент сработал обработчик (до которого «доплыло» событие).
    • event.eventPhase – на какой фазе он сработал (погружение =1, всплытие = 3).