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!

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.

2 Comentários

Deixe aqui o seu comentário