Não foi possível entender o parâmetro useCapture em addEventListener


290

Eu li o artigo em https://developer.mozilla.org/en/DOM/element.addEventListener, mas não consigo entender o useCaptureatributo. Definição existe:

Se verdadeiro, useCapture indica que o usuário deseja iniciar a captura. Após o início da captura, todos os eventos do tipo especificado serão despachados para o ouvinte registrado antes de serem despachados para qualquer EventTargets abaixo dele na árvore DOM. Eventos que estão subindo pela árvore não acionam um ouvinte designado para usar a captura.

Nesse evento pai de código é acionado antes do filho, portanto, não consigo entender seu comportamento. O objeto de documento tem usecapture true e child div tem usecapture definido false e a usecapture é seguida. Portanto, por que a propriedade document é preferida a child.

function load() {
  document.addEventListener("click", function() {
    alert("parent event");
  }, true);

  document.getElementById("div1").addEventListener("click", function() {
    alert("child event");
  }, false);
}
<body onload="load()">
  <div id="div1">click me</div>
</body>

Respostas:


350

Os eventos podem ser ativados em duas ocasiões: No início ("captura") e no final ("bolha"). Os eventos são executados na ordem em que são definidos. Digamos, você define 4 ouvintes de eventos:

window.addEventListener("click", function(){console.log(1)}, false);
window.addEventListener("click", function(){console.log(2)}, true);
window.addEventListener("click", function(){console.log(3)}, false);
window.addEventListener("click", function(){console.log(4)}, true);

As mensagens de log aparecerão nesta ordem:

  • 2(definido primeiro, usando capture=true)
  • 4(segundo definido usando capture=true)
  • 1(primeiro evento definido com capture=false)
  • 3(segundo evento definido com capture=false)

49
Ordem de execução é não garantida : no specification is made as to the order in which they will receive the event with regards to the other EventListeners on the EventTarget. Eu não testei todos os navegadores; portanto, eles podem implementá-lo da mesma maneira. Os eventos de captura, no entanto, serão feitos antes dos eventos de não captura.
Beatgammit 20/08/2013

47
@tjameson A ordem de execução é garantida no sucessor da especificação DOM2, eventos DOM3 : "a implementação deve determinar os ouvintes de eventos candidatos do destino atual . Essa deve ser a lista de todos os ouvintes de eventos que foram registrados no destino atual em seus ordem de registro ".
Rob W

1
então isso basicamente tem a ver com a ordem do evento, eu acho
slier

1
@slier, sim, a ordem em que vários manipuladores para o mesmo evento são executados.
JMD

6
Não faço ideia por que isso é a resposta aceita desde afaik, capturando e borbulhando fala sobre o comportamento de propagação e não sobre a ditar a ordem de execução de múltiplos, manipuladores de eventos adjacentes
georaldc

272

Acho que este diagrama é muito útil para entender as fases de captura / destino / bolha: http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

Abaixo, o conteúdo extraído do link.

Fases

O evento é despachado seguindo um caminho da raiz da árvore para este nó de destino. Em seguida, ele pode ser manipulado localmente no nível do nó de destino ou nos ancestrais de qualquer destino mais alto na árvore. O envio de eventos (também chamado de propagação de eventos) ocorre em três fases e na seguinte ordem:

  1. A fase de captura: o evento é despachado para os ancestrais do destino da raiz da árvore para o pai direto do nó de destino.
  2. A fase de destino: o evento é despachado para o nó de destino.
  3. A fase de bolha: o evento é despachado para os ancestrais do destino, desde o pai direto do nó de destino até a raiz da árvore.

representação gráfica de um evento despachado em uma árvore DOM usando o fluxo de eventos DOM

Os ancestrais do destino são determinados antes do envio inicial do evento. Se o nó de destino for removido durante o envio, ou o ancestral de um destino for adicionado ou removido, a propagação do evento sempre será baseada no nó de destino e nos ancestrais do destino determinados antes do envio.

Alguns eventos podem não necessariamente realizar as três fases do fluxo de eventos do DOM, por exemplo, o evento pode ser definido apenas para uma ou duas fases. Como exemplo, os eventos definidos nesta especificação sempre realizarão as fases de captura e destino, mas alguns não a fase de bolha ("eventos de bolha" versus "eventos de não bolha", consulte também o atributo Event.bubbles).


1
diagrama muito bom!
7776 Alex

1
E os filhos do nó de destino? Quando eles recebem o evento?
Aurimas

A raiz da árvore é realmente Window, em vez de document, porque documenté filha Window?
stackjlei

Este diagrama ajudou muito. Obrigado !
Thomas Um

1
Eu só desejo todos os recursos que explicam o "o quê" incluem um "porquê". Off para mais googling, como de costume.
aaaaaa 26/03

80

Evento de captura ( useCapture = true) vs Evento de bolha ( useCapture = false)

Referência MDN

  • O evento de captura será enviado antes do evento de bolha
  • A ordem de propagação do evento é
    1. Captura dos pais
    2. Captura de crianças
    3. Captura de Alvo e Bolha de Alvo
      • Na ordem em que foram registrados
      • Quando o elemento é o destino do evento, o useCaptureparâmetro não importa (obrigado @bam e @ legend80s)
    4. Bolha das crianças
    5. Parent Bubble
  • stopPropagation() vai parar o fluxo

use o fluxo de captura

Demo

Resultado:

  1. Captura dos pais
  2. Bolha alvo 1

    (Como o Capture e o Bubble of Target serão acionados na ordem em que foram registrados, o evento Bubble será acionado antes do evento Capture)

  3. Captura de Alvos

  4. Bolha alvo 2
  5. Parent Bubble

var parent = document.getElementById('parent'),
target = document.getElementById('target');

target.addEventListener('click', function (e) { 
console.log('Target Bubble 1');
// e.stopPropagation();
}, false);

target.addEventListener('click', function (e) { 
console.log('Target Capture');
// e.stopPropagation();
}, true);

target.addEventListener('click', function (e) { 
console.log('Target Bubble 2');
// e.stopPropagation();
}, false);

parent.addEventListener('click', function (e) { 
console.log('Parent Capture');
// e.stopPropagation();
}, true);

parent.addEventListener('click', function (e) { 
console.log('Parent Bubble');
// e.stopPropagation();
}, false);
<div id="parent">
    <button id="target" style="padding: 1em 0.8em;">
        Trigger event
    </button>
</div>


1
Há um erro no exemplo: você declarou eventos filhos na ordem: 1. captura infantil 2. bolha infantil Isso importa! Só porque se a Criança for o alvo do evento, os ouvintes serão chamados na mesma ordem. Veja a nota no MDN: quando o elemento é o destino do evento 'useCapture', o parâmetro não importa. ( developer.mozilla.org/pt-BR/docs/Web/API/EventTarget/… )
bam

1
Nota : Para ouvintes de eventos anexados ao destino do evento, o evento está na fase de destino, em vez das fases de captura e bolha. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.De developer.mozilla.org/en-US/docs/Web/API/EventTarget/… . Portanto, não existe fase de "Children Capture" e "Children Bubble".
precisa saber é

E isso explica por que executar o exemplo produz "Filhos da bolha 1" antes de "Filhos da captura", quando o diagrama sugere que a "captura" deve sempre ocorrer primeiro para qualquer elemento!
Gershom

18

Quando você diz useCapture = true, os Eventos são executados de cima para baixo na fase de captura, quando false, ele faz um balão de baixo para cima.


11

É tudo sobre modelos de eventos: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow Você pode capturar um evento na fase de bolhas ou na fase de captura. Sua escolha.
Dê uma olhada em http://www.quirksmode.org/js/events_order.html - você achará muito útil.


1
os links para o w3 são tão ou menos úteis que a pesquisa no google.
Muhammad Umer

3
Sim, esse link w3 é apenas um monte de palavras, mas, ao contrário, o segundo link para o site quirksmode explica o tópico muito bem e brevemente.
Stano

11

Exemplo de código:

<div id="div1" style="background:#9595FF">
  Outer Div<br />
  <div id="div2" style="background:#FFFFFF">
    Inner Div
  </div>
</div>

Código Javascript:

d1 = document.getElementById("div1");
d2 = document.getElementById("div2");

se ambos estiverem configurados para false

d1.addEventListener('click',function(){alert("Div 1")},false);
d2.addEventListener('click',function(){alert("Div 2")},false);

Executa: Ao clicar em Div interno, os alertas são exibidos como: Div 2> Div 1

Aqui, o script é executado a partir do elemento interno: Event Bubbling (useCapture foi definido como false)

div 1 é definido como true e div 2 é definido como false

d1.addEventListener('click',function(){alert("Div 1")},true);
d2.addEventListener('click',function(){alert("Div 2")},false);

Executa: Ao clicar em Div interno, os alertas são exibidos como: Div 1> Div 2

Aqui, o script é executado a partir do elemento ancestral / externo: Captura de Eventos (useCapture foi definido como true)

div 1 é definido como false e div 2 é definido como true

d1.addEventListener('click',function(){alert("Div 1")},false);
d2.addEventListener('click',function(){alert("Div 2")},true);

Executa: Ao clicar em Div interno, os alertas são exibidos como: Div 2> Div 1

Aqui, o script é executado a partir do elemento interno: Event Bubbling (useCapture foi definido como false)

div 1 é definido como true e div 2 é definido como true

d1.addEventListener('click',function(){alert("Div 1")},true);
d2.addEventListener('click',function(){alert("Div 2")},true);

Executa: Ao clicar em Div interno, os alertas são exibidos como: Div 1> Div 2

Aqui, o script é executado a partir do elemento ancestral / externo: Captura de Eventos desde que useCapture foi definido como true


1
Qual é o significado das divisas "maiores que" neste contexto?
2540625

9

Resumo:

A DOMespecificação descrita em:

https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases

funciona da seguinte maneira:

Um evento é despachado seguindo um caminho da raiz ( document) da árvore para o nó de destino . O nó de destino é o HTMLelemento mais profundo , ou seja, o event.target. O envio de eventos (também chamado de propagação de eventos) ocorre em três fases e na seguinte ordem:

  1. A fase de captura: o evento é despachado para os ancestrais do destino da raiz da árvore ( document) para o pai direto do nó de destino.
  2. A fase de destino: o evento é despachado para o nó de destino. A fase de destino está sempre no htmlelemento mais profundo no qual o evento foi despachado.
  3. A fase de bolha: o evento é despachado para os ancestrais do destino, desde o pai direto do nó de destino até a raiz da árvore.

Borbulhamento de evento, captura de evento, destino do evento

Exemplo:

// bubbling handlers, third argument (useCapture) false (default)
document.getElementById('outerBubble').addEventListener('click', () => {
  console.log('outerBubble');
}, false)

document.getElementById('innerBubble').addEventListener('click', () => {
  console.log('innerBubble');
}, false)


// capturing handlers, third argument (useCapture)  true
document.getElementById('outerCapture').addEventListener('click', () => {
  console.log('outerCapture');
}, true)

document.getElementById('innerCapture').addEventListener('click', () => {
  console.log('innerCapture');
}, true)
div:hover{
  color: red;
  cursor: pointer;
}
<!-- event bubbling -->
<div id="outerBubble">
  <div id="innerBubble">click me to see Bubbling</div>
</div>


<!-- event capturing -->
<div id="outerCapture">
  <div id="innerCapture">click me to see Capturing</div>
</div>

O exemplo acima ilustra realmente a diferença entre a bolha de eventos e a captura de eventos. Ao adicionar ouvintes de eventos addEventListener, existe um terceiro elemento chamado useCapture. Isso booleanquando definido como truepermite que o ouvinte de eventos use a captura de eventos em vez de bolhas de eventos.

Em nosso exemplo, quando configuramos o argumento useCapture false, vemos que ocorre a bolha de eventos. Primeiro, o evento na fase de destino é disparado (registra innerBubble) e, em seguida, através do evento borbulhante, o evento no elemento pai é disparado (registra outerBubble).

Quando configuramos o argumento useCapture true, vemos que o evento externo <div>é acionado primeiro. Isso ocorre porque o evento agora é disparado na fase de captura e não na fase de bolhas.


7

Dadas as três fases da viagem do evento :

  1. A fase de captura : o evento é despachado para os ancestrais do destino da raiz da árvore para o pai direto do nó de destino.
  2. A fase de destino : o evento é despachado para o nó de destino.
  3. A fase de bolha : o evento é despachado para os ancestrais do destino, desde o pai direto do nó de destino até a raiz da árvore.

useCaptureindica em quais fases a viagem do evento estará:

Se true, useCapture indica que o usuário deseja adicionar o ouvinte de evento para a fase de captura única, ou seja, este ouvinte de evento não será disparado durante o alvo e borbulhando fases. Se false, o ouvinte de evento será acionado apenas durante as fases de destino e de subida

A origem é igual à segunda melhor resposta: https://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-phases


2

A ordem da definição é importante apenas se os itens estiverem no mesmo nível. Se você reverter a ordem da definição no seu código, obterá os mesmos resultados.

No entanto, se você reverter a configuração useCapture nos dois manipuladores de eventos, o manipulador de eventos filho responderá antes do pai. A razão para isso é que o manipulador de eventos filho agora será acionado na fase de captura anterior à fase de bolhas na qual o manipulador de eventos pai será acionado.

Se você definir useCapture como true para os dois manipuladores de eventos, independentemente da ordem da definição, o manipulador de eventos pai será acionado primeiro porque ocorre antes do filho na fase de captura.

Por outro lado, se você definir useCapture como false para os dois manipuladores de eventos - novamente independentemente da ordem de definição - o manipulador de eventos filho será acionado primeiro porque ocorre antes do pai na fase de bolhas.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.