具有ID的DOM树元素是否会成为全局变量?


364

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

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

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

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

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

要么

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

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



1
@Bergi,声明不执行此操作的评论现在已过时,甚至无效。因此,我找不到不使用此功能的具体原因。
ESR

@EdmundReed您可能想再次阅读链接问题的答案-这仍然是一个坏主意:“ 隐式声明的全局变量 ”对工具的支持不佳甚至“ 导致代码易碎 ”。不要称其为“功能”,下面的答案解释了由于兼容性的原因,它只是成为标准一部分的错误。
Bergi

1
@Bergi很公平,你是对的。我仍然认为这是一个非常整洁的功能,只是被人们认为是有问题的,因为人们对此并不了解。这就是我使用它的方式:codepen.io/esr360/pen/WEavGE?editors=1000#0
ESR

@EdmundReed当然,如果您没有正确区分内容和逻辑,那么问题就少了。另外,我建议不要使用内联事件处理程序或在将其滥用为命名空间的DOM元素上安装自定义方法(注意,这不是“作用域”)。
Bergi

Answers:


395

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

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

这也意味着这些元素作为全局变量可见。幸运的是,在这种情况下,代码中的任何实际全局变量varfunction声明都将它们遮蔽起来,因此您不必担心在此处命名,但是如果您尝试使用名称冲突的方式对全局变量进行赋值而忘记声明它var,您会在IE中遇到错误,因为它试图将值分配给元素本身。

通常,忽略var以及依赖命名元素在window全局变量上或全局变量上可见是不正确的做法。坚持document.getElementById,这是得到更广泛支持和较少含糊的内容。如果您不喜欢键入,则可以使用短名称编写平凡的包装函数。无论哪种方式,使用id到元素的查找缓存都是没有意义的,因为浏览器通常会优化getElementById调用以使用快速查找。当元素更改id或从文档中添加/删除元素时,您得到的只是问题。

歌剧复制IE,然后WebKit的加入进来,现在都将命名元素上的以前不标准,实践document特性,并把他们的先前IE-只有实践window 标准化的HTML5,其方法是将文件和规范每浏览器作者对我们施加了可怕的习俗,使他们永远成为网络的一部分。因此,Firefox 4也将支持此功能。

什么是“命名元素”?带有id,和带有name用于“标识”目的的任何东西:即表单,图像,锚点和其他一些name属性,但没有其他与属性无关的实例,例如表单输入字段中的控件名称,<param>或中的元数据类型<meta>name应该避免使用“标识” 来代替id


5
答案很明确,谢谢。忽略document.getElementById并不是我的主意(事实上,我现在尽可能使用xpath来查找element / elements属性)。我偶然发现了命名项目的这种(不好的)做法,并对它的来源感到好奇。您已经足够透彻地回答了;现在我们知道了为什么它也可以在Chrome(webkit)中找到。
KooiInc 2010年

18
name应避免使用”的一个例外是<input>,其中name属性在形成表单提交的键-值对键中起关键作用。
Yahel 2011年

7
仅供参考,Firefox仅在进入怪癖模式时才这样做。

4
@yahelc:这正是我所做出的区分。“ name在表单输入字段中没有类似控件名称的其他用途……”
bobince 2011年

13
为什么!?我们有什么办法可以制止这种疯狂?通过引用元素重新定义了我的函数,并且调试花费了一个小时。:(
Farzher 2014年

52

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

它最初是由Internet Explorer添加的,最终由所有其他浏览器实现,只是为了与依赖此行为的网站兼容。有趣的是,Gecko(Firefox的渲染引擎)选择仅以怪癖模式实现此功能,而其他渲染引擎则将其保留为标准模式。

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

Webkit最近考虑了相反的做法,window仅将对象上的命名访问权限授予怪癖模式。他们以与壁虎相同的理由决定反对。

如此……疯狂,因为从技术上讲,现在在标准模式下的所有主流浏览器的最新版本中使用此行为,都是安全的。但是,尽管命名访问似乎有点方便,但不应使用它

为什么?关于全局变量为何坏的原因,本文可以总结很多原因。简而言之,拥有大量额外的全局变量会导致更多错误。假设您不小心输入了a的名称,var而偶然输入id了DOM节点SURPRISE的an !

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

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

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

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

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

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


3
只是注意“不应该使用它”这一前提的明显警告。也就是说,“除非您碰巧是一名代码牛仔,否则不应使用它。” 代码牛仔就去做。
杰里米·福斯特

5
@jeremyfoster除非“代码牛仔”表示使用和传播对开发人员不利的不良实现的人,否则我强烈不同意。
Patrick Roberts 2015年

2
一个好牛仔的标志是许多人不同意。但是现在我就像一个哲学牛仔或类似的人。
杰里米·福斯特

越来越多的人应该使用document.querySelectorAlldocument.querySelector访问DOM时。+1是使用它的好建议。通过选择器访问元素绝对是一个更有效的过程。
特拉维斯J

20

getElementById()在这种情况下,您应该坚持使用,例如:

document.getElementById('example').innerHTML

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


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,输出


1
也在Opera中。但是,我认为在此页面上表达的对此机制的异议已被很好地接受。
ncmathsadist,

1

这个问题听起来应该是:“具有提供的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使其效率低下,并为浏览器提供更多的开销,以防万一它不会仅仅破坏它。 HTML域,方法是使用相同的标记(现在[1996-97],已弃用),名称和标准ID属性为其提供相同的标记值。

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

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

另一方面,崛起的Microsoft太天真了,他们以为应该保留不推荐使用的标记并标记为删除Name属性,并将其视为唯一标识符的ID来对待,以便他们不会破坏该脚本的脚本功能Netscape学员编码的旧页面。他们是致命的错误...

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

这是W3C变得丑陋并给了我们白痴和诸如此类document.getElementById的洛可可式令人讨厌的语法的唯一原因...(...)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.