带有id的DOM树元素是否成为全局变量?

时间:2010-08-08 12:24:44

标签: javascript dom global-variables getelementbyid identifier

针对一个简单的HTMLElement包装器的想法我偶然发现了以下Internet Explorer和Chrome

对于DOM树中具有ID的给定HTMLElement,可以使用其ID作为变量名来检索div。所以对于像

这样的div
<div id="example">some text</div>
您可以在Internet Explorer 8和Chrome中执行

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

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

那么,这是否意味着 DOM树中的每个元素都转换为全局命名空间中的变量?它是否也意味着可以使用它来替代这些浏览器中的getElementById方法?

5 个答案:

答案 0 :(得分:370)

应该发生的是'命名元素'被添加为document对象的明显属性。这是一个非常糟糕的主意,因为它允许元素名称与document的真实属性冲突。

通过添加命名元素作为window对象的属性,IE使情况变得更糟。这是非常糟糕的,因为现在您必须避免在您(或项目中的任何其他库代码)可能想要使用的documentwindow对象的任何成员之后命名您的元素。 / p>

这也意味着这些元素作为类似全局变量可见。幸运的是,在这种情况下,代码中的任何真正的全局varfunction声明都会影响它们,因此您不必担心这里的命名,但是如果您尝试对全局进行分配变量与冲突的名称,你忘了声明它var,你会在IE中得到一个错误,因为它试图将值赋给元素本身。

通常认为忽略var以及依赖于window上可见的命名元素或全局变量是不好的做法。坚持document.getElementById,这是更广泛支持和更少模糊。如果您不喜欢打字,可以使用较短的名称编写一个简单的包装函数。无论哪种方式,使用id-to-element查找缓存都没有意义,因为浏览器通常会优化getElementById调用以使用快速查找;当元素发生变化id或从文档中添加/删除时,你得到的只是问题。

Opera复制IE,然后WebKit加入,现在既有先前未标准化的将命名元素放在document属性上的做法,以及以前只在IE上放置它们window的做法是HTML5的being standardised,其方法是记录并标准化浏览器作者对我们造成的每一种可怕做法,使其永远成为网络的一部分。所以Firefox 4也会支持这个。

什么是'命名元素'?任何带有id的内容,以及name用于“识别”目的的任何内容:即表单,图片,锚点和其他一些内容,而不是{{1}的其他无关实例}属性,如表单输入字段中的控件名称,name中的参数名称或<param>中的元数据类型。 “识别”<meta>应该避免使用name

答案 1 :(得分:46)

如前面的答案所述,此行为称为named access on the window object。某些元素的name属性值和所有元素的id属性值可用作全局window对象的属性。这些被称为命名元素。由于window是浏览器中的全局对象,因此每个命名元素都可以作为全局变量访问。

这最初是由Internet Explorer添加的,最终由所有其他浏览器实现,只是为了兼容依赖于此行为的网站。有趣的是,Gecko(Firefox的渲染引擎)选择仅在quirks mode中实现此功能,而其他渲染引擎则以标准模式启用它。

但是,从Firefox 14开始,window对象也是标准模式下的Firefox now supports named access。他们为什么改变这个?事实证明,仍有很多网站在标准模式下依赖此功能。微软甚至released a marketing demo确实阻止了这个演示在Firefox中运行。

Webkit最近considered the opposite,将window对象的命名访问权限仅限于怪癖模式。他们通过与Gecko相同的推理决定反对它。

所以...疯狂,因为在标准模式的所有主流浏览器的最新版本中,这种行为现在在技术上是安全的。但是,虽然命名访问看起来有点方便,但不应该使用

为什么呢?很多推理可以在本文中总结为什么global variables are bad。简而言之,拥有一堆额外的全局变量会导致更多错误。假设您不小心输入了var的名称,并且碰巧输入了一个DOM节点的id,SURPRISE!

此外,尽管标准化,但浏览器的命名访问实现仍然存在很多差异。

  • IE错误地使表单元素(输入,选择等)可以访问name属性的值。
  • Gecko和Webkit错误地不会通过<a>属性访问name个标记。
  • Gecko错误地处理多个具有相同名称的命名元素(它返回对单个节点而不是引用数组的引用)。

如果您尝试在边缘情况下使用命名访问,我相信还有更多。

如其他答案中所述,使用document.getElementById通过其id获取对DOM节点的引用。如果您需要通过name属性使用document.querySelectorAll来获取对节点的引用。

请不要在您的站点中使用命名访问来传播此问题。许多网络开发人员浪费时间试图追踪这种神奇的行为。我们确实需要采取行动并让渲染引擎在标准模式下关闭命名访问。从短期来看,它会破坏一些做坏事的网站,但从长远来看,这将有助于推动网络向前发展。

如果您有兴趣,我会在我的博客上更详细地讨论这个问题 - https://www.tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/

答案 2 :(得分:20)

在这些情况下你应该坚持getElementById(),例如:

document.getElementById('example').innerHTML

IE喜欢在全局命名空间中混合使用name ID属性的元素,因此最好明确说明您要获取的内容。

答案 3 :(得分:3)

是的,他们确实这样做了。

在Chrome 55,Firefox 50,IE 11,IE Edge 14和Safari 10中测试过 使用以下示例:

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

答案 4 :(得分:2)

听起来应该是这样的问题:“具有提供的ID的HTML标记是否可以成为可全局访问的DOM元素?”

答案是肯定的!

这就是它的工作方式,这就是W3C最初引入ID的原因。: 已解析的脚本环境中的HTML Tag的ID成为其相应的DOM元素句柄。

但是,Netscape Mozilla拒绝遵循(入侵他们)W3C,并顽固地使用不推荐使用的Name属性来制造破坏,因此破坏了W3C引入唯一ID带来的脚本功能和编码便利性。

在Netscape Navigator 4.7惨败之后,他们的开发人员都去了并渗透到W3C中,而他们的同事却以错误的做法和错误的示例取代了Web。强制使用和重用已经过时的Name属性[!,这并不是唯一的]与ID属性相提并论,以便利用ID句柄访问特定DOM元素的脚本会被破坏!

打破他们的做法,就像编写和发布大量的编码课程和示例一样(它们的浏览器无法识别),例如document.all.ElementID.property而不是ElementID.property,至少可以使其效率低下,并提供如果浏览器没有使用相同的令牌(现在[1996-97],已弃用)和为其提供相同令牌值的标准ID属性,而不仅仅是在HTML域将其破坏,则将增加浏览器的开销。

他们很容易地说服了当时的绝大多数无知的代码编写爱好者,他们的名字和ID实际上是相同的,只是ID属性更短,因此节省了字节,比古老的编码器更方便编码名称属性。这当然是骗人的。或者-在其取代已发表的HTML文章的过程中,说服您需要为标签提供名称和ID的文章,以便脚本引擎可以访问它们。

“马赛克杀手”(代号“ Mozilla”)非常生气,他们以为“如果我们失败了,互联网也应该如此”。

另一方面,崛起的Microsoft太天真了,以为他们应该保留不推荐使用的内容并标记为删除Name属性,并将其视为唯一标识的ID,这样他们才不会破坏Netscape受训者编码的旧页面的脚本功能。他们是致命的错误...

返回ID冲突元素的数组集合也不是解决此人为问题的方法。实际上,它打败了整个目标。

这是W3C变得丑陋并为我们提供了诸如document.getElementById之类的惯用语以及伴随的洛可可式令人讨厌的语法的唯一原因...  (...)