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>
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.
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
Em 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.
Segundo esse modelo o comportamento do evento é:
- Captures para baixo – na direção 1 -> 2 -> 3.
- 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.
Crédito: http://learn.javascript.ru/
Publicado segundo os termos da licença CC BY-NC-SA.