Diretto vs. delegato - jQuery .on ()


159

Sto cercando di comprendere questa particolare differenza tra i gestori di eventi diretti e delegati utilizzando il metodo jQuery .on () . In particolare, l'ultima frase in questo paragrafo:

Quando selectorviene fornito un, il gestore eventi viene definito delegato . Il gestore non viene chiamato quando l'evento si verifica direttamente sull'elemento associato, ma solo per i discendenti (elementi interni) che corrispondono al selettore. jQuery esegue la bolla dell'evento dalla destinazione dell'evento fino all'elemento a cui è collegato il gestore (ovvero, dall'elemento più interno a quello più esterno) ed esegue il gestore per tutti gli elementi lungo quel percorso corrispondenti al selettore.

Che cosa significa "esegue il gestore per qualsiasi elemento"? Ho creato una pagina di prova per sperimentare il concetto. Ma entrambi i costrutti seguenti portano allo stesso comportamento:

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

o,

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

Forse qualcuno potrebbe fare riferimento a un esempio diverso per chiarire questo punto? Grazie.


7
Per tutti gli interessati: jsperf.com/jquery-fn-on-delegate-vs-direct
dgo

1
@KevinWheeler Ho commentato il violino qui sotto, ma qui , in sostanza, non è impostato correttamente (sei vincolante per l'elemento genitore e la delega è destinata ai bambini). Per rispondere alla tua domanda, significa che il gestore delegato corrisponderà agli elementi appena aggiunti, mentre quello senza delega no. La delega ha il vantaggio che ci sono meno eventi collegati nel browser che causano un consumo di memoria inferiore per l'app, tuttavia il compromesso è che aumenta il tempo di elaborazione di un clic (minimo). Se stai realizzando un gioco, non delegare.
vipero07,

1
La "pagina di prova" a cui fai riferimento non funziona.
Jaime Montoya,

Risposte:


374

Caso 1 (diretto):

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

== Ehi! Voglio che ogni span.green all'interno del target div # ascolti: quando vieni cliccato, fai X.

Caso 2 (delegato):

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

== Ehi, div # target! Quando viene fatto clic su uno qualsiasi degli elementi figlio "span.green", esegui X con essi.

In altre parole...

Nel caso 1, a ciascuna di queste campate sono state date istruzioni individualmente. Se vengono creati nuovi span, non avranno sentito le istruzioni e non risponderanno ai clic. Ogni intervallo è direttamente responsabile dei propri eventi.

Nel caso 2, solo il contenitore ha ricevuto le istruzioni; è responsabile di notare i clic per conto dei suoi elementi figlio. Il lavoro di raccolta di eventi è stato delegato . Ciò significa anche che l'istruzione verrà eseguita per gli elementi figlio che verranno creati in futuro.


46
Questa è una grande spiegazione e ha portato chiarezza su un problema che da tempo mi sono rifiutato di capire. Grazie!
Do

3
Quindi perché on()consentire due argomenti quando quello sarebbe praticamente lo stesso dell'uso click()?
Nipponese,

5
.on () è un'API generica in grado di gestire qualsiasi tipo di evento, inclusi più eventi diversi (puoi inserire più nomi di eventi in quella prima stringa.) .click () è solo una scorciatoia per quel primo modulo.
N3dst4

1
@newbie, @ N3dst4: e.targetsarà la destinazione iniziale dell'evento click (può essere un nodo figlio se span.greenha figli). Dall'interno del gestore, è necessario utilizzare il thisriferimento. Vedi questo violino .
LeGEC,

1
Un'altra nota da aggiungere all'argomento della delega: è molto utile e molto efficiente. Utilizzerai meno risorse del browser con la delega anziché allegando un evento a ogni elemento corrispondente. Pensavo di menzionarlo, nel caso in cui le persone avessero bisogno di più motivi per usare la delega.
phatskat,

6

Il primo modo, $("div#target span.green").on()associa un gestore di clic direttamente agli span che corrispondono al selettore nel momento in cui il codice viene eseguito. Ciò significa che se altri span vengono aggiunti in un secondo momento (o se la loro classe viene modificata in modo che corrispondano), si sono persi e non avranno un gestore di clic. Significa anche che se in seguito rimuovi la classe "verde" da uno degli span il suo gestore di clic continuerà a funzionare - jQuery non tiene traccia di come è stato assegnato il gestore e controlla se il selettore continua a corrispondere.

Il secondo modo, $("div#target").on()lega un gestore di clic ai div che corrispondono (di nuovo, questo è contro quelli che corrispondono in quel momento), ma quando si verifica un clic da qualche parte nel div la funzione del gestore verrà eseguita solo se il clic si è verificato non solo nel div ma in un elemento figlio che corrisponde al selettore nel secondo parametro a .on()"span.green". Fatto in questo modo, non importa quando sono stati creati quegli span figlio, facendo clic su di essi verrà comunque eseguito il gestore.

Quindi per una pagina che non aggiunge o modifica dinamicamente i suoi contenuti non noterai una differenza tra i due metodi. Se stai aggiungendo in modo dinamico elementi secondari aggiuntivi, la seconda sintassi significa che non devi preoccuparti di assegnare loro i gestori dei clic perché l'hai già fatto una volta sul genitore.


5

La spiegazione di N3dst4 è perfetta. Sulla base di questo, possiamo supporre che tutti gli elementi figlio siano all'interno del corpo, quindi dobbiamo usare solo questo:

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

Funziona con eventi diretti o delegati.


2
jquery sconsiglia di usare body, poiché è più lento, perché la sceneggiatura dovrebbe cercare tutti i bambini all'interno del corpo, il che dovrebbe essere molto nella maggior parte dei casi. È meglio (più veloce) usare il contenitore parent intermedio dell'elemento.
mikesoft,

1
Jquery ha rimosso il metodo live che stava facendo lo stesso come mostrato. Questa è una prestazione debole e non dovrebbe essere usata.
Maciej Sikora,

Nei browser moderni, la $('body').on()delega da .elementdovrebbe comportarsi esattamente come nativa document.body.addEventHandler()con un if (Event.target.className.matches(/\belement\b/))callback. Potrebbe essere leggermente più lento in jquery a causa delle $.proxyspese generali, ma non citarmi su questo.
cowbert,

2

Tangenziale per l'OP, ma il concetto che mi ha aiutato a svelare la confusione con questa caratteristica è che gli elementi associati devono essere genitori degli elementi selezionati .

  • Rilevato si riferisce a ciò che resta del .on.
  • Selezionato si riferisce al secondo argomento di .on().

La delega non funziona come .find (), selezionando un sottoinsieme degli elementi associati. Il selettore si applica solo agli elementi figlio rigorosi.

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

è molto diverso da

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

In particolare, per ottenere i vantaggi che @ N3dst4 suggerisce con "elementi che verranno creati in futuro", l'elemento associato deve essere un genitore permanente . Quindi i bambini selezionati possono andare e venire.

MODIFICARE

Elenco di controllo del perché il delegato .onnon funziona

Ragioni ingannevoli per cui $('.bound').on('event', '.selected', some_function)potrebbe non funzionare:

  1. L'elemento associato non è permanente . È stato creato dopo aver chiamato.on()
  2. L'elemento selezionato non è figlio di un elemento associato. È lo stesso elemento.
  3. L'elemento selezionato ha impedito il bubbling di un evento all'elemento associato chiamando .stopPropagation().

(Omettendo ragioni meno complicate, come un selettore errato.)


1

Guardo un post con un confronto di eventi diretti e delegati. Confronto pure js ma ha lo stesso significato per jquery che lo incapsula soltanto.

La conclusione è che la gestione degli eventi delegati è per la struttura DOM dinamica in cui gli elementi associati possono essere creati mentre l'utente interagisce con la pagina (non sono necessari ancora collegamenti) e la gestione diretta degli eventi è per gli elementi DOM statici, quando sappiamo che la struttura non cambierà.

Per ulteriori informazioni e confronto completo: http://maciejsikora.com/standard-events-vs-event-delegation/

Utilizzando gestori sempre delegati, che vedo attualmente molto alla moda, non è il modo giusto, molti programmatori lo usano perché "dovrebbe essere usato", ma la verità è che i gestori di eventi diretti sono migliori per alcune situazioni e la scelta di quale metodo utilizzare dovrebbe essere supportato dalla conoscenza delle differenze.


se si collegano molti gestori di eventi (ad esempio, ogni riga di una tabella), è spesso consigliabile utilizzare un gestore delegato singolo nel contenitore anziché collegare molti gestori diretti. Lo si può vedere dal fatto fondamentale che il conteggio dei gestori di eventi è di per sé una metrica di profilazione.
cowbert,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.