Qualcuno può spiegare la delega degli eventi in JavaScript e come è utile?
Qualcuno può spiegare la delega degli eventi in JavaScript e come è utile?
Risposte:
La delega di eventi DOM è un meccanismo di risposta agli ui-eventi tramite un singolo genitore comune piuttosto che ogni bambino, attraverso la magia dell'evento "gorgoglio" (ovvero propagazione di eventi).
Quando un evento viene attivato su un elemento, si verifica quanto segue :
L'evento viene inviato alla sua destinazione
EventTarget
e tutti i listener di eventi trovati lì vengono attivati. Gli eventi gorgoglianti attiveranno quindi eventuali listener di eventi aggiuntivi rilevati seguendo laEventTarget
catena padre verso l'alto , verificando la presenza di listener di eventi registrati su ciascun EventTarget successivo. Questa propagazione verso l'alto continuerà fino alDocument
.
Il bubbling di eventi fornisce le basi per la delega degli eventi nei browser. Ora puoi associare un gestore eventi a un singolo elemento genitore e quel gestore verrà eseguito ogni volta che si verifica l'evento su uno dei suoi nodi figlio (e uno dei loro figli a sua volta). Questa è una delegazione di eventi. Ecco un esempio in pratica:
<ul onclick="alert(event.type + '!')">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
Con questo esempio, se dovessi fare clic su uno dei <li>
nodi figlio , visualizzerai un avviso "click!"
, anche se non vi è alcun gestore di clic associato al <li>
clic su cui hai fatto clic. Se ci legassimo onclick="..."
a ciascuno <li>
otterresti lo stesso effetto.
Quindi qual è il vantaggio?
Immagina di avere ora la necessità di aggiungere dinamicamente nuovi <li>
elementi all'elenco sopra tramite manipolazione DOM:
var newLi = document.createElement('li');
newLi.innerHTML = 'Four';
myUL.appendChild(newLi);
Senza usare la delega degli eventi dovresti "ricollegare" il "onclick"
gestore eventi al nuovo <li>
elemento, in modo che agisca allo stesso modo dei suoi fratelli. Con la delega degli eventi non è necessario fare nulla. Basta aggiungere il nuovo <li>
all'elenco e il gioco è fatto.
Questo è assolutamente fantastico per le app Web con gestori di eventi associati a molti elementi, in cui i nuovi elementi vengono creati e / o rimossi in modo dinamico nel DOM. Con la delega degli eventi il numero di associazioni di eventi può essere drasticamente ridotto spostandoli in un elemento padre comune e il codice che crea al volo nuovi elementi dinamicamente può essere disaccoppiato dalla logica di associazione dei gestori di eventi.
Un altro vantaggio della delega degli eventi è che la quantità di memoria totale utilizzata dai listener di eventi diminuisce (poiché il numero di associazioni di eventi diminuisce). Potrebbe non fare molta differenza per le piccole pagine che scaricano spesso (ad esempio, l'utente passa spesso a pagine diverse). Ma per applicazioni di lunga durata può essere significativo. Ci sono alcune situazioni davvero difficili da rintracciare quando gli elementi rimossi dal DOM continuano a richiedere memoria (cioè perdono) e spesso questa memoria trapelata è legata a un binding di eventi. Con la delega degli eventi sei libero di distruggere elementi figlio senza il rischio di dimenticare di "separare" i loro ascoltatori di eventi (poiché l'ascoltatore è sull'antenato). Questi tipi di perdite di memoria possono quindi essere contenuti (se non eliminati, il che è molto difficile da fare a volte. IE ti sto guardando).
Ecco alcuni esempi di codice concreto migliori della delega degli eventi:
focus
eblur
(che non fanno bolle)<li>
quando dovrebbe fermarsi a <ul>
? Se la mia domanda non è ancora chiara o necessita di un thread separato, sarei felice di accontentarti.
La delega degli eventi consente di evitare l'aggiunta di listener di eventi a nodi specifici; invece, il listener di eventi viene aggiunto a un genitore. Quel listener di eventi analizza gli eventi gorgoglianti per trovare una corrispondenza sugli elementi figlio.
Esempio JavaScript:
Diciamo che abbiamo un elemento UL padre con diversi elementi figlio:
<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>
Diciamo anche che qualcosa deve accadere quando si fa clic su ciascun elemento figlio. È possibile aggiungere un listener di eventi separato a ogni singolo elemento LI, ma cosa succede se gli elementi LI vengono aggiunti e rimossi frequentemente dall'elenco? L'aggiunta e la rimozione di listener di eventi sarebbe un incubo, soprattutto se il codice di aggiunta e rimozione si trova in punti diversi all'interno della tua app. La soluzione migliore è aggiungere un listener di eventi all'elemento UL padre. Ma se aggiungi il listener di eventi al genitore, come fai a sapere quale elemento è stato cliccato?
Semplice: quando l'evento bolle all'elemento UL, si controlla la proprietà target dell'oggetto evento per ottenere un riferimento al nodo effettivamente selezionato. Ecco uno snippet JavaScript di base che illustra la delega degli eventi:
// Get the element, add a click listener...
document.getElementById("parent-list").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a list item
if(e.target && e.target.nodeName == "LI") {
// List item found! Output the ID!
console.log("List item ", e.target.id.replace("post-"), " was clicked!");
}
});
Inizia aggiungendo un listener di eventi click all'elemento genitore. Quando viene attivato il listener di eventi, controllare l'elemento dell'evento per assicurarsi che sia il tipo di elemento a cui reagire. Se è un elemento LI, boom: abbiamo ciò di cui abbiamo bisogno! Se non è un elemento che vogliamo, l'evento può essere ignorato. Questo esempio è piuttosto semplice: UL e LI sono un confronto diretto. Proviamo qualcosa di più difficile. Facciamo un DIV genitore con molti figli, ma ci interessa solo un tag A con la classe CSS classA:
// Get the parent DIV, add click listener...
document.getElementById("myDiv").addEventListener("click",function(e) {
// e.target was the clicked element
if(e.target && e.target.nodeName == "A") {
// Get the CSS classes
var classes = e.target.className.split(" ");
// Search for the CSS class!
if(classes) {
// For every CSS class the element has...
for(var x = 0; x < classes.length; x++) {
// If it has the CSS class we want...
if(classes[x] == "classA") {
// Bingo!
console.log("Anchor element clicked!");
// Now do something here....
}
}
}
}
});
delegazione di eventi dom è qualcosa di diverso dalla definizione di informatica.
Si riferisce alla gestione di eventi gorgoglianti da molti elementi, come celle di tabella, da un oggetto padre, come la tabella. Può mantenere il codice più semplice, specialmente quando si aggiungono o rimuovono elementi, e consente di risparmiare un po 'di memoria.
Delegazione è una tecnica in cui un oggetto esprime un determinato comportamento verso l'esterno ma in realtà delega la responsabilità dell'implementazione di tale comportamento a un oggetto associato. All'inizio sembra molto simile al modello proxy, ma ha uno scopo molto diverso. La delega è un meccanismo di astrazione che centralizza il comportamento dell'oggetto (metodo).
In generale: utilizzare la delega come alternativa all'eredità. L'ereditarietà è una buona strategia, quando esiste una stretta relazione tra oggetto genitore e figlio, tuttavia l'ereditarietà accoppia gli oggetti molto da vicino. Spesso, la delega è il modo più flessibile di esprimere una relazione tra classi.
Questo modello è anche noto come "catene proxy". Diversi altri modelli di progettazione utilizzano la delega: lo Stato, la strategia e i modelli di visitatori dipendono da esso.
Se ci sono molti elementi all'interno di un genitore e desideri gestirne gli eventi, non associare i gestori a ciascun elemento. Invece, associa il gestore singolo al genitore e ottieni il figlio da event.target. Questo sito fornisce informazioni utili su come implementare la delega degli eventi. http://javascript.info/tutorial/event-delegation
La delega degli eventi sta gestendo un evento che bolle utilizzando un gestore eventi su un elemento contenitore, ma attiva il comportamento del gestore eventi solo se l'evento si è verificato su un elemento all'interno del contenitore che corrisponde a una determinata condizione. Ciò può semplificare la gestione degli eventi sugli elementi all'interno del contenitore.
Ad esempio, supponiamo di voler gestire un clic su qualsiasi cella di una tabella in una tabella grande. si potrebbe scrivere un ciclo per collegare un gestore di clic a ciascuna cella ... oppure è possibile collegare un gestore di clic sulla tabella e utilizzare la delega degli eventi per attivarlo solo per le celle della tabella (e non per le intestazioni della tabella o lo spazio bianco all'interno di un remare intorno alle celle, ecc.).
È anche utile quando si aggiungono e rimuovono elementi dal contenitore, poiché non è necessario preoccuparsi di aggiungere e rimuovere gestori di eventi su tali elementi; basta agganciare l'evento sul contenitore e gestirlo quando bolle.
Ecco un semplice esempio (è intenzionalmente dettagliato per consentire una spiegazione in linea): Gestire un clic su qualsiasi td
elemento in una tabella del contenitore:
// Handle the event on the container
document.getElementById("container").addEventListener("click", function(event) {
// Find out if the event targeted or bubbled through a `td` en route to this container element
var element = event.target;
var target;
while (element && !target) {
if (element.matches("td")) {
// Found a `td` within the container!
target = element;
} else {
// Not found
if (element === this) {
// We've reached the container, stop
element = null;
} else {
// Go to the next parent in the ancestry
element = element.parentNode;
}
}
}
if (target) {
console.log("You clicked a td: " + target.textContent);
} else {
console.log("That wasn't a td in the container table");
}
});
table {
border-collapse: collapse;
border: 1px solid #ddd;
}
th, td {
padding: 4px;
border: 1px solid #ddd;
font-weight: normal;
}
th.rowheader {
text-align: left;
}
td {
cursor: pointer;
}
<table id="container">
<thead>
<tr>
<th>Language</th>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
</thead>
<tbody>
<tr>
<th class="rowheader">English</th>
<td>one</td>
<td>two</td>
<td>three</td>
</tr>
<tr>
<th class="rowheader">Español</th>
<td>uno</td>
<td>dos</td>
<td>tres</td>
</tr>
<tr>
<th class="rowheader">Italiano</th>
<td>uno</td>
<td>due</td>
<td>tre</td>
</tr>
</tbody>
</table>
Prima di entrare nei dettagli, ricordiamoci come funzionano gli eventi DOM.
Gli eventi DOM vengono inviati dal documento all'elemento target (la fase di acquisizione ), quindi rimbalzano dall'elemento target al documento ( fase di bubbling ). Questo grafico nelle specifiche degli eventi DOM3 precedenti (ora sostituito, ma il grafico è ancora valido) lo mostra davvero bene:
Non tutti gli eventi si diffondono, ma molti lo fanno, incluso click
.
I commenti nell'esempio di codice sopra descrivono come funziona. matches
controlla se un elemento corrisponde a un selettore CSS, ma ovviamente puoi verificare se qualcosa corrisponde ai tuoi criteri in altri modi se non vuoi usare un selettore CSS.
Quel codice è scritto per richiamare verbalmente i singoli passaggi, ma su browser vagamente moderni (e anche su IE se usi un polyfill), puoi usare closest
e contains
invece del ciclo:
var target = event.target.closest("td");
console.log("You clicked a td: " + target.textContent);
} else {
console.log("That wasn't a td in the container table");
}
Esempio live:
closest
controlla l'elemento su cui lo chiami per vedere se corrisponde al selettore CSS dato e, in tal caso, restituisce lo stesso elemento; in caso contrario, controlla l'elemento genitore per vedere se corrisponde e restituisce il genitore in tal caso; in caso contrario, controlla il genitore del genitore, ecc. Quindi trova l'elemento "più vicino" nell'elenco degli antenati che corrisponde al selettore. Poiché ciò potrebbe andare oltre l'elemento contenitore, il codice sopra utilizzato contains
controlla che se è stato trovato un elemento corrispondente, si trova all'interno del contenitore - poiché agganciando l'evento sul contenitore, hai indicato che vuoi solo gestire elementi all'interno di quel contenitore .
Tornando al nostro esempio di tabella, ciò significa che se si dispone di una tabella all'interno di una cella della tabella, questa non corrisponderà alla cella della tabella contenente la tabella:
Fondamentalmente è il modo in cui l'associazione è fatta all'elemento. .click
si applica al DOM corrente, mentre .on
(utilizzando la delega) continuerà ad essere valido per i nuovi elementi aggiunti al DOM dopo l'associazione degli eventi.
Quale è meglio usare, direi che dipende dal caso.
Esempio:
<ul id="todo">
<li>Do 1</li>
<li>Do 2</li>
<li>Do 3</li>
<li>Do 4</li>
</ul>
Evento clic:
$("li").click(function () {
$(this).remove ();
});
Evento .on:
$("#todo").on("click", "li", function () {
$(this).remove();
});
Nota che ho separato il selettore in .on. Spiegherò perché.
Supponiamo che dopo questa associazione, facciamo quanto segue:
$("#todo").append("<li>Do 5</li>");
È qui che noterai la differenza.
Se l'evento è stato associato tramite .click, l'attività 5 non obbedirà all'evento click e quindi non verrà rimosso.
Se è stato associato tramite .on, con il selettore separato, obbedirà.
Per capire prima la delega degli eventi, dobbiamo sapere perché e quando effettivamente abbiamo bisogno o vogliamo la delega degli eventi.
Potrebbero esserci molti casi, ma discutiamo di due casi d'uso importanti per la delega degli eventi. 1. Il primo caso è quando abbiamo un elemento con molti elementi figlio che ci interessano. In questo caso, invece di aggiungere un gestore eventi a tutti questi elementi figlio, lo aggiungiamo semplicemente all'elemento padre e quindi determiniamo su quale elemento figlio l'evento è stato generato.
2.Il secondo caso d'uso per la delega di eventi è quando vogliamo un gestore di eventi collegato a un elemento che non è ancora nel DOM quando viene caricata la nostra pagina. Questo, ovviamente, perché non possiamo aggiungere un gestore di eventi a qualcosa che non è sulla nostra pagina, quindi in caso di deprecazione stiamo codificando.
Supponiamo di avere un elenco di 0, 10 o 100 elementi nel DOM quando carichi la pagina e che altri elementi siano in attesa di aggiungerli nell'elenco. Quindi non c'è modo di collegare un gestore di eventi per gli elementi futuri o quegli elementi non sono ancora stati aggiunti nel DOM, e inoltre potrebbero esserci molti elementi, quindi non sarebbe utile avere un gestore di eventi collegato a ciascuno di loro.
Delegazione di eventi
Va bene, quindi per parlare della delega degli eventi, il primo concetto di cui dobbiamo effettivamente parlare è il gorgoglio degli eventi.
Bubbling di eventi: il bubbling di eventi significa che quando un evento viene attivato o attivato su un elemento DOM, ad esempio facendo clic sul nostro pulsante qui sull'immagine qui sotto, lo stesso evento esatto viene attivato anche su tutti gli elementi principali.
L'evento viene prima attivato sul pulsante, ma poi verrà attivato anche su tutti gli elementi padre uno alla volta, quindi si attiverà anche sul paragrafo nella sezione dell'elemento principale e fino in fondo in un albero DOM fino all'elemento HTML che è la radice. Quindi diciamo che l'evento bolle all'interno dell'albero DOM, ed è per questo che si chiama gorgogliamento.
Elemento target: l'elemento su cui l'evento è stato effettivamente generato per la prima volta chiamato elemento target, quindi l'elemento che ha causato l'evento, è chiamato elemento target. Nel nostro esempio qui sopra è, ovviamente, il pulsante che è stato cliccato. La parte importante è che questo elemento target viene memorizzato come proprietà nell'oggetto evento, Ciò significa che tutti gli elementi parent su cui verrà generato l'evento conosceranno anche l'elemento target dell'evento, quindi dove l'evento è stato generato per la prima volta.
Questo ci porta alla delegazione degli eventi perché se l'evento si verifica nella struttura ad albero DOM e se sappiamo dove l'evento è stato generato, possiamo semplicemente collegare un gestore eventi a un elemento padre e attendere che l'evento si riempia di bolle, e possiamo quindi fare tutto ciò che intendevamo fare con il nostro elemento target. Questa tecnica è chiamata delega degli eventi. In questo esempio, potremmo semplicemente aggiungere il gestore eventi all'elemento principale.
Va bene, quindi, ancora una volta, la delega degli eventi non è quella di impostare il gestore eventi sull'elemento originale a cui siamo interessati ma di collegarlo a un elemento padre e, in sostanza, catturare l'evento lì perché bolle. Possiamo quindi agire sull'elemento che ci interessa utilizzare la proprietà dell'elemento target.
Esempio: ora supponiamo che nella nostra pagina siano presenti due voci di elenco, dopo aver aggiunto a livello di codice elementi in tale elenco, vogliamo eliminare uno o più elementi da essi. Usando la tecnica di delega di eventi possiamo raggiungere facilmente il nostro scopo.
<div class="body">
<div class="top">
</div>
<div class="bottom">
<div class="other">
<!-- other bottom elements -->
</div>
<div class="container clearfix">
<div class="income">
<h2 class="icome__title">Income</h2>
<div class="income__list">
<!-- list items -->
</div>
</div>
<div class="expenses">
<h2 class="expenses__title">Expenses</h2>
<div class="expenses__list">
<!-- list items -->
</div>
</div>
</div>
</div>
</div>
Aggiunta di elementi in tali elenchi:
const DOMstrings={
type:{
income:'inc',
expense:'exp'
},
incomeContainer:'.income__list',
expenseContainer:'.expenses__list',
container:'.container'
}
var addListItem = function(obj, type){
//create html string with the place holder
var html, element;
if(type===DOMstrings.type.income){
element = DOMstrings.incomeContainer
html = `<div class="item clearfix" id="inc-${obj.id}">
<div class="item__description">${obj.descripiton}</div>
<div class="right clearfix">
<div class="item__value">${obj.value}</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>`
}else if (type ===DOMstrings.type.expense){
element=DOMstrings.expenseContainer;
html = ` <div class="item clearfix" id="exp-${obj.id}">
<div class="item__description">${obj.descripiton}</div>
<div class="right clearfix">
<div class="item__value">${obj.value}</div>
<div class="item__percentage">21%</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>`
}
var htmlObject = document.createElement('div');
htmlObject.innerHTML=html;
document.querySelector(element).insertAdjacentElement('beforeend', htmlObject);
}
Elimina elementi:
var ctrlDeleteItem = function(event){
// var itemId = event.target.parentNode.parentNode.parentNode.parentNode.id;
var parent = event.target.parentNode;
var splitId, type, ID;
while(parent.id===""){
parent = parent.parentNode
}
if(parent.id){
splitId = parent.id.split('-');
type = splitId[0];
ID=parseInt(splitId[1]);
}
deleteItem(type, ID);
deleteListItem(parent.id);
}
var deleteItem = function(type, id){
var ids, index;
ids = data.allItems[type].map(function(current){
return current.id;
});
index = ids.indexOf(id);
if(index>-1){
data.allItems[type].splice(index,1);
}
}
var deleteListItem = function(selectorID){
var element = document.getElementById(selectorID);
element.parentNode.removeChild(element);
}
Un delegato in C # è simile a un puntatore a funzione in C o C ++. L'uso di un delegato consente al programmatore di incapsulare un riferimento a un metodo all'interno di un oggetto delegato. L'oggetto delegato può quindi essere passato al codice che può chiamare il metodo di riferimento, senza dover sapere in fase di compilazione quale metodo verrà invocato.
Vedi questo link -> http://www.akadia.com/services/dotnet_delegates_and_events.html
La delega di eventi fa uso di due funzionalità spesso trascurate degli eventi JavaScript: il bubbling di eventi e l'elemento target. Quando un evento viene attivato su un elemento, ad esempio un clic del mouse su un pulsante, lo stesso evento viene attivato anche su tutti gli antenati di quell'elemento . Questo processo è noto come gorgogliamento di eventi; l'evento bolle dall'elemento di origine all'inizio dell'albero DOM.
Immagina una tabella HTML con 10 colonne e 100 righe in cui desideri che accada qualcosa quando l'utente fa clic su una cella della tabella. Ad esempio, una volta ho dovuto rendere modificabile ogni cella di una tabella di quella dimensione quando si fa clic. L'aggiunta di gestori di eventi a ciascuna delle 1000 celle sarebbe un grave problema di prestazioni e, potenzialmente, una fonte di perdite di memoria che causano arresti anomali del browser. Invece, usando la delega degli eventi, aggiungere un solo gestore eventi all'elemento tabella, intercettare l'evento click e determinare quale cella è stata cliccata.
Collegare un listener di eventi a un elemento padre che viene generato quando si verifica un evento su un elemento figlio.
Propagazione dell'eventoQuando un evento si sposta attraverso il DOM da figlio a un elemento padre, si chiama Propagazione evento , poiché l'evento si propaga o si sposta attraverso il DOM.
In questo esempio, un evento (clic) da un pulsante viene passato al paragrafo principale.
$(document).ready(function() {
$(".spoiler span").hide();
/* add event onclick on parent (.spoiler) and delegate its event to child (button) */
$(".spoiler").on( "click", "button", function() {
$(".spoiler button").hide();
$(".spoiler span").show();
} );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<p class="spoiler">
<span>Hello World</span>
<button>Click Me</button>
</p>