Se um elemento e seu pai têm um manipulador de eventos para o mesmo evento, qual elemento será acionado primeiro quando acionado?

A propagação de eventos em JavaScript usando bubbling e captura fornece desenvolvedores com uma resposta a esta pergunta. Neste artigo, aprenderemos como o borbulhamento e a captura de eventos funcionam, compararemos diferentes métodos para acessar as propriedades do evento e examinaremos alguns exemplos e casos de uso diferentes.

Vamos começar!

Borbulhamento de evento e sequência de captura

O que é captura de evento?

Na captura de evento, também conhecida como trickling, o manipulador de eventos externo é acionado antes que o manipulador específico seja acionado. Por exemplo, o evento no div dispara antes do evento no botão:

Hierarquia de captura de evento=documento → html → corpo → pai → filho

A captura tem uma prioridade mais alta do que o bubbling, o que significa que os manipuladores de evento de captura são executado antes de manipuladores de evento de bolha, conforme mostrado pelas fases de propagação de evento:

Fase de captura: o evento desce em direção ao elemento Fase de destino: o evento atinge o elemento de destino Fase de bolha: o evento borbulha do elemento

O que é bolha de evento?

A bolha de evento segue a ordem oposta da captura de evento. Um evento se propaga de um elemento HTML filho e, em seguida, sobe na hierarquia DOM para seus elementos pais:

Hierarquia de bolhas de eventos=filho → pai → corpo → html → documento

Ouvindo eventos de propagação

Podemos ouvir esses eventos de propagação usando o método addEventListener (), que é anexado aos nós HTML. Ele aceita três argumentos: um nome de evento, uma função de retorno de chamada e um valor de captura opcional, que é definido como falso por padrão:

element.addEventListener (evento, manipulador, falso)

O valor de captura está vazio

Vamos revisar o que acontecerá se um usuário clicar no botão quando o valor de captura for deixado em branco:

element.addEventListener (event, handler)

O evento click começa na fase de captura. Ele pesquisa os elementos pais do destino para qualquer evento com o manipulador de eventos. Ele não encontrará nenhum manipulador de eventos para a fase de captura.

Em seguida, ele desce até o destino. Depois que todos os eventos da fase de captura são executados, o evento passa para sua fase de bolha. Ele executa qualquer manipulador de eventos definido no elemento de destino. Ele se propaga novamente, procurando por meio dos elementos pais do alvo qualquer manipulador de eventos para a fase de bolhas. Agora, o ciclo do evento está completo.

Capture value is true

Vamos considerar o que acontece se o valor de captura for definido como true:

element.addEventListener (event, handler , verdadeiro)

O snippet de código acima seguirá a mesma ordem de quando o valor estava vazio. A principal diferença é que o manipulador de eventos executará qualquer manipulador de eventos que encontrar antes de chegar ao destino.

Acessando propriedades do objeto de evento

Existem diferenças nos métodos usados ​​pelos manipuladores para propriedades do objeto de evento de acesso:

event.target: refere-se ao elemento DOM que acionou o evento event.eventPhase: retorna a fase atual de propagação do evento (captura: 1, destino: 2, bolha: 3) event.currentTarget: refere-se ao elemento DOM que manipula o evento

Observe que se um ouvinte de evento for anexado ao pai, mas a propagação do evento for interrompida pelo filho, event.currentTarget se refere ao elemento DOM que interrompeu a propagação.

Estrutura de bolha de evento

Agora que entendemos como funciona a bolha e captura de evento, vamos tentar um exemplo! Digamos que temos a seguinte estrutura DOM e o seguinte ouvinte de evento, respectivamente:

document.querySelector (‘. Cta_button’).addEventListener (‘click’, function (event) {console.info (`Click event disparado em $ {this.nodeName}`);});

O evento de clique disparado em BUTTON seria registrado no console.

Estrutura DOM aninhada

Vamos ver o que acontece quando a estrutura DOM é aninhada e usa o mesmo ouvinte de evento anexado a o elemento pai:

document.querySelector (‘. cta_container’).addEventListener (‘click’, function (event) {console.info (`Click event disparado em $ {this.nodeName}`);});

No snippet de código acima, definimos um ouvinte de evento de clique no div, o elemento pai do botão. Quando clicado, ele registra o tipo de evento disparado e o elemento em que é disparado.

Quando os usuários clicam no botão do balão Assistir, o evento é direcionado para o botão. Se um manipulador de eventos for definido para o botão, o evento será disparado. Caso contrário, o evento borbulha ou se propaga para o div pai e um evento de clique é disparado no pai. Se o evento não for manipulado, o processo continua para o próximo pai no limite externo até que finalmente alcance o objeto do documento.

Mesmo que você tenha clicado no botão, as informações registradas no console são evento de clique disparado no DIV.

Anexar ouvinte de evento ao botão

O que acontece quando também anexamos um ouvinte de evento ao botão?

Watch me bubble

document.querySelector (‘. cta_container’).addEventListener (‘click’, function (event) {console.info (Click event disparado em $ {this. nodeName} `);});

A saída se torna o evento Click disparado em BUTTON e o evento Click disparado em DIV.

Como você pode ver, o evento borbulhou para o pai. Você pode usar a propriedade event.bubbles para verificar se um evento borbulha:

document.querySelector (‘. Cta_button’).addEventListener (‘click’, function (event) {console.info (Click event disparado em $ { this.nodeName}. Borbulha? $ {event.bubbles} `);});

Interrompendo a propagação

Um evento em um elemento DOM se propaga para todos os seus elementos pais, a menos que seja interrompido. Embora geralmente não haja necessidade de evitar o borbulhamento, pode ser útil em certos casos. Por exemplo, interromper a propagação pode evitar que os manipuladores de eventos interfiram uns com os outros.

Considere manipular arrastar e soltar usando eventos mousemove e mouseup. Parar a propagação pode evitar bugs de navegador que surgem como resultado de usuários movendo o mouse aleatoriamente.

Chamar event.stopPropagation () no elemento filho impede que ele borbulhe no elemento pai:

document.querySelector (‘. cta_button’). addEventListener (‘click’, event=> {event.stopPropagation ();//…});

Vamos parar a propagação do nosso exemplo da seção anterior, onde clicamos em um botão:

Vamos adicionar os ouvintes de evento:

document.querySelector (‘. cta_container’).addEventListener (‘click’, function (event) {console.info (`Click event disparado em $ {this.nodeName } `);}); document.querySelector (‘. cta_button’).addEventListener (‘click’, function (event) {event.stopPropagation (); console.info (`Click event disparado em $ {this.nodeName}`);});

O evento foi impedido de borbulhar usando event.stopPropagation (). A saída se torna o evento Click disparado no BUTTON.

Impedir o padrão do navegador

Digamos que você queira permitir que a propagação continue, mas deseja evitar que o navegador execute sua ação padrão se não há ouvinte tratando do evento. Você pode usar event.preventDefault ():

document.querySelector (‘. Cta_button’).addEventListener (‘click’, event=> {event.preventDefault ();//…});

Caso de uso de bolha de evento

Vamos aplicar o que cobrimos e criar um aplicativo de lista de compras que atinge um item assim que você o compra. Nesse cenário, adicionar ouvintes de eventos individuais não será viável porque você pode decidir adicionar um novo item no futuro.

Em vez disso, você precisa anexar o ouvinte de eventos ao elemento pai da lista. O ouvinte de eventos tratará do evento de clique dos filhos por meio do borbulhamento de eventos.

No trecho de código abaixo, listamos nossos itens de compras:

  • MacBook Pro
  • câmera Sony a6400
  • Microfone cardióide universal Boya
  • Light anel

Adicione um ouvinte de evento anexado a

    , conforme visto abaixo:

    document.querySelector (‘. list’). addEventListener (‘click’, event=> { event.target.classList.toggle (‘comprado’);});

    Você pode visualizar este código no CodePen abaixo:

    Veja a caneta
    Evento Bubbling
    por Chiamaka Ikeanyi ( @chiamakaikeanyi )
    em CodePen .

    Estrutura de captura de eventos

    Durante a delegação de eventos, onde o bubbling de eventos não é suportado, a captura de eventos torna-se especialmente benéfica para anexar eventos a conteúdo dinâmico. Por exemplo, você pode precisar lidar com eventos como foco e desfoque, para os quais não há suporte para bolhas.

    Para capturar um evento na fase de captura, é necessário definir a opção useCapture como true. Lembre-se, por padrão, ele é definido como falso:

    element.addEventListener (event, handler, true)

    Vamos considerar uma estrutura DOM aninhada:

    Definiremos a opção useCapture do elemento pai como true:

    document.querySelector (‘. cta_container’). addEventListener (‘click’, function (event) {console.info (Click event disparado em $ {this.nodeName} `);}, true); document.querySelector (‘. cta_button’).addEventListener (‘click’, function (event) {console.info (`Click event disparado em $ {this.nodeName}`);});

    Ao contrário do que obtemos com o bubbling, a saída é o evento Click disparado no DIV e o evento Click disparado no BUTTON.

    Caso de uso de captura de evento

    Vamos continuar nosso exemplo anterior quando construímos uma lista de compras. Se você quiser adicionar um campo de entrada à lista de compras que permite definir um orçamento para cada item, o ouvinte de evento anexado ao pai não se aplica a esses campos de entrada.

    Vamos pegar o código para nossa lista de compras e um ouvinte de eventos:

    Lista de compras

    • MacBook Pro
    • Chaves Logitech MX
    • Câmera Sony a6400
    • Microfone cardióide universal Boya
    • Anel leve

    document.querySelector (“. list”). addEventListener (“focus”, function (event) {console.info ($ {event.type} evento disparado em $ {this.nodeName} `); event.target.style.background=”#eee”; console.log (“destino:”, evento.target); console.log (“currentTarget:”, event.currentTarget); console.log (“eventPhase:”, event.eventPhase); });

    Quando você focaliza o cursor em qualquer campo de entrada, nada acontece. No entanto, ao definir a opção useCapture como true, você obterá o resultado desejado:

    document.querySelector (“. List”). AddEventListener (“focus”, function (event) {console.info (`$ evento {event.type} disparado em $ {this.nodeName} `); event.target.style.background=”#eee”; console.log (“target:”, event.target); console.log (“currentTarget:”, event.currentTarget); console.log (“eventPhase:”, event.eventPhase);}, true);

    Veja a Caneta
    Captura de evento
    de Chiamaka Ikeanyi ( @ chiamakaikeanyi )
    em CodePen .

    Você pode ver esta lista no CodePen acima.

    Conclusão

    Um forte entendimento de borbulhamento e captura de eventos é essencial para lidar com eventos de usuário em JavaScript. Neste tutorial, aprendemos como a propagação de eventos funciona em JavaScript, seguindo a sequência de captura, fase de destino e bubbling.

    Observe que bubbling sempre se propaga de um elemento filho para o pai, enquanto a captura se propaga do elemento pai para o filho. Para lembrar a ordem de propagação, você pode pensar em “bolha para cima e para baixo”.

    Espero que tenha gostado deste tutorial!