Direto vs. Delegado - jQuery .on ()

159

Eu estou tentando entender essa diferença particular entre os diretos e delegados manipuladores de eventos usando o jQuery .no () método . Especificamente, a última frase deste parágrafo:

Quando a selectoré fornecido, o manipulador de eventos é chamado de delegado . O manipulador não é chamado quando o evento ocorre diretamente no elemento vinculado, mas apenas para descendentes (elementos internos) que correspondem ao seletor. jQuery borbulha o evento do destino do evento até o elemento em que o manipulador está anexado (ou seja, do elemento mais interno ao mais externo) e executa o manipulador para quaisquer elementos ao longo desse caminho que correspondam ao seletor.

O que significa "executa o manipulador para quaisquer elementos"? Eu fiz uma página de teste para experimentar o conceito. Mas as duas construções a seguir levam ao mesmo comportamento:

$("div#target span.green").on("click", function() {
   alert($(this).attr("class") + " is clicked");
});

ou,

$("div#target").on("click", "span.green", function() {
   alert($(this).attr("class") + " is clicked");
});

Talvez alguém possa se referir a um exemplo diferente para esclarecer esse ponto? Obrigado.

moey
fonte
7
Para todos os interessados: jsperf.com/jquery-fn-on-delegate-vs-direct
DGO
1
@KevinWheeler Comentei seu violino abaixo, mas aqui , essencialmente, ele não está configurado corretamente (você está vinculando ao elemento pai e a delegação é destinada aos filhos). Para responder sua pergunta mais, significa que o manipulador delegado corresponderá aos elementos adicionados recentemente, onde o sem delegação não corresponderá. A delegação tem o benefício de haver menos eventos conectados ao navegador, causando menor consumo de memória para o aplicativo; no entanto, a desvantagem é que aumenta o tempo para processar um clique (minimamente). Se você está fazendo um jogo, não delegue.
vipero07
1
A "página de teste" que você faz referência não está funcionando.
Jaime Montoya

Respostas:

373

Caso 1 (direto):

$("div#target span.green").on("click", function() {...});

== Ei! Quero que todo span.green dentro do div # target seja ouvido: quando você clica em, faça X.

Caso 2 (delegado):

$("div#target").on("click", "span.green", function() {...});

== Ei, div # target! Quando qualquer um dos elementos filhos "span.green" for clicado, faça X com eles.

Em outras palavras...

No caso 1, cada uma dessas extensões recebeu instruções individualmente. Se novos spans forem criados, eles não terão ouvido as instruções e não responderão aos cliques. Cada período é diretamente responsável por seus próprios eventos.

No caso 2, apenas o contêiner recebeu a instrução; é responsável por perceber cliques em nome de seus elementos filhos. O trabalho de captura de eventos foi delegado . Isso também significa que a instrução será executada para elementos filho criados no futuro.

N3dst4
fonte
45
Essa é uma ótima explicação e trouxe clareza a uma questão que há muito tempo me recuso a entender. Obrigado!
DGO
3
Então, por que on()permitem dois argumentos quando isso seria o mesmo que usar click()?
Nipponese
5
.on () é uma API de uso geral que pode manipular qualquer tipo de evento, incluindo vários eventos diferentes (você pode colocar vários nomes de eventos nessa primeira sequência.) .click () é apenas uma abreviação para esse primeiro formulário.
N3dst4
1
@ newbie, @ N3dst4: e.targetserá o destino inicial do evento click (pode ser um nó filho se span.greentiver filhos). De dentro do manipulador, você deve usar a thisreferência. Veja este violino .
LeGEC 14/02
1
Outra observação a ser adicionada ao tópico da delegação - é muito útil e muito eficiente. Você utilizará menos recursos do navegador com delegação e anexará um evento a todos os elementos correspondentes. Pensei em mencioná-lo, caso as pessoas precisassem de mais motivos para usar a delegação.
phatskat
6

A primeira maneira, $("div#target span.green").on()vincula um manipulador de cliques diretamente aos espaços que correspondem ao seletor no momento em que o código é executado. Isso significa que se outras extensões forem adicionadas mais tarde (ou tiverem sua classe alterada para corresponder), elas perderão e não terão um manipulador de cliques. Isso também significa que, se você remover posteriormente a classe "verde" de um dos períodos em que o manipulador de cliques continuará sendo executado - o jQuery não acompanhará como o manipulador foi atribuído e verificará se o seletor ainda corresponde.

A segunda maneira, $("div#target").on()vincula um manipulador de cliques às divs que correspondem (novamente, isso é contra as que correspondem naquele momento), mas quando um clique ocorre em algum lugar na div, a função manipuladora será executada apenas se o clique ocorreu não apenas na div, mas em um elemento filho que corresponde ao seletor no segundo parâmetro para .on()"span.green". Feito dessa maneira, não importa quando essas extensões filho foram criadas, clicar nelas ainda executará o manipulador.

Portanto, para uma página que não está adicionando ou alterando dinamicamente seu conteúdo, você não notará diferença entre os dois métodos. Se você estiver adicionando elementos filho adicionais dinamicamente, a segunda sintaxe significa que você não precisa se preocupar em atribuir manipuladores de cliques a eles, porque você já fez isso uma vez no pai.

nnnnnn
fonte
5

A explicação do N3dst4 é perfeita. Com base nisso, podemos assumir que todos os elementos filhos estão dentro do corpo; portanto, precisamos usar apenas isso:

$('body').on('click', '.element', function(){
    alert('It works!')
});

Funciona com evento direto ou delegado.

Brynner Ferreira
fonte
2
jquery desaconselha o uso do corpo, pois é mais lento, porque o script precisaria procurar todas as crianças dentro do corpo, o que deve ser muito na maioria dos casos. É melhor (mais rápido) usar o contêiner pai intermediário do elemento.
precisa saber é o seguinte
1
O Jquery removeu o método ativo, que estava fazendo o mesmo que você mostrou. Esse desempenho é fraco e não deve ser usado.
Maciej Sikora
Nos navegadores modernos, a $('body').on()delegação de .elementdeve se comportar exatamente da mesma forma que a nativa document.body.addEventHandler()com um if (Event.target.className.matches(/\belement\b/))retorno de chamada. Pode ser um pouco mais lento no jquery devido à $.proxysobrecarga, mas não me cite nisso.
cowbert
2

Tangencial para o OP, mas o conceito que me ajudou a desvendar a confusão com esse recurso é que os elementos vinculados devem ser os pais dos elementos selecionados .

  • Limite refere-se ao que resta do .on.
  • Selecionado refere-se ao segundo argumento de .on().

A delegação não funciona como .find (), selecionando um subconjunto dos elementos ligados. O seletor se aplica apenas a elementos filho estritos.

$("span.green").on("click", ...

é muito diferente de

$("span").on("click", ".green", ...

Em particular, para obter as vantagens, o @ N3dst4 sugere com "elementos criados no futuro" que o elemento ligado deve ser um pai permanente . Então as crianças selecionadas podem ir e vir.

EDITAR

Lista de verificação de por que o delegado .onnão funciona

Motivos complicados pelos quais $('.bound').on('event', '.selected', some_function)pode não funcionar:

  1. O elemento encadernado não é permanente . Foi criado após a chamada.on()
  2. O elemento selecionado não é um filho adequado de um elemento vinculado. É o mesmo elemento.
  3. O elemento selecionado evitou a ocorrência de bolhas de um evento no elemento ligado ao chamar .stopPropagation().

(Omitindo razões menos complicadas, como um seletor com erros ortográficos.)

Bob Stein
fonte
1

Escrevi um post com uma comparação de eventos diretos e delegados. Eu comparo js puros, mas tem o mesmo significado para jquery, que apenas o encapsula.

A conclusão é que a manipulação de eventos delegados é para a estrutura dinâmica do DOM, onde os elementos vinculados podem ser criados enquanto o usuário interage com a página (não é necessário vincular novamente), e a manipulação direta de eventos é para os elementos estáticos do DOM, quando sabemos que a estrutura não será alterada.

Para mais informações e comparação completa - http://maciejsikora.com/standard-events-vs-event-delegation/

Usar manipuladores sempre delegados, que eu vejo atualmente muito na moda, não é o caminho certo, muitos programadores o usam porque "deve ser usado", mas a verdade é que os manipuladores diretos de eventos são melhores para alguma situação e a escolha de qual método deve ser suportado pelo conhecimento das diferenças.

Maciej Sikora
fonte
se anexar muitos manipuladores de eventos (por exemplo, cada linha de uma tabela), geralmente é melhor desempenho usar manipulador delegado único no contêiner, em vez de anexar muitos manipuladores diretos. Você pode ver isso pelo fato básico de que a contagem de manipuladores de eventos é, por si só, uma métrica de criação de perfil.
cowbert