Event binding é basicamente a técnica que nós desenvolvedores usamos para criar regras na interface de usuário, como por exemplo: “quando o usuário clicar neste botão, fazer tal coisa”, ou “ao digitar neste campo, mostre a quantidade de caracteres ao lado”.
Neste post, pretendo:

  1. esclarecer um pouco sobre como exatamente os eventos acontecem no browser
  2. responder uma pergunta que normalmente recebo sobre a diferença entre e.stopPropagation() e e.stopImmediatePropagation()

Event binding no jQuery

A biblioteca jQuery provê uma API bem concisa e natural para definirmos nossas regras para event binding.
Podemos, por exemplo, codificar da seguinte maneira:

$('#myButton').click(function(){
  alert('myButton was clicked');
});

A funcão que executa uma determinada ação quando acontece um evento é chamada de função de callback.
Graças á natureza de escopo léxico do Javascript, também podemos usar variáveis disponíveis no escopo de fora de onde a função está sendo definida. Por exemplo:

var mensagem = 'myButton was double clicked';
$('#myButton').dblclick(function(){
  alert(mensagem);
  // variável mensagem foi definida fora da função!
});

Porém, há um mecanismo do jQuery que funciona abaixo disso, e saber como este mecanismo funciona é fundamental para entender como podemos construir interfaces mais complexas mais rapidamente e com um código mais conciso.

Entendendo event bubbling nativo

Para entender este mecanismo, primeiro temos que entender como os eventos se comportam dentro de uma página e como são disparados. Tendo a seguinte estrutura:

<form>
    <div>
        <button>Botão</button>
    </div>
</form>

Quando o usuário clica no <button>, isso é o que o browser faz, em ordem:
[list type=”icon-circle”]

  • chama a função que está na propriedade onclick do <button>
  • chama a função que está na propriedade onclick da <div>
  • chama a função que está na propriedade onclick do <form>

[/list]
E continua a chamar as funções definidas no onclick dos elementos pais até chegar no elemento document. Este processo é chamado de event bubbling e acontece dessa maneira em todos os browsers de hoje. Até agora, isto é Javascript padrão, sem jQuery.

Event binding do jQuery

O jQuery abstrai este mecanismo e permite várias facilidades incluindo a possibilidade de definir várias funções para um mesmo evento em um mesmo elemento (note que a propriedade onclick padrão recebia apenas uma função, não sendo possível definir mais de uma).
Isto é feito usando uma estratégia bem simples: o jQuery define uma função interna como callback para o evento (ex: na propriedadeonclick do elemento), e esta função é quem itera pelas outras funções definidas pelo seu código para este evento, disparando uma por uma até não existir mais funções, e então o próprio browser assume controle e, conforme o processo descrito acima, dispara o mesmo evento para o elemento pai, sendo executada a mesma estratégia, recursivamente.

Inspecionando detalhes do evento disparado

Para cada função de callback definida, você pode opcionalmente receber como primeiro parâmetro um objeto que contém detalhes sobre o evento atual. Este parâmetro é normalmente chamado de e, como abaixo:

$('#myButton').click(function(e){
  alert(e.type + ' aconteceu no elemento ' + e.target);
  // click aconteceu no elemento <button></button>
});

Existem várias propriedades no objeto Event, incluindo quem foi o elemento no qual o evento aconteceu (e.target), qual o tipo (e.type), entre outras. Este objeto também provê diversas funções para interagir com o evento e até modificar seu comportamento. Duas delas sãostopPropagation e stopImmediatePropagation

Diferença entre e.stopPropagation() e e.stopImmediatePropagation()

Tendo consciência dos conceitos acima, agora podemos entender como estas duas funções diferem.
Usando a estrutura acima e jQuery, digamos que você definiu 3 funções de callback para o clique do <button>, sendo elas fn1fn2 e fn3:

var fn1 = function(e){ /*...*/ };
var fn2 = function(e){ /*...*/ };
var fn3 = function(e){ /*...*/ };
$('button').click(fn1);
$('button').click(fn2);
$('button').click(fn3);

Ao clicar no botão, essas 3 funções irão ser executadas na ordem que foram registradas.

e.stopPropagation()

Digamos que na fn2 fizemos uma chamada para e.stopPropagation(), como abaixo:

var fn2 = function(e){
    console.log('eu sou a função 2!');
    e.stopPropagation();
};

O que irá acontecer é: as funções fn1fn2 e fn3 irão ser executadas normalmente, porém as funções de callback dos elementos pais não serão mais executadas. Este é o efeito de stopPropagation.

e.stopImmediatePropagation()

Agora, suponhamos que ao invés da chamada para e.stopPropagation() em fn2, temos agora e.stopImmediatePropagation(). Ao executar fn2, todas as próximas funções da cadeia atual (neste caso, f3) não serão executadas, nem qualquer função de callback dos elementos pais!

Conclusão

Esta é a fundamental diferença entre esses duas funções, um prevê a execução somente dos callbacks dos elementos pais, continuando com a cadeia do elemento atual, e a outra para imediatamente a execução até das funções restantes na cadeia.
Em um próximo post, irei um pouco além e mostrar como você pode melhorar drasticamente a performance de uma tela com muitos elementos, como por exemplo uma tabela com muitas linhas!

Autor

Wende Mendes é desenvolver na Bluesoft há mais de 13 anos. Atualmente, ele lidera uma grande equipe de desenvolvedores que atua no módulo de Comercial e Operações do software da Bluesoft.

3 Comentários

  1. Parabéns Cabelo.
    Não sei se vc já viu isso, mas o que o jQuery faz é usar a funcão de stopPropagation do próprio browser segue o link
    https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
    Mas quando o browser não suporta(IE) ele faz um cancelBubble
    Segue o código do jQuery
    stopPropagation: function() {
    this.isPropagationStopped = returnTrue;
    var e = this.originalEvent;
    if ( !e ) {
    return;
    }
    // if stopPropagation exists run it on the original event
    if ( e.stopPropagation ) {
    e.stopPropagation();
    }
    // otherwise set the cancelBubble property of the original event to true (IE)
    e.cancelBubble = true;
    },
    O bom de fazer dessa forma que eles fazem é que você ganha muito em performance.
    Abs

    • Olá Bruno!
      Sim, o jQuery faz um proxy de várias coisas do Event, na verdade ele normaliza o Event nativo, guardando em uma propriedade originalEvent do objeto dele para prover um comportamento consistente através de browsers.
      Para uma relação completa de quais propriedades são copiadas/normalizadas e quais métodos são redirecionados, veja na Documentação Oficial sobre o objeto Event nos docs do jQuery.
      Obrigado pela complementação, Bruno!

Deixe aqui o seu comentário