Les éléments d'arborescence DOM avec des ID deviennent-ils des variables globales?

364

En travaillant sur une idée pour un wrapper HTMLElement simple, je suis tombé sur les éléments suivants pour Internet Explorer et Chrome :

Pour un HTMLElement donné avec ID dans l'arborescence DOM, il est possible de récupérer le div en utilisant son ID comme nom de variable. Donc pour un div comme

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

dans Internet Explorer 8 et Chrome, vous pouvez faire:

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

ou

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

Donc, cela signifie-t-il que chaque élément de l'arborescence DOM est converti en une variable dans l'espace de noms global? Et cela signifie-t-il également que l'on peut l'utiliser comme remplacement de la getElementByIdméthode dans ces navigateurs?

KooiInc
la source
1
@Bergi, le commentaire qui dit de ne pas le faire est désormais dépassé et même invalide. Par conséquent, je ne trouve aucune raison concrète de ne pas utiliser cette fonctionnalité.
ESR
@EdmundReed Vous voudrez peut-être relire la réponse à la question liée - c'est toujours une mauvaise idée: les " variables globales déclarées implicitement " ont un support d'outils nul ou nul et " conduisent à du code fragile ". Ne l'appelez pas une "fonctionnalité", la réponse ci-dessous explique comment c'est juste un bug qui est devenu une partie de la norme pour des raisons de compatibilité.
Bergi
1
@Bergi, vous avez raison. Je pense toujours que c'est une fonctionnalité vraiment intéressante et n'est considérée comme problématique que parce que les gens n'en sont pas conscients. Voici comment j'envisage de l'
ESR
@EdmundReed C'est moins problématique si vous ne séparez pas correctement le contenu et la logique bien sûr. Je recommande également de ne jamais utiliser de gestionnaires d'événements en ligne ou d'installer des méthodes personnalisées sur les éléments DOM en les abusant comme des espaces de noms (notez que ce n'est pas une "portée").
Bergi

Réponses:

395

Ce qui est censé se produire, c'est que des «éléments nommés» sont ajoutés en tant que propriétés apparentes de l' documentobjet. C'est une très mauvaise idée, car elle permet aux noms d'éléments d'entrer en conflit avec les propriétés réelles de document.

IE a aggravé la situation en ajoutant également des éléments nommés en tant que propriétés de l' windowobjet. Ceci est doublement mauvais dans la mesure où vous devez maintenant éviter de nommer vos éléments après qu'un membre de documentl' windowobjet ou de l' objet que vous (ou tout autre code de bibliothèque de votre projet) voudriez utiliser.

Cela signifie également que ces éléments sont visibles en tant que variables de type global. Heureusement dans ce cas, toutes les déclarations varou functiondéclarations globales réelles dans votre code les masquent, vous n'avez donc pas à vous soucier autant de la dénomination ici, mais si vous essayez de faire une affectation à une variable globale avec un nom conflictuel et que vous oubliez de déclarer il var, vous obtiendrez une erreur dans IE car il tente d'affecter la valeur à l'élément lui - même.

Il est généralement considéré comme une mauvaise pratique d'omettre var, ainsi que de s'appuyer sur des éléments nommés visibles sur windowou en tant que globaux. Tenez-vous-en à document.getElementById, qui est plus largement pris en charge et moins ambigu. Vous pouvez écrire une fonction wrapper triviale avec un nom plus court si vous n'aimez pas la saisie. Quoi qu'il en soit, il est inutile d'utiliser un cache de recherche id-to-element, car les navigateurs optimisent généralement l' getElementByIdappel pour utiliser une recherche rapide de toute façon; tout ce que vous obtenez est des problèmes lorsque des éléments changent idou sont ajoutés / supprimés du document.

Opera copié IE, puis WebKit a rejoint dans, et maintenant à la fois le précédemment non normalisée pratique de mettre des éléments figurant sur les documentpropriétés et la pratique précédemment IE seulement de les mettre sur windowsont en cours normalisés par HTML5, dont l' approche est de documenter et d' uniformiser tous les terrible pratique que nous ont infligée les auteurs de navigateurs, ce qui en fait une partie du Web pour toujours. Donc Firefox 4 le supportera également.

Que sont les «éléments nommés»? Tout ce qui a un id, et tout ce qui nameest utilisé à des fins `` d'identification '': c'est-à-dire les formulaires, les images, les ancres et quelques autres, mais pas d'autres instances indépendantes d'un nameattribut, comme les noms de contrôle dans les champs de saisie du formulaire, les noms des paramètres dans <param>ou tapez les métadonnées <meta>. Les «identifiants» namesont ceux qui devraient être évités en faveur de id.

bobince
la source
5
C'est une réponse claire, merci. Ce n'était pas mon idée d'omettre document.getElementById (enfin, en fait, j'utilise xpath dans la mesure du possible pour rechercher des éléments / propriétés d'éléments de nos jours). Je suis tombé sur cette (mauvaise) pratique pour les objets nommés et j'étais curieux de savoir d'où cela venait. Vous y avez répondu assez longuement; nous savons maintenant pourquoi il peut également être trouvé dans Chrome (webkit).
KooiInc
18
Une exception à "l'utilisation de namedevrait être évitée" est avec <input>, où l' nameattribut joue un rôle essentiel dans la formation de la clé des paires clé-valeur pour les soumissions de formulaire.
Yahel
7
FYI Firefox ne fait cela que lorsqu'il est mis en mode excentrique.
Crescent Fresh
4
@yahelc: c'est exactement la distinction que je fais. «Pas d'autres utilisations de namenoms de contrôle similaires dans les champs de saisie de formulaire ...»
bobince
13
POURQUOI!? Y a-t-il quelque chose que nous puissions faire pour arrêter cette folie? Mes fonctions ont été redéfinies par des références à des éléments et il m'a fallu une heure pour déboguer. :(
Farzher
52

Comme mentionné dans la réponse précédente, ce comportement est appelé accès nommé sur l'objet fenêtre . La valeur de l' nameattribut pour certains éléments et la valeur de l' idattribut pour tous les éléments sont mises à disposition en tant que propriétés de l' windowobjet global . Ce sont des éléments nommés. Puisque windowc'est l'objet global dans le navigateur, chaque élément nommé sera accessible en tant que variable globale.

Cela a été ajouté à l'origine par Internet Explorer et a finalement été implémenté par tous les autres navigateurs simplement pour la compatibilité avec les sites qui dépendent de ce comportement. Fait intéressant, Gecko (le moteur de rendu de Firefox) a choisi de l'implémenter en mode excentrique uniquement, tandis que d'autres moteurs de rendu l'ont laissé en mode standard.

Cependant, à partir de Firefox 14, Firefox prend désormais en charge l'accès nommé sur l' windowobjet en mode standard également. Pourquoi ont-ils changé cela? Il s'avère qu'il y a encore beaucoup de sites qui s'appuient sur cette fonctionnalité en mode standard. Microsoft a même publié une démo marketing qui l'a empêché de fonctionner dans Firefox.

Webkit a récemment considéré le contraire , reléguant l'accès nommé sur l' windowobjet au mode excentrique uniquement. Ils ont décidé contre cela par le même raisonnement que Gecko.

Donc… fou comme il semble que ce comportement est désormais techniquement sûr à utiliser dans la dernière version de tous les principaux navigateurs en mode standard . Mais si l'accès nommé peut sembler quelque peu pratique, il ne doit pas être utilisé .

Pourquoi? Une grande partie du raisonnement peut être résumée dans cet article sur les raisons pour lesquelles les variables globales sont mauvaises . Autrement dit, avoir un tas de variables globales supplémentaires conduit à plus de bugs. Disons que vous tapez accidentellement le nom d'un varet qu'il vous arrive de taper un idd'un nœud DOM, SURPRISE!

De plus, bien qu'il soit standardisé, il existe encore quelques différences dans les implémentations d'accès nommé du navigateur.

  • IE rend incorrectement la valeur de l' nameattribut accessible pour les éléments de formulaire (entrée, sélection, etc.).
  • Gecko et Webkit ne rendent pas les <a>balises accessibles via leur nameattribut de manière incorrecte .
  • Gecko ne gère pas correctement plusieurs éléments nommés avec le même nom (il renvoie une référence à un seul nœud au lieu d'un tableau de références).

Et je suis sûr qu'il y a plus si vous essayez d'utiliser l'accès nommé sur les cas marginaux.

Comme mentionné dans d'autres réponses, utilisez document.getElementByIdpour obtenir une référence à un nœud DOM par son id. Si vous devez obtenir une référence à un nœud par son nameattribut, utilisez document.querySelectorAll.

Veuillez ne pas propager ce problème en utilisant l'accès nommé dans votre site. Tant de développeurs Web ont perdu du temps à essayer de retrouver ce comportement magique . Nous devons vraiment agir et obtenir des moteurs de rendu pour désactiver l'accès nommé en mode standard. À court terme, certains sites feront de mauvaises choses, mais à long terme, cela aidera à faire avancer le Web.

Si vous êtes intéressé, j'en parle plus en détail sur mon blog - https://www.tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ .

TJ VanToll
la source
3
Juste une note à la mise en garde évidente à la prémisse que "il ne devrait pas être utilisé". Autrement dit, "il ne doit pas être utilisé À MOINS que vous ne soyez un cow-boy de code." Cowboys de code allez-y.
Jeremy Foster
5
@jeremyfoster, sauf si "code cowboy" signifie quelqu'un qui utilise et propage de mauvaises implémentations hostiles aux développeurs, je suis fortement en désaccord.
Patrick Roberts
2
Une marque d'un bon cowboy est que beaucoup ne sont pas d'accord. Mais maintenant, je suis comme le cow-boy philosophique ou quelque chose comme ça.
Jeremy Foster
Plus de personnes devraient utiliser document.querySelectorAllet document.querySelectoraccéder au DOM. +1 pour la bonne suggestion d'utiliser cela. L'accès aux éléments par sélecteur est définitivement un processus plus efficace.
Travis J du
20

Vous devez vous en tenir à getElementById()ces cas, par exemple:

document.getElementById('example').innerHTML

IE aime mélanger les éléments avec name et les ID attributs dans l'espace de noms global, il vaut donc mieux être explicite sur ce que vous essayez d'obtenir.

Nick Craver
la source
3

Oui, ils le font.

Testé dans Chrome 55, Firefox 50, IE 11, IE Edge 14 et Safari 10
avec l'exemple suivant:

<!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

qff
la source
1
Toujours à Opera. Cependant, je pense que l'objection à ce mécanisme exprimée sur cette page est très bien acceptée.
ncmathsadist le
1

La question devrait sonner: "Les balises HTML avec les ID fournis deviennent-elles des éléments DOM accessibles à l'échelle mondiale?"

La réponse est oui!

C'est ainsi que cela devait fonctionner, et c'est pourquoi les ID ont été introduits par le W3C pour commencer: l'ID d'une balise HTML dans un environnement de script analysé devient son descripteur d'élément DOM correspondant.

Cependant, Netscape Mozilla a refusé de se conformer (à leur intrusion) au W3C et a obstinément continué à utiliser l'attribut Nom obsolète pour créer des ravages et donc casser la fonctionnalité de script et la commodité de codage apportées par l'introduction par le W3C des ID uniques.

Après le fiasco de Netscape Navigator 4.7, leurs développeurs se sont tous infiltrés dans le W3C, tandis que leurs associés ont remplacé le Web par de mauvaises pratiques et des exemples d'utilisation abusive. Forcer l'utilisation et la réutilisation de l'attribut Name déjà obsolète [! Qui n'était pas censé être unique] au même titre que les attributs ID afin que les scripts qui utilisaient des descripteurs ID pour accéder à des éléments DOM particuliers se cassent tout simplement!

Et ils ont fait comme s'ils écrivaient et publiaient également des leçons et des exemples de codage étendus [leur navigateur ne les reconnaîtrait pas de toute façon], comme document.all.ElementID.propertyau lieu de le ElementID.propertyrendre au moins inefficace et de donner au navigateur plus de frais généraux au cas où il ne le casserait pas simplement à Domaine HTML en utilisant le même jeton pour le nom (désormais [1996-97], obsolète) et l'attribut ID standard lui fournissant la même valeur de jeton.

Ils ont facilement réussi à convaincre l'armée écrasante d'amateurs ignorants de l'écriture de code que les noms et les ID sont pratiquement les mêmes, sauf que l'attribut ID est plus court et donc moins d'octets et plus pratique pour le codeur que l'ancienne propriété Name. Ce qui était bien sûr un mensonge. Ou - dans leurs articles HTML remplaçants publiés, des articles convaincants que vous devrez fournir à la fois le nom et l'ID à vos balises pour qu'elles soient accessibles par le moteur de script.

Mosaic Killers [nom de code "Mozilla"] était tellement énervé qu'ils pensaient "si nous descendons, Internet devrait en faire autant".

D'autre part, Microsoft en pleine ascension était si naïf qu'il pensait qu'il fallait conserver la propriété Name obsolète et marquée pour suppression et la traiter comme s'il s'agissait d'un ID qui est un identifiant unique afin de ne pas casser la fonctionnalité de script de anciennes pages codées par les stagiaires de Netscape. Ils se sont trompés mortellement ...

Et le retour d'une collection de tableaux d'éléments en conflit avec l'ID n'était pas non plus une solution à ce problème délibéré créé par l'homme. En fait, cela a déjoué tout le but.

Et c'est la seule raison pour laquelle le W3C est devenu laid et nous a donné des idioties comme document.getElementByIdet la putain de syntaxe ennuyeuse rococo qui l'accompagne ... (...)

Bekim Bacaj
la source