Bubble.js una soluzione 1.6K per un problema comune

Uno dei compiti più comuni nello sviluppo web è la gestione degli eventi. Il nostro codice JavaScript di solito ascolta gli eventi inviati dagli elementi DOM. 

Questo è il modo in cui otteniamo le informazioni dall'utente: cioè, lui o lei fa clic, digita, interagisce con la nostra pagina e dobbiamo sapere quando ciò accadrà. Aggiungere listener di eventi sembra banale ma potrebbe essere un processo difficile.

In questo articolo, vedremo un vero problema del caso e la sua soluzione 1.6K.

Il problema

Un mio amico lavora come sviluppatore junior. Come tale, non ha molta esperienza con JavaScript vaniglia; tuttavia, ha dovuto iniziare a utilizzare framework come AngularJS ed Ember senza avere la comprensione fondamentale della relazione DOM-to-JavaScript. Durante il suo tempo come sviluppatore junior, è stato messo a capo di un piccolo progetto: un sito web di campagne di una sola pagina con quasi nessun JavaScript coinvolto. Ha affrontato un problema piccolo ma molto interessante che alla fine mi ha portato a scrivere Bubble.js.

Immagina di avere un popup. Uno stile piacevole

elemento:

...

Ecco il codice che usiamo per mostrare un messaggio:

var popup = document.querySelector ('. popup'); var showMessage = function (msg) popup.style.display = 'block'; popup.innerHTML = msg;  ... showMessage ('Caricamento in corso, attendere prego');

Abbiamo un'altra funzione HideMessage che cambia il display proprietà a nessuna e nasconde il popup. L'approccio potrebbe funzionare nel caso più generico ma ha ancora alcuni problemi. 

Ad esempio, supponiamo di dover implementare una logica aggiuntiva se i problemi si presentano uno per uno. Diciamo che dobbiamo aggiungere due pulsanti al contenuto del popup - e No.

var content = 'Sei sicuro?
'; contenuto + = 'Sì'; contenuto + = 'No'; showMessage (contenuto);

Quindi, come sapremo che l'utente fa clic su di loro? Dobbiamo aggiungere listener di eventi a ognuno dei link. 

Per esempio:

var addListeners = function (yesCB, noCB) popup.querySelector ('. popup - yes'). addEventListener ('click', yesCB); popup.querySelector ('. popup - no'). addEventListener ('click', noCB);  showMessage (contenuto); addListeners (function () console.log ('Pulsante Sì cliccato');, function () console.log ('Nessun pulsante cliccato'););

Il codice sopra funziona durante la prima esecuzione. E se avessimo bisogno di un nuovo pulsante o, peggio ancora, se avessimo bisogno di un diverso tipo di pulsante? Cioè, e se dovessimo continuare a usare  elementi ma con nomi di classi diversi? Non possiamo usare lo stesso addListeners funzione, ed è fastidioso creare un nuovo metodo per ogni variazione di popup.

Qui sono dove i problemi diventano visibili:

  • Dobbiamo aggiungere gli ascoltatori ancora e ancora. In realtà, dobbiamo farlo ogni volta che l'HTML è presente nel popup
    è cambiato.
  • Potremmo collegare gli ascoltatori di eventi solo se il contenuto del popup è aggiornato. Solo dopo il showMessage chiamata. Dobbiamo pensarci sempre e sincronizzare i due processi.
  • Il codice che aggiunge gli ascoltatori ha una forte dipendenza - il apparire variabile. Dobbiamo chiamarlo querySelector funzione al posto di document.querySelector. Altrimenti, potremmo selezionare un elemento sbagliato.
  • Una volta che cambiamo la logica nel messaggio, dobbiamo cambiare i selettori e probabilmente il addEventListener chiamate. Non è affatto ASCIUTTO.

Ci deve essere un modo migliore per farlo.

Sì, c'è un approccio migliore. E no, la soluzione non è usare un framework.

Prima di rivelare la risposta parliamo un po 'degli eventi nell'albero DOM.

Comprendere la gestione degli eventi

Gli eventi sono una parte essenziale dello sviluppo web. Aggiungono interattività alle nostre applicazioni e fungono da ponte tra la logica aziendale e l'utente. Ogni elemento DOM può inviare eventi. Tutto ciò che dobbiamo fare è iscriversi a questi eventi ed elaborare l'oggetto evento ricevuto.

C'è un termine propagazione dell'evento che sta dietro evento ribollente e cattura di eventi entrambi sono due modi di gestire gli eventi in DOM. Usiamo il seguente markup e vediamo la differenza tra loro.

cliccami

Ci collegheremo clic gestori di eventi per entrambi gli elementi. Tuttavia, poiché ci sono nidificati l'uno nell'altro, entrambi riceveranno il clic evento.

document.querySelector ('. wrapper'). addEventListener ('click', function (e) console.log ('. wrapper cliccato');); document.querySelector ('a'). addEventListener ('click', function (e) console.log ('a clic su'););

Una volta premuto il collegamento, nella console viene visualizzato il seguente output:

un clic su. wrapper cliccato

Quindi, in effetti entrambi gli elementi ricevono il clic evento. Innanzitutto, il link e poi il

. Questo è l'effetto bubbling. Dall'elemento più profondo possibile ai suoi genitori. C'è un modo per fermare il gorgoglio. Ogni gestore riceve un oggetto evento che ha stopPropagation metodo:

document.querySelector ('a'). addEventListener ('click', function (e) e.stopPropagation (); console.log ('a clickked'););

Usando stopPropagation funzione, indichiamo che l'evento non deve essere inviato ai genitori.

A volte potrebbe essere necessario invertire l'ordine e fare in modo che l'evento venga catturato dall'elemento esterno. Per raggiungere questo obiettivo, dobbiamo utilizzare un terzo parametro in addEventListener. Se passiamo vero come valore lo faremo esfogo. Per esempio:

document.querySelector ('. wrapper'). addEventListener ('click', function (e) console.log ('. wrapper cliccato');, true); document.querySelector ('a'). addEventListener ('click', function (e) console.log ('a clic su');, true);

È così che il nostro browser elabora gli eventi quando interagiamo con la pagina.

La soluzione

Okay, allora perché abbiamo passato una parte dell'articolo parlando di bolle e cattura. Li abbiamo citati perché il bubbling è la risposta dei nostri problemi con il popup. Dovremmo impostare gli ascoltatori dell'evento non sui collegamenti ma su

direttamente.

var content = 'Sei sicuro?
'; contenuto + = 'Sì'; contenuto + = 'No'; var addListeners = function () popup.addEventListener ('click', function (e) var link = e.target;); showMessage (contenuto); addListeners ();

Seguendo questo approccio, eliminiamo i problemi elencati all'inizio.

  • C'è solo un listener di eventi e lo stiamo aggiungendo una volta. Indipendentemente da ciò che inseriamo nel popup, la cattura degli eventi avverrà nei loro genitori.
  • Non siamo legati al contenuto aggiuntivo. In altre parole, non ci importa quando il showMessage è chiamato. Finché il apparire variabile è vivo prenderemo gli eventi.
  • Perché chiamiamo addListeners una volta, usiamo il apparire variabile anche una volta. Non dobbiamo tenerlo o passarlo tra i metodi.
  • Il nostro codice è diventato flessibile perché abbiamo scelto di non interessare l'HTML passato showMessage. Abbiamo accesso all'ancora cliccata in questo e.target punta all'elemento premuto.

Il codice sopra è migliore di quello con cui abbiamo iniziato. Tuttavia, continua a non funzionare allo stesso modo. Come abbiamo detto, e.target punta al clic etichetta. Quindi, lo useremo per distinguere il e No pulsanti.

var addListeners = function (callbacks) popup.addEventListener ('click', function (e) var link = e.target; var buttonType = link.getAttribute ('classe'); if (callbacks [buttonType]) callbacks [ buttonType] (e););  ... addListeners ('popup - yes': function () console.log ('Yes');, 'popup - no': function () console.log ('No');) ;

Abbiamo recuperato il valore di classe attributo e usarlo come chiave. Le diverse classi puntano a diversi callback.

Tuttavia, non è una buona idea usare il classe attributo. È riservato per applicare gli stili visivi all'elemento e il suo valore può cambiare in qualsiasi momento. Come sviluppatori JavaScript, dovremmo usare dati attributi.

var content = 'Sei sicuro?
'; contenuto + = 'Sì'; contenuto + = 'No';

Anche il nostro codice diventa un po 'migliore. Possiamo rimuovere le virgolette usate in addListeners funzione:

addListeners (yes: function () console.log ('Yes');, no: function () console.log ('No'););

Il risultato potrebbe essere visto in questo JSBin.

Bubble.js

Ho applicato la soluzione di cui sopra in diversi progetti, quindi aveva senso avvolgerla in una libreria. Si chiama Bubble.js ed è disponibile in GitHub. È un file 1.6K che fa esattamente ciò che abbiamo fatto sopra.

Trasformiamo il nostro esempio popup da usare Bubble.js. La prima cosa che dobbiamo cambiare è il markup usato:

var content = 'Sei sicuro?
'; contenuto + = 'Sì'; contenuto + = 'No';

Invece di Dati-action dovremmo usare Dati-bubble-action.

Una volta incluso bubble.min.js nella nostra pagina, abbiamo un globale bolla funzione disponibile. Accetta un selettore di elementi DOM e restituisce l'API della libreria. Il sopra il metodo è quello che aggiunge gli ascoltatori:

bubble ('. popup') .on ('yes', function () console.log ('Yes');) .on ('no', function () console.log ('No'); );

C'è anche una sintassi alternativa:

bubble ('. popup'). on (yes: function () console.log ('Yes');, no: function () console.log ('No'););

Di default, Bubble.js ascolta clic eventi, ma c'è un'opzione per cambiarlo. Aggiungiamo un campo di input e ne ascoltiamo keyup evento:

Il gestore JavaScript continua a ricevere l'oggetto Event. Quindi, in questo caso, siamo in grado di mostrare il testo del campo:

bubble ('. popup'). on (... input: function (e) console.log ('Nuovo valore:' + e.target.value););

A volte abbiamo bisogno di catturare non uno ma molti eventi inviati dallo stesso elemento. Dati-bubble-action accetta più valori separati da virgola:

Trova la variante finale in un JSBin qui.

fallback

La soluzione fornita in questo articolo si basa completamente sul bubbling dell'evento. In alcuni casi e.target potrebbe non indicare l'elemento di cui abbiamo bisogno. 

Per esempio:

per favore, scegliere me!

Se posizioniamo il mouse su "scegli" ed esegui un clic, l'elemento che invia l'evento non è il tag ma il campata elemento.

Sommario

Certo, la comunicazione con il DOM è una parte essenziale del nostro sviluppo di applicazioni, ma è una pratica comune che usiamo i framework solo per bypassare quella comunicazione. 

Non ci piace aggiungere gli ascoltatori ancora e ancora. Non ci piace il debug di bug strani a doppio evento. La verità è che se sappiamo come funziona il browser, siamo in grado di eliminare questi problemi.

Bubble.js è solo un risultato di poche ore di lettura e un'ora di codifica: è la nostra soluzione 1.6K a uno dei problemi più comuni.