Logo do siteSilhueta da face do Maujor seguida do texto Maujor o dinossauro das CSS

Menu hamburger Três barras horizontais na cor laranja destinadas a ser clicada para abrir o menu menu do site

JavaScript bubbling e capturing

Publicado em: 2014-09-23 — 23.192 visualizacoes

capa

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.

Ordem de bubbling

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

Desenvolvimento com Padrões Web? Adquira os livros do Maujor
Visite o site dos livros.

Esta matéria foi publicada em: 2014-09-23 (terça-feira). Subscreva o feed RSS 2.0 para comentários.
Comente abaixo, ou link para https://www.maujor.com/blog/2014/09/23/javascript-bubbling-e-capturing/trackback no seu site.

8 comentários na matéria: “JavaScript bubbling e capturing”

  1. RezendeNo Gravatar disse:

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

  2. Rezende JuniorNo Gravatar disse:

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

  3. LucasNo Gravatar disse:

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

  4. julia valeryNo Gravatar disse:

    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

  5. EdengbNo Gravatar disse:

    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

  6. CesarNo Gravatar disse:

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

  7. Tita CarréNo Gravatar disse:

    Olá, adorei as dicas, at+
    Tita Carré

  8. James ClébioNo Gravatar disse:

    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.

Comentário:





Teclar "Enter" cria um novo parágrafo. Teclar "Shift + Enter" causa uma quebra de linha.

Subscribe without commenting

topo