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:
- esclarecer um pouco sobre como exatamente os eventos acontecem no browser
- responder uma pergunta que normalmente recebo sobre a diferença entre
e.stopPropagation()
ee.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 fn1
, fn2
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 fn1
, fn2
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!
3 Comentários
Parabéns, Rodolfo!
Excelente post! Muito bem explicado.
Abraços.
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 oEvent
nativo, guardando em uma propriedadeoriginalEvent
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!