Bubble.js En 1.6K løsning på et vanlig problem

En av de vanligste oppgavene i webutvikling er hendelsehåndtering. Vår JavaScript-kode lytter vanligvis til hendelser som sendes av DOM-elementene. 

Slik får vi informasjon fra brukeren: Det vil si, han eller hun klikker, typer, samhandler med siden vår, og vi må vite når dette skjer. Å legge til hendelseslyttere ser trivielt ut, men det kan være en tøff prosess.

I denne artikkelen vil vi se et real-case problem og sin 1.6K løsning.

Problemet

En venn av meg jobber som junior utvikler. Som sådan har han ikke mye erfaring med vanilla JavaScript; Han har imidlertid måttet begynne å bruke rammer som AngularJS og Ember uten å ha den grunnleggende forståelsen av DOM-til-JavaScript-forhold. I løpet av sin tid som junior utvikler ble han ansvarlig for et lite prosjekt: En enkelt side kampanje nettsteder med nesten ingen JavaScript involvert. Han møtte et lite, men veldig interessant problem som til slutt fører meg til å skrive Bubble.js.

Tenk deg at vi har en popup. Et pent stylet

element:

...

Her er koden vi bruker til å vise en melding:

var popup = document.querySelector ('popup'); var showMessage = funksjon (msg) popup.style.display = 'block'; popup.innerHTML = msg;  ... showMessage ('Laster inn. Vennligst vent.');

Vi har en annen funksjon hideMessage som endrer vise eiendom til ingen og gjemmer popupen. Tilnærmingen kan fungere i det mest generiske tilfellet, men har fortsatt noen problemer. 

For eksempel, si at vi må implementere tilleggslogikk hvis problemene kommer i en etter en. La oss si at vi må legge to knapper til innholdet i popup-vinduet - Ja og Nei.

var content = 'Er du sikker?
'; innhold + = 'Ja'; innhold + = 'Nei'; showMessage (innhold);

Så, hvordan vet vi at brukeren klikker på dem? Vi må legge til hendelseslyttere til hver enkelt kobling. 

For eksempel:

var addListeners = funksjon (yesCB, noCB) popup.querySelector ('popup - yes'). addEventListener ('klikk', yesCB); popup.querySelector ('popup - no'). addEventListener ('klikk', noCB);  showMessage (innhold); addListeners (function () console.log ('Yes button clicked');, funksjon () console.log ('Ingen knapp klikket'););

Koden ovenfor fungerer i løpet av første løp. Hva om vi trenger en ny knapp, eller enda verre, hva om vi trenger en annen type knapp? Det er, hva hvis vi skulle fortsette å bruke  elementer, men med forskjellige klassenavn? Vi kan ikke bruke det samme addListeners funksjon, og det er irriterende å lage en ny metode for hver variant av popup.

Her er hvor problemer blir synlige:

  • Vi må legge til lytterne igjen og igjen. Faktisk må vi gjøre dette hver gang når HTML-koden i popup-filen er
    er endret.
  • Vi kunne bare vedvare hendelseslyttere hvis innholdet i popup-oppdateringen er oppdatert. Bare etter Vis melding kall. Vi må tenke på det hele tiden og synkronisere de to prosessene.
  • Koden som legger til lytterne har en vanskelig avhengighet - popup variabel. Vi må ringe det querySelector funksjon i stedet for document.querySelector. Ellers kan vi velge et feil element.
  • Når vi endrer logikken i meldingen, må vi endre selektorer og sannsynligvis addEventListener samtaler. Det er ikke tørt i det hele tatt.

Det må være en bedre måte å gjøre dette på.

Ja, det er en bedre tilnærming. Og nei, løsningen er ikke å bruke et rammeverk.

Før du avslører svaret, la oss snakke litt om hendelsene i DOM-treet.

Forstå hendelseshåndtering

Hendelser er en viktig del av webutviklingen. De legger til interaktivitet i våre applikasjoner og fungerer som en bro mellom forretningslogikken og brukeren. Hvert DOM-element kan sende hendelser. Alt vi trenger å gjøre er å abonnere på disse hendelsene og behandle det mottatte Event-objektet.

Det er et begrep hendelse propagasjon som står bak hendelse bobler og hendelsesfangst begge er to måter å behandle hendelser i DOM. La oss bruke følgende oppføring og se forskjellen mellom dem.

Klikk på meg

Vi vil legge ved klikk hendelseshåndterer til begge elementene. Men fordi det er nestet inn i hverandre, vil de begge motta klikk begivenhet.

document.querySelector ('. wrapper'). addEventListener ('klikk', funksjon (e) console.log ('wrapper clicked');); document.querySelector ('a'). addEventListener ('klikk', funksjon (e) console.log ('et klikk'););

Når vi trykker på linken, ser vi følgende utgang i konsollen:

en klikket .wrapper klikket

Så, faktisk, mottar begge elementene klikk begivenhet. Først, lenken og deretter

. Dette er den boblende effekten. Fra det dypest mulige element til foreldrene sine. Det er en måte å stoppe boblingen på. Hver håndterer mottar et hendelseobjekt som har stopPropagation metode:

document.querySelector ('a'). addEventListener ('klikk', funksjon (e) e.stopPropagation (); console.log ('et klikkt'););

Ved bruk av stopPropagation funksjon, indikerer vi at hendelsen ikke skal sendes til foreldrene.

Noen ganger kan det hende at vi må reversere ordren og få hendelsen tatt av det ytre elementet. For å oppnå dette må vi bruke en tredje parameter i addEventListener. Hvis vi passerer ekte Som en verdi vil vi gjøre eventilasjonsfangst. For eksempel:

document.querySelector ('. wrapper'). addEventListener ('klikk', funksjon (e) console.log ('wrapper clicked');, true); document.querySelector ('a'). addEventListener ('klikk', funksjon (e) console.log ('et klikk');, true);

Slik behandler nettleseren hendelsene når vi samhandler med siden.

Løsningen

Ok, så hvorfor brukte vi en del av artikkelen som snakket om å boble og fange. Vi nevnte dem fordi boblende er svaret på våre problemer med popupen. Vi bør sette hendelseslytterne ikke til koblingene, men til

direkte.

var content = 'Er du sikker?
'; innhold + = 'Ja'; innhold + = 'Nei'; var addListeners = function () popup.addEventListener ('klikk', funksjon (e) var link = e.target;); showMessage (innhold); addListeners ();

Ved å følge denne fremgangsmåten eliminerer vi problemene som er oppført i begynnelsen.

  • Det er bare en hendelse lytter og vi legger den til en gang. Uansett hva vi legger inn i popupen, vil fangst av hendelsene skje i foreldrene deres.
  • Vi er ikke bundet til det ekstra innholdet. Med andre ord bryr vi oss ikke når Vis melding er kalt. Så lenge som popup variabel er i live vil vi fange hendelsene.
  • Fordi vi ringer addListeners en gang bruker vi popup variabel også en gang. Vi behøver ikke å beholde det eller overføre det mellom metodene.
  • Vår kode ble fleksibel fordi vi valgte ikke bryr seg om HTML passert til Vis melding. Vi har tilgang til det klikket ankeret i det e.target peker på det pressede elementet.

Koden ovenfor er bedre enn den vi startet med. Fungerer likevel ikke på samme måte. Som vi sa, e.target peker til det klikket stikkord. Så, vi vil bruke det for å skille mellom Ja og Nei knapper.

var addListeners = funksjon (tilbakeringinger) popup.addEventListener ('klikk', funksjon (e) var link = e.target; var buttonType = link.getAttribute ('class'); hvis (tilbakeringinger [buttonType]) callbacks [ buttonType] (e););  ... addListeners ('popup - yes': funksjon () console.log ('Yes');, 'popup - no': funksjon () console.log ('Nei');) ;

Vi hentet verdien av klasse Tilordne og bruk det som en nøkkel. De ulike klassene peker på forskjellige tilbakekallinger.

Det er imidlertid ikke en god idé å bruke klasse Egenskap. Det er reservert for å bruke visuelle stiler til elementet, og verdien kan endres når som helst. Som JavaScript-utviklere bør vi bruke data egenskaper.

var content = 'Er du sikker?
'; innhold + = 'Ja'; innhold + = 'Nei';

Vår kode blir også litt bedre. Vi kan fjerne anførselstegnene som brukes i addListeners funksjon:

addListeners (yes: function () console.log ('Yes');, nei: funksjon () console.log ('Nei'););

Resultatet kunne ses i denne JSBin.

Bubble.js

Jeg brukte løsningen over i flere prosjekter, så det var fornuftig å pakke det inn i et bibliotek. Den kalles Bubble.js, og den er tilgjengelig i GitHub. Det er 1,6k fil som gjør akkurat det vi gjorde over.

La oss forvandle vårt popup-eksempel til bruk Bubble.js. Det første vi må bytte, er den brukte merkingen:

var content = 'Er du sikker?
'; innhold + = 'Ja'; innhold + = 'Nei';

I stedet for data-handling vi burde bruke data-boble-handling.

Når vi inkluderer bubble.min.js På vår side har vi en global boble funksjon tilgjengelig. Den aksepterer en DOM-elementvelger og returnerer bibliotekets API. De Metoden er den som legger til lytterne:

boble ('popup') .on ('ja', funksjon () console.log ('Ja');) .on ('nei', funksjon () console.log ('Nei'); );

Det finnes også en alternativ syntaks:

boble ('popup'). på (Ja: funksjon () console.log ('Ja');, nei: funksjon () console.log ('Nei'););

Som standard, Bubble.js lytter etter klikk hendelser, men det er et alternativ å endre det. La oss legge til et inntastingsfelt og lytter etter det keyup begivenhet:

JavaScript-håndtereren mottar fortsatt Event-objektet. Så, i dette tilfellet kan vi vise teksten til feltet:

boble ('popup'). på (... input: funksjon (e) console.log ('Ny verdi:' + e.target.value););

Noen ganger trenger vi ikke å fange en, men mange hendelser som sendes av det samme elementet. data-boble-handling aksepterer flere verdier skilt med komma:

Finn den siste varianten i et JSBin her.

fallbacks

Løsningen som er gitt i denne artikkelen er helt avhengig av hendelsen som bobler. I noen tilfeller e.target Kan ikke peke på elementet vi trenger. 

For eksempel:

Vær så snill, velge meg!

Hvis vi legger musen over "velg" og utfører et klikk, er elementet som sender hendelsen ikke det tag men span element.

Sammendrag

Ganske vist er kommunikasjon med DOM en viktig del av vår applikasjonsutvikling, men det er en vanlig praksis at vi bruker rammer bare for å omgå den kommunikasjonen. 

Vi liker ikke å legge til lyttere igjen og igjen. Vi liker ikke å feilsøke merkelige dobbelthendelse-skyte bugs. Sannheten er at hvis vi vet hvordan nettleseren fungerer, kan vi eliminere disse problemene.

Bubble.js er bare ett resultat av få timers lesing og en times koding - det er vår 1.6K løsning på et av de vanligste problemene.