413

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

对于DOM 树中HTMLElement带有 的给定,可以使用其 ID 作为变量名或作为 的属性来检索。所以对于一个喜欢id<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这些浏览器中的方法?

4

5 回答 5

437

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

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

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

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

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

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

于 2010-08-08T13:03:05.627 回答
65

正如前面的回答中提到的,这种行为被称为对窗口对象的命名访问name某些元素的属性值和所有id元素的属性值都可用作全局window对象的属性。这些被称为命名元素。由于window是浏览器中的全局对象,因此每个命名元素都可以作为全局变量访问。

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

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

Webkit 最近考虑了相反的情况,将对象的命名访问降级为window仅怪癖模式。他们以与壁虎相同的理由决定反对它。

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

为什么?这篇文章中可以总结出很多关于为什么全局变量不好的原因。简单地说,拥有一堆额外的全局变量会导致更多的错误。假设您不小心输入了 a 的名称,var并且碰巧输入id了 DOM 节点的 an,SURPRISE!

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

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

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

正如其他答案中提到的那样,document.getElementById用于通过id. 如果您需要通过name属性获取对节点的引用,请使用document.querySelectorAll.

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

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

于 2012-07-27T15:54:22.450 回答
21

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

document.getElementById('example').innerHTML

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

于 2010-08-08T12:29:09.930 回答
9

这个问题听起来应该是:“带有提供 ID 的 HTML 标签会成为全球可访问的 DOM 元素吗?”

答案是肯定的!

这就是它的工作方式,这也是 W3C 一开始就引入 ID 的原因。: 解析脚本环境中 HTML 标记的 ID 成为其对应的 DOM 元素句柄。

然而,Netscape Mozilla 拒绝遵守(对他们侵入)W3C 并顽固地使用已弃用的 Name 属性来造成严重破坏,因此破坏了 W3C 引入唯一 ID 带来的脚本功能和编码便利性。

在 Netscape Navigator 4.7 惨败之后,他们的开发人员都潜入了 W3C,而他们的同事则以错误的做法和滥用示例来取代 Web。强制使用和重用已弃用的 Name 属性 [!which 不意味着是唯一的] 与 ID 属性相提并论,这样使用 ID 句柄访问特定 DOM 元素的脚本就会崩溃!

并打破他们所做的,因为他们还将编写和发布广泛的编码课程和示例[他们的浏览器无论如何都无法识别], document.all.ElementID.property而不是ElementID.property至少使其效率低下并给浏览器更多的开销,以防它没有简单地打破它HTML 域通过使用相同的标记(现在 [1996-97],已弃用)名称和标准 ID 属性为其提供相同的标记值。

他们轻松地说服了——当时——压倒性的无知代码编写爱好者大军,名称和 ID 实际上是相同的,除了 ID 属性更短,因此比古老的 Name 属性更节省字节并且对编码器更方便。这当然是谎言。或者 - 在他们取代已发布的 HTML 文章中,令人信服的文章表明您需要为您的标签提供名称和 ID,以便脚本引擎可以访问它们。

Mosaic Killers [代号“Mozilla”] 非常生气,他们认为“如果我们倒闭,互联网也应该如此”。

另一方面,崛起的微软非常天真,他们认为应该保留已弃用并标记为删除的 Name 属性,并将其视为唯一标识符的 ID,这样他们就不会破坏脚本功能由 Netscape 学员编码的旧页面。他们大错特错...

并且返回 ID 冲突元素的数组集合也不能解决这个故意的人为问题。实际上它破坏了整个目的。

这就是 W3C 变得丑陋并给我们带来诸如document.getElementById洛可可式的讨厌语法之类的白痴的唯一原因......(......)

于 2019-02-06T03:18:08.987 回答
4

是的,他们有。


通过以下示例在 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,输出

于 2016-12-23T14:33:09.777 回答