Tiesitkö, että DOM-elementit, joissa on tunnus, ovat käytettävissä JavaScriptissä globaaleina muuttujina? Se on yksi niistä asioista, joka on ollut olemassa ikuisesti, mutta kaivaudun siihen todella ensimmäistä kertaa.
Jos tämä on ensimmäinen kerta, kun kuulet siitä, varaudu! Näemme sen toiminnassa yksinkertaisesti lisäämällä tunnuksen HTML-elementtiin:
Normaalisti määritämme uuden muuttujan käyttämällä querySelector("#cool")
or getElementById("cool")
valitaksesi kyseisen elementin:
var el = querySelector("#cool");
Mutta meillä on itse asiassa jo pääsy siihen #cool
ilman sitä rigamoraalia:
Joten mikä tahansa id
- tai name
attribuuttia – HTML-koodissa voidaan käyttää JavaScriptin avulla window[ELEMENT_ID]
. Jälleen kerran, tämä ei ole aivan "uusi", mutta se on todella harvinaista nähdä.
Kuten arvata saattaa, globaalin laajuuden käyttäminen nimettyjen viitteiden avulla ei ole paras idea. Jotkut ihmiset ovat alkaneet kutsua tätä "maailmanlaajuiseksi saastuttajaksi". Selvitämme miksi näin on, mutta ensin…
Jotkut yhteydet
Tämä lähestymistapa on HTML-määrityksessä, jossa se on kuvattu "nimetyksi pääsyksi Window
esine."
Internet Explorer oli ensimmäinen, joka otti ominaisuuden käyttöön. Myös kaikki muut selaimet lisäsivät sen. Gecko oli tuolloin ainoa selain, joka ei tukenut sitä suoraan standarditilassa, vaan päätti tehdä siitä kokeellisen ominaisuuden. Oli epäröintiä toteuttaa se ollenkaan, mutta se oli edettiin selaimen yhteensopivuuden nimissä (Gecko jopa yritti vakuuttaa WebKit siirtääksesi sen pois standarditilasta) ja lopulta pääsi standarditilaan Firefox 14:ssä.
Yksi asia, jota ei ehkä tiedetä hyvin, on se, että selaimet joutuivat ottamaan käyttöön muutamia varotoimenpiteitä – vaihtelevalla menestyksellä – varmistaakseen, että luodut globaalit eivät rikkoisi verkkosivua. Yksi tällainen toimenpide on…
Muuttuva varjostus
Luultavasti mielenkiintoisin osa tätä ominaisuutta on, että nimetyt elementtiviitteet eivät sitä tee varjostaa olemassa olevia globaaleja muuttujia. Joten jos DOM-elementillä on id
joka on jo määritetty globaaliksi, se ei ohita olemassa olevaa. Esimerkiksi:
window.foo = "bar";
I won't override window.foo
console.log(window.foo); // Prints "bar"
Ja myös päinvastoin:
I will be overridden :(
window.foo = "bar";
console.log(window.foo); // Prints "bar"
Tämä toiminta on välttämätöntä, koska se mitätöi vaaralliset ohitukset, kuten
, joka muuten aiheuttaisi ristiriidan mitätöimällä alert
API. Tämä turvatekniikka saattaa hyvinkin olla syy, miksi sinä – jos olet kuten minä – opit tästä ensimmäistä kertaa.
Kanne nimettyjä globaaleja vastaan
Aiemmin sanoin, että globaalien nimettyjen elementtien käyttäminen viitteinä ei ehkä ole paras idea. Siihen on monia syitä, mikä TJ VanToll on käsitellyt asiaa hienosti blogissaan ja teen yhteenvedon tähän:
- Jos DOM muuttuu, niin myös viittaus. Tämä tekee joistakin todella "hauraita" (spesifikaation termi se) koodia, jossa HTML:n ja JavaScriptin välinen ero voi olla liikaa.
- Satunnaiset viittaukset ovat aivan liian helppoja. Yksinkertainen kirjoitusvirhe saattaa hyvinkin päätellä nimetyn globaalin viittaamisen ja antaa odottamattomia tuloksia.
- Se toteutetaan eri tavalla selaimissa. Meidän pitäisi esimerkiksi päästä käsiksi ankkuriin an
id
- esimerkiksi— mutta jotkin selaimet (eli Safari ja Firefox) palauttavat a
ReferenceError
konsolissa. - Se ei ehkä palauta sitä, mitä ajattelet. Teknisten tietojen mukaan, kun DOM:ssa on useita samannimisen elementin esiintymiä - esimerkiksi kaksi esiintymää
- selaimen pitäisi palauttaa an
HTMLCollection
joukolla esiintymiä. Firefox kuitenkin palauttaa vain ensimmäisen esiintymän. Toisaalta, speksi sanoo meidän pitäisi käyttää yhtä esiintymää anid
joka tapauksessa elementin puussa. Mutta se ei estä sivua toimimasta tai mitään vastaavaa. - Ehkä siinä on suorituskustannukset? Tarkoitan, selaimen täytyy tehdä tuo viiteluettelo ja ylläpitää sitä. Pari ihmistä teki testejä tässä StackOverflow-ketjussa, jossa nimetyt globaalit itse asiassa olivat tehokkaampi yhdessä testissä ja vähemmän suorituskykyinen uudemmassa testissä.
Muita näkökohtia
Oletetaan, että torjumme kritiikin nimettyjen globaalien käyttöä vastaan ja käytämme niitä joka tapauksessa. Kaikki on hyvin. Mutta on joitain asioita, joita sinun kannattaa harkita tehdessäsi.
Polyfillit
Niin äärimmäiseltä kuin se kuulostaakin, tämän tyyppiset yleiset tarkistukset ovat tyypillinen monitäytteiden asetusvaatimus. Katso seuraava esimerkki, jossa asetamme evästeen käyttämällä uutta CookieStore
API, polyfilling se selaimissa, jotka eivät vielä tue sitä:
// Polyfill the CookieStore API if not yet implemented.
// https://developer.mozilla.org/en-US/docs/Web/API/CookieStore
if (!window.cookieStore) {
window.cookieStore = myCookieStorePolyfill;
}
cookieStore.set("foo", "bar");
Tämä koodi toimii täydellisesti Chromessa, mutta aiheuttaa seuraavan virheen Safarissa:
TypeError: cookieStore.set is not a function
Safarista puuttuu tuki CookieStore
API tämän kirjoituksen jälkeen. Tämän seurauksena monitäytettä ei käytetä, koska img
elementin ID luo globaalin muuttujan, joka on ristiriidassa cookieStore
globaali.
JavaScript API -päivitykset
Voimme kääntää tilanteen ja löytää vielä yhden ongelman, jossa selaimen JavaScript-moottorin päivitykset voivat rikkoa nimetyn elementin globaalit viittaukset.
Esimerkiksi:
window.BarcodeDetector.focus();
Tämä komentosarja nappaa viittauksen syöttöelementtiin ja kutsuu focus()
sen päällä. Se toimii oikein. Silti emme tiedä miten pitkä se toimii edelleen.
Näet, globaali muuttuja, jota käytämme viittaamaan syöttöelementtiin, lakkaa toimimasta heti, kun selaimet alkavat tukea BarcodeDetector
API. Siinä vaiheessa, window.BarcodeDetector
global ei enää ole viittaus syöttöelementtiin ja .focus()
heittää "window.BarcodeDetector.focus
ei ole toiminto" -virhe.
Bonus: Kaikki nimetyt elementit eivät tuota globaaleja viittauksia
Haluatko kuulla jotain hauskaa? Vamman loukkaamiseksi nimetyt elementit ovat käytettävissä globaaleina muuttujina vain, jos nimet eivät sisällä muuta kuin kirjainta. Selaimet eivät luo yleistä viittausta elementille, jonka tunnus sisältää erikoismerkkejä ja numeroita, kuten esim hello-world
ja item1
.
Yhteenveto
Tehdään yhteenveto, miten tähän päädyttiin:
- Kaikki yleisimmät selaimet luovat automaattisesti maailmanlaajuiset viittaukset jokaiseen DOM-elementtiin, jossa on an
id
(tai joissain tapauksissa aname
attribuutti). - Näihin elementteihin pääsy maailmanlaajuisten viittausten kautta on epäluotettavaa ja mahdollisesti vaarallista. Käyttää
querySelector
orgetElementById
sen sijaan. - Koska maailmanlaajuiset viittaukset luodaan automaattisesti, niillä voi olla sivuvaikutuksia koodiisi. Tämä on hyvä syy välttää sen käyttöä
id
ominaisuus, ellet todella tarvitse sitä.
Loppujen lopuksi on luultavasti hyvä idea välttää nimettyjen globaalien käyttämistä JavaScriptissä. Lainasin aiemmin spesifikaatiota siitä, kuinka se johtaa "hauraaseen" koodiin, mutta tässä on koko teksti, joka vie asian kotiin:
Yleissääntönä tähän luottaminen johtaa hauraaseen koodiin. Mitkä tunnukset päätyvät tähän sovellusliittymään, voivat vaihdella ajan myötä, kun esimerkiksi verkkoalustaan lisätään uusia ominaisuuksia. Käytä tämän sijaan
document.getElementById()
ordocument.querySelector()
.
Mielestäni se tosiasia, että HTML-spesifikaatio itse suosittelee pysymään poissa tästä ominaisuudesta, puhuu puolestaan.