Categories: javascripttodas

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

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.

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

Maujor

View Comments

  • Seu blog é excelente. Estou aprendendo um pouco sobre programação e a sua página é uma de minhas fontes principais para tirar duvidas.

  • Parabéns! ótimas dicas, estou começando a apreender um pouco sobre programação, tentando melhorar meu site! obrigado, dicas bem uteis!

  • Seu blog é excelente. Estou aprendendo um pouco sobre programação e a sua página é uma de minhas fontes principais para tirar duvidas.

  • Estou muito grato pelo que você compartilhe este artigo, eu incluído na categoria de iniciantes para jogar assuntos javascrip ou html, onde eu poderia aprender ?? se for permitido , existe um site on-line para aprender o básico sobre html ?? mendigar em responder , estou muito ansioso para ser capaz de dominar as ligações do HTML ou qualquer javascrip , obrigado
    Video Bokep

  • Assim como o Cesar não fico comentando, mas hj concordo com ele... estou aprendendo com o livro Guia Definitivo e sou iniciante, mas sempre vejo seus conteúdo quando preciso. xD

  • Muito bom! Sou Programador Web e sempre acompanho os posts deste blog sem falar no site do maujor.com que para mim é essencial uma passada por lá mesmo que seja rápida! Aqui sempre há algo de novo e interessante para se aprender. Eu sempre que posso passo por aqui mas somente desta vez resolvi comentar. Este post me orientou bastante em trabalhos que eu estava desenvolvendo. Este site é e sempre será um incentivo para eu continuar crescendo e aprendendo constantemente. Parabéns a todos pela rica iniciativa de partilhar conhecimentos de qualidade de forma didática, clara e objetiva!!!

  • Excelente post! Eu mesmo já tive essa dúvida e sempre vejo gente procurando saber: como cancelar evento de clique atrelado a um elemento pai durante o clique de um elemento filho... Enfim, esse artigo esclarece bem a questão.

Share
Published by
Maujor

Recent Posts

Teste seu conhecimento #20

Em 2006 comecei a publicar nesse blog uma série de desafios CSS que consistiam em…

7 anos ago

Teste seu conhecimento #19

Há muito tempo que eu não publico um "Teste seu conhecimento". Esta semana, revendo algumas…

9 anos ago

HTML5 – W3C versus WHATWG

HTML5? Web universal? É comum eu me deparar com dúvidas sobre a HTML5 não só…

10 anos ago

Seria esse o futuro das imagens responsivas?

Quem é Tab Atkins Jr? Tab Atkins Jr, um desenvolvedor americano, trabalhou durante muitos anos…

10 anos ago

BrazilJS Conf 2013

Pessoal, a BrazilJS Conf 2013 disponibilizou para o Maujor cupons de desconto para serem oferecidos…

11 anos ago

Efeito CSS “Dinossauro”

Ultimamente recebi vários emails de meus leitores perguntando com criar o efeito que existe no…

11 anos ago