Vimos no post anterior algumas formas de definição de funções de callback para eventos em um determinado elemento, bem como impedir que as demais sejam executadas. Neste post, vou mostrar algumas dicas para melhorar a performance na interação do usuário com uma página contendo muitos elementos (linhas de uma tabela muito grande, por exemplo).
Antes do jQuery 1.7, existiam várias maneiras de se registrar uma função de callback para um evento:
$('.myButton').click(<function>);
$('.myButton').bind('click', <function>);
$(document).delegate('.myButton', 'click', <function>);
$('.myButton').live('click', <function>);
Coloco as formas acima para referência — caso você veja um exemplo de código como acima, não use-o antes de converter para usar com versões atuais do jQuery. Uma referência pode ser achada aqui.
A partir do jQuery 1.7, todas estas formas poderão ser feitas com o método .on()
. Porém, uma grande fonte de confusão é que o .on()
pode causar efeitos diferentes dependendo dos argumentos que são passados, portanto pode ser facilmente usado de maneira incorreta, causando problemas de performance da tela e efeitos indesejados como chamadas repetidas à função de callback.
Primeiro devemos separar o entendimento em duas partes: eventos diretos e eventos delegados.
Eventos diretos
São eventos registrados especificamente para certos elementos na página. Deste modo, quando um elemento é removido, não responderá mais aos eventos registrados para ele. Ou seja, a informação de eventos fica guardada no próprio elemento e some junto com ele. Um exemplo:
$('.myButton').on('click', <function>);
$('.myButton').remove();
$('<button />').addClass('myButton').appendTo('body');
// agora, o evento não será mais disparado pois o elemento original foi removido
Isto pode parecer óbvio, mas é necessário para entender o que falaremos a seguir.
Eventos delegados
Em contraste com os eventos diretos, eventos delegados são registrados á um seletor ao invés de um elemento específico. Dessa maneira, as funções de callback existem independentemente dos elementos da tela. Ex:
$(document).on('click', '.myButton', <function>);
$('.myButton').remove();
$('<button />').addClass('myButton').appendTo('body');
// o evento será disparado
Neste caso, qualquer evento click
disparado por um elemento dentro de document
(que é o delegado) que corresponda ao seletor '.myButton'
irá disparar. Eventos delegados também tem outro benefício: a possibilidade de adicionar mais elementos na página que correspondam com o seletor, sem a necessidade de registrar explicitamente com .bind()
.
Na prática
Uma vez que entendemos a diferença entre os dois, é importante analisar qual é a melhor abordagem para o seu caso. No cenário de uma tabela de resultados com muitas linhas, por exemplo, é muito mais performático usar a segunda abordagem. Por exemplo:
HTML
<table id="resultados"> <thead> <tr> <td>ID</td> <td>Nome</td> <td>E-mail</td> </tr> </thead> <tbody> <tr> <td>123</td> ... </tr> <!--- + 1000 linhas --> </tbody> </table>
JS
$('#resultados tbody').on('click', 'a.remover', function(e) { // você pode acessar o elemento com e.target $(e.target) // é o <a> .closest('tr').remove(); // e o elemento delegate com e.delegateTarget $(e.delegateTarget) // é o <tbody> dentro de #resultados .data('count', $(e.delegateTarget).data('count') - 1); });
Teste real de performance
Só de quebra, aqui vai um teste de performance no jsperf.com, comprovando o ganho de performance de 98% com uma tabela de apenas 1000 linhas. No seu projeto pode existir mais que isso!
2 Comentários
Bacana, Rodolfo. Parabéns pelo artigo.
Muito bom Rodolfo.
Só pra complementar, tem um post bem bacana no Tableless: http://tableless.com.br/jquery-conheca-os-metodos-on-e-off/ do Davi Ferreira, que além do on, também explica o uso do off e one, e do uso dos mesmos com namespace
😉