具有 ID 的 DOM 树元素是否成为全局属性?

在为一个简单的包装器做一个想法时,我偶然发现了Internet Explorer和Chrome的以下内容:HTMLElement

对于在 DOM 树中具有 的给定,可以使用其 ID 作为变量名或 作为 的属性来检索 。所以为了一个喜欢HTMLElementid<div>window<div>

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

Internet Explorer 8和Chrome中,您可以执行以下操作:

alert(example.innerHTML); // Alerts "some text".

alert(window["example"].innerHTML); // Alerts "some text".

那么,这是否意味着 DOM 树中的每个元素都转换为全局对象上的属性?这是否也意味着可以使用它来替代这些浏览器中的方法?getElementById


答案 1

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

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

这也意味着这些元素作为类似全局的变量可见。幸运的是,在这种情况下,代码中的任何真正的全局或声明都会隐藏它们,因此您不必担心在此处命名,但是如果您尝试对具有冲突名称的全局变量进行赋值并且忘记声明它,则在IE中将值分配给元素本身时,您将在IE中收到错误。varfunctionvar

省略 ,以及依赖于在全局变量上可见或作为全局变量可见的命名元素,通常被认为是不好的做法。坚持使用 ,它得到更广泛的支持,并且不那么模棱两可。如果您不喜欢键入,则可以使用较短的名称编写一个简单的包装器函数。无论哪种方式,使用id到元素查找缓存都没有意义,因为浏览器通常会优化调用以使用快速查找;您得到的只是元素更改或从文档中添加/删除时的问题。varwindowdocument.getElementByIdgetElementByIdid

Opera复制了IE,然后WebKit加入了进来,现在以前不标准化的将命名元素放在属性上的做法,以及以前只有IE的放置它们的做法正在被HTML5标准化,HTML5的方法是记录和标准化浏览器作者强加给我们的每一个可怕的做法,使它们永远成为网络的一部分。所以Firefox 4也会支持这一点。documentwindow

什么是“命名元素”?任何带有 、和 具有 的任何内容都用于“标识”目的:即窗体、图像、锚点和其他一些实例,但不是属性的其他不相关实例,如表单输入字段中的控件名称、中的参数名称或 元数据类型。“识别”是应该避免的,而应改为 。idnamename<param><meta>nameid


答案 2

如前面的答案中所述,此行为称为对窗口对象的命名访问。某些元素的属性值和所有元素的属性值都可用作全局对象的属性。这些元素称为命名元素。由于 是 浏览器中的全局对象,因此每个命名元素都可以作为全局变量进行访问。nameidwindowwindow

这最初是由 Internet Explorer 添加的,最终由所有其他浏览器实现,只是为了与依赖于此行为的站点兼容。有趣的是,Gecko(Firefox的渲染引擎)选择仅在quirks模式下实现它,而其他渲染引擎在标准模式下保持打开状态。

但是,从Firefox 14开始,Firefox现在也支持在标准模式下对对象进行命名访问。他们为什么要改变这一点?事实证明,仍然有很多网站在标准模式下依赖于此功能。微软甚至发布了一个营销演示,阻止了该演示在Firefox中工作。window

Webkit最近考虑了相反的情况,将对象的命名访问权限降级为仅 quirks 模式。他们通过与壁虎相同的推理决定反对它。window

所以。。。疯狂的是,似乎这种行为现在在技术上是安全的,可以在标准模式下使用所有主要浏览器的最新版本。但是,虽然命名访问似乎有点方便,但它不应该被使用

为什么?在本文中可以总结出很多关于为什么全局变量不好的推理。简单地说,有一堆额外的全局变量会导致更多的错误。假设您不小心键入了 a 的名称,并碰巧键入了 DOM 节点的 a,SURPRISE!varid

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

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

我敢肯定,如果您尝试在边缘情况上使用命名访问,还会有更多。

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

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

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