带有 ID 的 DOM 树元素会成为全局属性吗?

Do DOM tree elements with IDs become global properties?

提问人:KooiInc 提问时间:8/8/2010 最后编辑:Sebastian SimonKooiInc 更新时间:5/16/2023 访问量:57052

问:

在为一个简单的包装器构想时,我偶然发现了以下适用于 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

JavaScript HTML DOM 全局变量 getElementById

评论

10赞 Bergi 9/1/2014
参见 为什么我们不只使用元素 ID 作为 JavaScript 中的标识符? 为什么不应该使用它,以及 是否有规范将元素的 ID 设为全局变量? 关于它是如何规范的。
3赞 ESR 8/28/2017
@Bergi,声明不这样做的评论现在已经过时,甚至无效。因此,我找不到不使用此功能的具体原因。
2赞 Bergi 8/29/2017
@EdmundReed 您可能想再次阅读链接问题的答案 - 这仍然是一个坏主意:“隐式声明的全局变量”没有工具支持,并且“导致代码脆弱”。不要称它为“功能”,下面的答案解释了它只是一个错误,出于兼容性原因成为标准的一部分。
2赞 ESR 8/29/2017
@Bergi公平地说,你是对的。不过,我仍然认为这是一个非常简洁的功能,并且只是因为人们没有意识到它而被认为是有问题的。这就是我设想使用它的方式:codepen.io/esr360/pen/WEavGE?editors=1000#0
1赞 Bergi 8/29/2017
@EdmundReed当然,如果你没有正确地将内容和逻辑分开,问题就不那么大了。此外,我建议永远不要使用内联事件处理程序或在 DOM 元素上安装自定义方法,将它们滥用为命名空间(请注意,它不是“范围”)。

答:

23赞 Nick Craver 8/8/2010 #1

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

document.getElementById('example').innerHTML

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

475赞 bobince 8/8/2010 #2

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

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

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

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

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

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

评论

25赞 Farzher 5/8/2014
为什么!?我们能做些什么来阻止这种疯狂吗?我的函数通过对元素的引用重新定义,我花了一个小时来调试。:(
2赞 Dimitry K 1/6/2016
我们能否为这篇文章提供一些 TLDR,并可能更新到 2016 年?会不会像“将'命名元素'暴露到全局/窗口/文档范围对于浏览器来说是一个坏主意。并且应该避免依赖此功能。[TODO:命名元素以避免名称冲突的建议是什么? 例如,我可以命名我的 DIV#location 吗?
1赞 Jason Sperske 5/20/2017
我很好奇性能差异,我认为这 jsperf.com/global-named-element/1 表明它是戏剧性的,除非我错过了什么。
2赞 a cat 11/6/2021
有趣的事实:这些全局属性也延续到浏览器扩展代码的“”隔离“”执行上下文中, 并且由于这个惊人的功能,LastPass扩展得到了完全拥有
2赞 jjr2000 2/17/2022
我同意使用 document.getElementById 是最佳实践,但是我会测试性能,看看以不同的方式这样做是否有好处。对于现代浏览器来说,它似乎确实是性能最高的。这是我运行的简单测试来评估这一点: jsben.ch/AZD81
77赞 TJ VanToll 7/27/2012 #3

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

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

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

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

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

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

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

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

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

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

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

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

评论

5赞 Jeremy Foster 9/13/2014
只是对“不应使用它”这一前提的明显警告的注释。也就是说,“除非你碰巧是一个代码牛仔,否则不应该使用它。代码牛仔只是去做。
6赞 Patrick Roberts 6/28/2015
@jeremyfoster除非“代码牛仔”是指使用和传播对开发人员不友好的糟糕实现的人,否则我强烈不同意。
3赞 Jeremy Foster 6/28/2015
一个好牛仔的一个标志是很多人不同意。但现在我就像哲学牛仔或类似的东西。
1赞 Travis J 10/29/2015
更多的人应该使用 和 当访问 DOM 时。 +1 以获得使用它的好建议。通过选择器访问元素绝对是一个更有效的过程。document.querySelectorAlldocument.querySelector
0赞 vanowm 4/3/2022
@TravisJ 仅当 id 不可用时,否则它比getElementsByClassNamegetElementById
5赞 qff 12/23/2016 #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,output

评论

1赞 ncmathsadist 1/25/2019
同样在歌剧中。但是,我认为本页表达的对这一机制的反对意见是很好的。
0赞 Adam Carr 11/26/2021
它如何处理带连字符的变量名称,例如 id='first-name'。我的测试似乎表明它不起作用。
0赞 vanowm 4/3/2022
@AdamCarr您可以通过 Object 访问它们:windowwindow["first-name"]
15赞 Bekim Bacaj 2/6/2019 #5

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

答案是肯定的!

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

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

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

他们这样做了,因为他们也会编写和发布大量的编码课程和示例[他们的浏览器无论如何都不会识别],例如至少使其效率低下并给浏览器更多的开销,以防它没有简单地在HTML域使用相同的令牌来破坏它(现在[1996-97], deprecated) 名称和为其提供相同令牌值的标准 ID 属性。document.all.ElementID.propertyElementID.property

他们轻而易举地说服了当时绝大多数无知的代码编写业余爱好者,他们相信 Names 和 ID 实际上是相同的,只是 ID 属性更短,因此比古老的 Name 属性更节省字节,对编码人员来说更方便。这当然是谎言。或者 - 在他们取代已发布的 HTML 文章中,说服您需要为标记提供 Name 和 ID,以便脚本引擎可以访问它们。

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

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

返回 ID 冲突元素的数组集合也不是解决这个故意的人为问题的方法。实际上,它违背了整个目的。

这就是 W3C 变得丑陋并给我们带来白痴的唯一原因,例如伴随的洛可可式该死的烦人语法...... (...)document.getElementById

评论

2赞 drooh 11/19/2021
所以在 2021 年,我认为将其用于小型网站和表单是安全的吗?真正的潜在客户无法提交不使用 document.getElementById() 的表单的百分比是多少?对我来说,在 2021 年,这似乎可以节省大量时间,并且对于所有密集目的来说,使用起来安全吗?只要开发人员确切地知道在小级别使用代码中发生了什么。
1赞 agiopnl 3/8/2022
这很有趣,我也不喜欢洛可可代码。我不太了解整个历史。在 getElementById 之前如何访问元素?
2赞 Bekim Bacaj 4/15/2022
@agiopnl 他们通过分配的 ID 访问!这是 WC3 引入 ID 而不是名称的唯一原因。ID 应该是唯一的。而名称不是。就像在现实生活中一样。
2赞 jordanfb 5/16/2023 #6

document.all 需要取消弃用并修复一点

带有 or 的元素可用,这对我来说是令人兴奋的消息。idnamedocument.all

然而,从我收集到的内容来看,它被老前辈弃用和唾弃了,但似乎没有人能够提供任何令人信服的理由来永远不使用它,或者至少能够提供足够有力的论据来说明为什么它应该被弃用。all

我正在将它用于一个小的内部工具页面,它完全有效......目前。它确实有问题,但便利性和清洁度不容忽视:

鉴于

<h1 id="welcomeMessage" hidden>Welcome!</h1>

你可以做

document.all.welcomeMessage.hidden = false

与。

document.getElementById('welcomeMessage').hidden = false
document.querySelector('#welcomeMessage').hidden = false

太好了!但是存在一些问题:

  • 如果碰巧有多个元素具有相同的(可能,但可以而且应该避免)或(非常可能,但由于复选框组而无法避免),则返回一个,而不是单个idnameHTMLCollectionElement
  • 如果您出于某种原因尝试获取具有与继承属性(如 或)匹配的 id 或名称的元素,那么您将获得这些对象而不是__proto__toStringundefined
  • 它不如 快,因此如果您要查询大量元素,则存在性能问题document.getElementById

一个不错的选择

我决定这样做,我真的不确定我参加派对有多晚,但我很喜欢这个:

const elements = new Proxy({}, {
  get(target, prop) {
    return document.getElementById(prop) || document.getElementsByName(prop)[0];
  }
});

// Clean and direct access to elements just like document.all
elements.welcomeMessage.hidden = false