Gli elementi dell'albero DOM con ID diventano variabili globali?


364

Lavorando su un'idea per un semplice wrapper HTMLElement mi sono imbattuto in quanto segue per Internet Explorer e Chrome :

Per un dato HTMLElement con ID nella struttura DOM, è possibile recuperare il div utilizzando il suo ID come nome della variabile. Quindi per un div come

<div id="example">some text</div>

in Internet Explorer 8 e Chrome puoi fare:

alert(example.innerHTML); //=> 'some text'

o

alert(window['example'].innerHTML); //=> 'some text'

Quindi, questo significa che ogni elemento dell'albero DOM viene convertito in una variabile nello spazio dei nomi globale? E significa anche che si può usare questo come sostituto del getElementByIdmetodo in questi browser?



1
@Bergi, il commento che afferma di non farlo ora è obsoleto e persino non valido. Pertanto, non riesco a trovare un motivo concreto per non utilizzare questa funzione.
ESR,

@EdmundReed Potresti voler leggere di nuovo la risposta alla domanda collegata - è ancora una cattiva idea: " variabili globali dichiarate implicitamente " non hanno un supporto per gli strumenti e " portano a codice fragile ". Non chiamarlo "funzionalità", la risposta che segue spiega come è solo un bug che è diventato parte dello standard per motivi di compatibilità.
Bergi,

1
@Bergi abbastanza giusto, hai ragione. Penso comunque che sia una caratteristica davvero accurata, ed è considerata problematica solo perché le persone non ne sono consapevoli. Ecco come immagino di usarlo: codepen.io/esr360/pen/WEavGE?editors=1000#0
ESR

@EdmundReed È meno problematico se non si separano correttamente il contenuto e la logica, ovviamente. Inoltre consiglio di non usare mai gestori di eventi inline o installare metodi personalizzati su elementi DOM abusandoli come spazi dei nomi (si noti che non è un "ambito").
Bergi,

Risposte:


395

Ciò che dovrebbe accadere è che gli "elementi nominati" vengano aggiunti come proprietà apparenti documentdell'oggetto. Questa è una pessima idea, in quanto consente ai nomi degli elementi di scontrarsi con proprietà reali di document.

IE ha peggiorato la situazione aggiungendo anche elementi nominati come proprietà windowdell'oggetto. Questo è doppiamente negativo in quanto ora devi evitare di nominare i tuoi elementi dopo che qualsiasi membro dell'oggetto documento windowche tu (o qualsiasi altro codice di libreria nel tuo progetto) potresti voler usare.

Significa anche che questi elementi sono visibili come variabili globali. Fortunatamente in questo caso qualsiasi globale reale varo functiondichiarazioni nel tuo codice li ombreggiano, quindi non devi preoccuparti così tanto di nominare qui, ma se provi a fare un compito a una variabile globale con un nome in conflitto e ti dimentichi di dichiarare esso var, visualizzerà un errore in IE mentre tenta di assegnare il valore all'elemento stesso.

In genere è considerata una cattiva pratica omettere vare fare affidamento sul fatto che gli elementi denominati siano visibili su windowo come globali. Attenersi a document.getElementById, che è più ampiamente supportato e meno ambiguo. Puoi scrivere una banale funzione wrapper con un nome più breve se non ti piace la digitazione. In entrambi i casi, non ha senso utilizzare una cache di ricerca id-to-element, poiché i browser in genere ottimizzano la getElementByIdchiamata per utilizzare comunque una ricerca rapida; tutto ciò che ottieni sono problemi quando gli elementi cambiano ido vengono aggiunti / rimossi dal documento.

Opera ha copiato IE, quindi WebKit si è unito e ora sia la pratica precedentemente non standardizzata di mettere elementi nominati sulle documentproprietà, sia la pratica precedentemente solo IE di metterli su windowsono stati standardizzati da HTML5, il cui approccio è di documentare e standardizzare ogni terribile pratica inflitta a noi dagli autori del browser, rendendoli parte del Web per sempre. Quindi Firefox 4 supporterà anche questo.

Cosa sono gli "elementi nominati"? Qualunque cosa abbia un id, e qualsiasi cosa namevenga usata per scopi di "identificazione": ovvero, forme, immagini, ancore e poche altre, ma non altre istanze non correlate di un nameattributo, come i nomi di controllo nei campi di input del modulo, i nomi dei parametri in <param>o digitare i metadati <meta>. L '"identificazione" nameè quella che dovrebbe essere evitata a favore id.


5
Questa è una risposta chiara, grazie. Non è stata una mia idea omettere document.getElementById (beh, di fatto uso xpath dove possibile per cercare elementi / proprietà degli elementi al giorno d'oggi). Mi sono imbattuto in questa (cattiva) pratica per oggetti nominati ed ero curioso di sapere da dove venisse. Hai risposto abbastanza attentamente; ora sappiamo perché può essere trovato anche in Chrome (webkit).
KooiInc

18
Un'eccezione a "uso di namedovrebbe essere evitato" è con <input>, in cui l' nameattributo gioca un ruolo critico nel formare la chiave delle coppie chiave-valore per l'invio di moduli.
Yahel,

7
FYI Firefox lo fa solo quando messo in modalità stranezze.
Crescent Fresh,

4
@yahelc: questa è esattamente la distinzione che sto facendo. "Non altri usi di namenomi di controllo simili nei campi di input del modulo ..."
bobince

13
PERCHÉ!? C'è qualcosa che possiamo fare per fermare questa follia? Le mie funzioni sono state ridefinite da riferimenti ad elementi e ho impiegato un'ora per eseguire il debug. :(
Farzher,

52

Come menzionato nella risposta precedente, questo comportamento è noto come accesso con nome sull'oggetto finestra . Il valore namedell'attributo per alcuni elementi e il valore iddell'attributo per tutti gli elementi sono resi disponibili come proprietà windowdell'oggetto globale . Questi sono noti come elementi denominati. Poiché windowè l'oggetto globale nel browser, ogni elemento denominato sarà accessibile come variabile globale.

Questo è stato originariamente aggiunto da Internet Explorer e alla fine è stato implementato da tutti gli altri browser semplicemente per la compatibilità con i siti che dipendono da questo comportamento. È interessante notare che Gecko (il motore di rendering di Firefox) ha scelto di implementarlo solo in modalità stranezze , mentre altri motori di rendering lo hanno lasciato attivo in modalità standard.

Tuttavia, come di Firefox 14, Firefox ora supporta il nome di accesso sul windowoggetto in modalità standard pure. Perché hanno cambiato questo? Risulta che ci sono ancora molti siti che si basano su questa funzionalità in modalità standard. Microsoft ha persino rilasciato una demo di marketing che ha impedito il funzionamento della demo in Firefox.

Webkit ha recentemente considerato il contrario , relegando l'accesso denominato windowsull'oggetto solo alla modalità stranezze. Hanno deciso di non farlo con lo stesso ragionamento di Gecko.

Quindi ... folle come sembra che questo comportamento sia ora tecnicamente sicuro da usare nell'ultima versione di tutti i principali browser in modalità standard . Ma mentre l'accesso con nome può sembrare in qualche modo conveniente, non dovrebbe essere usato .

Perché? Gran parte del ragionamento può essere riassunto in questo articolo sul perché le variabili globali sono cattive . In poche parole, avere un sacco di variabili globali extra porta a più bug. Supponiamo che tu digiti accidentalmente il nome di a vare ti capiti di digitare un idnodo DOM, SORPRESA!

Inoltre, nonostante sia standardizzato, esistono ancora alcune discrepanze nelle implementazioni di accesso con nome del browser.

  • IE rende erroneamente il valore namedell'attributo accessibile per gli elementi del modulo (input, select, ecc.).
  • Gecko e Webkit in modo errato NON rendono i <a>tag accessibili tramite il loro nameattributo.
  • Gecko gestisce in modo errato più elementi con lo stesso nome (restituisce un riferimento a un singolo nodo invece di una matrice di riferimenti).

E sono sicuro che c'è di più se provi ad usare l'accesso con nome su casi limite.

Come menzionato in altre risposte, utilizzare document.getElementByIdper ottenere un riferimento a un nodo DOM dal suo id. Se è necessario ottenere un riferimento a un nodo mediante il suo nameattributo, utilizzare document.querySelectorAll.

Per favore, non propagare questo problema usando l'accesso denominato nel tuo sito. Così tanti sviluppatori web hanno perso tempo cercando di rintracciare questo comportamento magico . Dobbiamo davvero agire e ottenere motori di rendering per disattivare l'accesso denominato in modalità standard. A breve termine interromperà alcuni siti facendo cose cattive, ma a lungo termine contribuirà a far avanzare il web.

Se sei interessato ne parlo più dettagliatamente sul mio blog - https://www.tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ .


3
Solo una nota all'ovvio avvertimento alla premessa che "non dovrebbe essere usato". Cioè, "non dovrebbe essere usato A meno che tu non sia un cowboy da codice". I cowboy del codice ci provano.
Jeremy Foster,

5
@jeremyfoster a meno che "cowboy di codice" non significhi qualcuno che usa e diffonde cattive implementazioni ostili agli sviluppatori, non sono assolutamente d'accordo.
Patrick Roberts,

2
Un segno di un buon cowboy è che molti non sono d'accordo. Ma ora sono come il cowboy filosofico o qualcosa del genere.
Jeremy Foster,

Più persone dovrebbero usare document.querySelectorAlle document.querySelectorquando accedono al DOM. +1 per il buon suggerimento di usarlo. L'accesso agli elementi tramite selettore è sicuramente un processo più efficiente.
Travis J,

20

In getElementById()questi casi è necessario attenersi , ad esempio:

document.getElementById('example').innerHTML

A IE piace mescolare elementi name e ID attributi nello spazio dei nomi globale, quindi è meglio essere espliciti su ciò che stai cercando di ottenere.


3

Si lo fanno.

Testato in Chrome 55, Firefox 50, IE 11, IE Edge 14 e Safari 10
con il seguente esempio:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <div id="im_not_particularly_happy_with_that">
    Hello World!
  </div>
  <script>
    im_not_particularly_happy_with_that.innerText = 'Hello Internet!';
  </script>
  <!-- Looking at you W3 HTML5 spec group _ -->
</body>
</html>

http://jsbin.com/mahobinopa/edit?html,output


1
Anche a Opera. Tuttavia, ritengo che l'obiezione a questo meccanismo espressa in questa pagina sia presa molto bene.
ncmathsadist,

1

Dovrebbe apparire la domanda: "I tag HTML con ID forniti diventano elementi DOM accessibili a livello globale?"

La risposta è si!

È così che doveva funzionare, ed è per questo che gli ID sono stati introdotti dal W3C per cominciare .: L'ID di un tag HTML in un ambiente di script analizzato diventa il relativo handle di elemento DOM.

Tuttavia, Netscape Mozilla ha rifiutato di conformarsi al W3C (a loro intruso) e ha continuato ostinatamente a utilizzare l'attributo Nome deprecato per creare il caos e quindi rompere la funzionalità di scripting e la convenienza della codifica introdotta dall'introduzione degli ID univoci del W3C.

Dopo il fiasco di Netscape Navigator 4.7, i loro sviluppatori sono andati tutti e si sono infiltrati nel W3C, mentre i loro associati stavano sostituendo il Web con pratiche sbagliate ed esempi di uso improprio. Forzare l'uso e il riutilizzo dell'attributo Nome già deprecato [! Che non intendeva essere univoco] alla pari degli attributi ID in modo che gli script che utilizzavano gli handle ID per accedere a particolari elementi DOM si interrompessero semplicemente!

E hanno fatto ciò che avrebbero anche scritto e pubblicato ampie lezioni ed esempi di codifica [il loro browser non riconoscerebbe comunque] come document.all.ElementID.propertyinvece di ElementID.propertyrenderlo almeno inefficiente e dare al browser più sovraccarico nel caso in cui non lo interrompesse Dominio HTML utilizzando lo stesso token per il nome (ora [1996-97], obsoleto) e l'attributo ID standard che gli fornisce lo stesso valore token.

Sono riusciti facilmente a convincere - a quel tempo - l'esercito schiacciante di ignoranti dilettanti di scrittura di codice che nomi e ID sono praticamente gli stessi, tranne per il fatto che l'attributo ID è più breve e quindi salva byte e più conveniente per il programmatore rispetto all'antica proprietà Name. Che era ovviamente una bugia. Oppure - nei loro articoli pubblicati in sostituzione di HTML, articoli convincenti che è necessario fornire sia il nome che l'ID ai tag affinché siano accessibili dal motore di scripting.

Mosaic Killers [nome in codice "Mozilla"] erano così incazzati che pensavano "se andiamo giù, così dovrebbe Internet".

Il nascente Microsoft - d'altra parte - era così ingenuo che pensava di dover mantenere il deprecato e contrassegnato per l'eliminazione della proprietà Name e trattarlo come se fosse un ID che è un identificatore univoco in modo da non interrompere la funzionalità di scripting di vecchie pagine codificate dai tirocinanti di Netscape. Erano mortalmente sbagliati ...

E la restituzione di una raccolta di array di elementi in conflitto con ID non è stata nemmeno una soluzione a questo deliberato problema creato dall'uomo. In realtà ha sconfitto l'intero scopo.

E questa è l'unica ragione per cui il W3C è diventato brutto e ci ha dato idiocie come document.getElementByIde il dannato rococò che accompagna la fastidiosa sintassi del genere ... (...)

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.