JavaScript bubbling e capturing


Introdução

Elementos da marcação HTML podem ser aninhados uns dentro de outros, criando-se uma cadeia de elementos-filhos e seus elementos-ancestrais. Neste cenário, quando se atrela um evento JavaScript (por exemplo: o evento clicar) a um determinado elemento E do DOM ele, evento, será disparado mesmo que o clique ocorre em um elemento-filho de E.

 

O nome usado na terminologia JavaScript para descrever esse efeito é event bubbling. Bubbling em tradução livre significa borbulhante, assim em linguagem não técnica podemos dizer que eventos JavaScript borbulham, no sentido de que são disparados por ação em elementos descendentes do elemento a que são atrelados.

No exemplo mostrado a seguir o evento clique dispara o alerta mesmo quando o clique ocorre nos elementos em ou code, embora ele, evento, tenha sido atrelado ao elemento div.

 <div onclick="alert('Evento disparado!')">
 <em>Nesta área experimente clicar no elemento <code>EM</code> aninhado não clicando no <code>DIV</code> e observe que há o disparo do alerta.</em>
 </div>
Nesta área experimente clicar no elemento EM aninhado não clicando no DIV e observe que há o disparo do alerta.

Esse efeito ocorre porque o evento “borbulha” bubbles do elemento mais aninhado para seu elemento-ancestral.

Bubbling

O princípio fundamental do efeito bubbling diz o seguinte:
Depois que um evento é disparado no elemento mais distante de uma cadeia aninhada do DOM ele é disparado em seus elementos ancestrais na ordem crescente de aninhamento.

Observe um exemplo com três elementos div aninhados:

<!DOCTYPE HTML>
<html>
<body>
<link type="text/css" rel="stylesheet" href="example.css">
 <div class="d1">1 <!-- ancestral mais alto -->
 <div class="d2">2
 <div class="d3">3 <!-- descendente mais baixo -->
 </div>
 </div>
 </div>
</body>
</html>

O efeito bubbling da JavaScript faz com que um clique no no div 3 dispare o evento a ele atrelado primeiro no elemento descendente mais baixo 3 (também chamado de target), depois no elemento 2 e finalmente no elemento 1.

um clique no no div 2 dispara o evento a ele atrelado primeiro no elemento 2 (também chamado de target) e depois no elemento 1.

um clique no no div 1 dispara o evento a ele atrelado (também chamado de target) e nada mais.

A ordem de disparo é chamada de bubbling order, pois o evento “borbulha” do elemento descendente mais baixo para seus ancestrais tal como ocorre com uma bolha de ar na água.

O exemplo mostrado a seguir é interativo e demonstra visualmente o efeito bubble. Clique os divs:

Observe a seguir os código do exemplo:

HTML

<div class="d1">1 
 <div class="d2">2
 <div class="d3">3 
 </div> 
 </div>
</div>

CSS

<style>
.d1 {
 background-color: green;
 position: relative;
 width: 150px;
 height: 150px;
 text-align: center;
 cursor: pointer;
}

.d2 {
 background-color: blue;
 position: absolute;
 top: 25px;
 left: 25px;
 width: 100px;
 height: 100px;
}

.d3 {
 background-color: red;
 position: absolute;
 top: 25px;
 left: 25px;
 width: 50px;
 height: 50px;
 line-height: 50px;
}
</style>

JavaScript

<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>

this e event.target

O elemento descendente mais baixo que dispara o evento é chamado de target ou elemento originário.

O navegador Internet Explorer define para target a propriedade srcElement e os navegadores em conformidade com o W3C definem a propriedade event.target.

O código JavaScript cross-browser é mostrado a seguir:

var target = event.target || event.srcElement

Handlers (ações disparadas por eventos) em ancestrais:

  • event.target/srcElement – refere-se ao elemento que origina o evento.
  • this – refere-se ao elemento corrente, aquele para o qual o evento “borbulhou” ou ainda, aquele que dispara o handler.

ordem bubbling do evento

No exemplo interativo mostrado a seguir para cada elemento div foi definido o atributo onclick para disparar um handler cuja saída mostra quem é target e quem é this.

Clique em um div e observe o seguinte:

  • target permanece constante durante todo o processo de bubbling,
  • this modifica-se e é destacado em cor diferente.

Observe a seguir os código do exemplo:

HTML

<div class="d1">1 
 <div class="d2">2
 <div class="d3">3 
 </div> 
 </div>
</div>

CSS

<style>
.d1 {
 background-color: green;
 position: relative;
 width: 150px;
 height: 150px;
 text-align: center;
 cursor: pointer;
}

.d2 {
 background-color: blue;
 position: absolute;
 top: 25px;
 left: 25px;
 width: 100px;
 height: 100px;
}

.d3 {
 background-color: red;
 position: absolute;
 top: 25px;
 left: 25px;
 width: 50px;
 height: 50px;
 line-height: 50px;
}
</style>

JavaScript

<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>

Em navegadores em conformidade com o W3C this também pode ser obtido com uso de event.currentTarget.

Cancelar bubbling

Vimos que o efeito bubbling percorre elementos aninhados no DOM de baixo para cima (do elemento filho para seus ancestrais).

É possível interromper o efeito bubbling antes que ele percorra todos os elementos aninhados.

O código para interromper o efeito em navegadores em conformidade com o W3C é mostrado a seguir:

event.stopPropagation()

Para os IE<9:

event.cancelBubble = true

E, finalmente o código cross-browser:

element.onclick = function(event) {
event = event || window.event // cross-browser event
 if (event.stopPropagation) {
 // opção para poadrões W3C
 event.stopPropagation()
 } else {
 // opção para IE
 event.cancelBubble = true
 }
}

Simplificação do código cross-browser:

event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true)

Se a um elemento for atrelado vários handlers disparados pelo mesmo evento, os handelers serão independentes..

Por exemplo: se em um link existerem dois handelers disparados por click, interromper o efeito bubbling em um dos handelers não interrompe no outro. O navegador não tem qualquer compromisso com a ordem de disparo dos handelers.

Capturing

En todos os navegadores, exceto nos IE<9 os eventos são processados em dois estágios.

No primeiro estágio o evento percorre a cadeia aninhada de cima para baixo (dos elementos ancestrais para os elementos descendentes) – este estágio é chamado capturing. No outro estágio ocorre o efeito bubbles como estudado. Este comportamento é padronizado pelas especificações do W3C.

W3C ordem eventos

Segundo esse modelo o comportamento do evento é:

  1. Captures para baixo – na direção 1 -> 2 -> 3.
  2. Bubbles para cima – na direção 3 -> 2 -> 1.

Todos os métodos de manipulação de eventos simplesmente ignoram a fase caputuring. Para que o evento ocorra na fase capturing declaramos o último argumento método addEventListener como sendo true.

Observe o código mostrado a seguir:

elem.addEventListener( type, handler, phase )
phase = true
O handler dispara na fase capturing.
phase = false
O handler dispara na fase bubbling.

Clique em um dos divs mostrados a seguir para constatar o efeito capturing em ação (exceto IE<9):

A ordem deverá ser 1 -> 2 -> 3.

Observe o código JavaScript desse exemplo:

var divs = document.getElementsByTagName('div')
 for(var i=0; i<divs.length; i++) {
 divs[i].addEventListener("click", highlightThis, true)
 }

Na prática a fase capturing raramente é usada, mas
existem eventos que não “borbulham”, mas honram o efeito capturing. Por exemplo: onfocus e onblur.

No exemplo mostrado a seguir atrelamos handlers a ambos os estágios.

Clique em um dos divs mostrados a seguir para constatar a ordem de processamento dos eventos (exceto IE<9):

A ordem deverá ser 1 -> 2 -> 3 -> 3 -> 2 -> 1.

Observe o código JavaScript desse exemplo:

 

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)
}

Sumário

  • Eventos primeiramente são captured para baixo e depois bubble para cima. Os IE<9 honram apenas o efeito bubble.
  • Todos os handlers disparam no efeito bubbling exceto quando o último argumento do método addEventListener for declarado true, aliás essa é a única maneira de disparar o evento na fase capturing.
  • Bubbling e capturing podem ser cancelados no IE com uso de event.cancelBubble=true (IE) ou com event.stopPropagation() para os demais navegadores.

Tags: html

Total de comentários: 0
avatar