在为一个简单的HTMLElement包装器工作的想法中,我偶然发现了下面的Internet Explorer和Chrome:
对于在DOM树中带有id的给定HTMLElement,可以使用它的id作为变量名或窗口属性来检索<div>。对于<div>
<div id="example">some text</div>
在ie8和Chrome浏览器中,你可以:
alert(example.innerHTML); // Alerts "some text".
or
alert(window["example"].innerHTML); // Alerts "some text".
那么,这是否意味着DOM树中的每个元素都转换为全局对象的属性?它是否也意味着可以在这些浏览器中使用它作为getElementById方法的替代品?
是的,他们有。
在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
正如前面的回答中提到的,这种行为被称为窗口对象的命名访问。某些元素的name属性值和所有元素的id属性值作为全局窗口对象的属性可用。这些元素称为命名元素。由于window是浏览器中的全局对象,每个命名的元素都可以作为全局变量访问。
这最初是由Internet Explorer添加的,最终被所有其他浏览器实现,只是为了与依赖此行为的站点兼容。有趣的是,Gecko (Firefox的渲染引擎)选择仅以怪异模式实现此功能,而其他渲染引擎则将其保持在标准模式。
但是,从Firefox 14开始,Firefox现在也支持以标准模式对窗口对象进行命名访问。他们为什么要改变这个?事实证明,仍然有很多网站在标准模式下依赖这个功能。微软甚至发布了一个营销演示,阻止演示在Firefox上运行。
Webkit最近考虑了相反的情况,将窗口对象上的命名访问降级为怪异模式。他们反对的理由和壁虎一样。
看起来很疯狂,在所有主流浏览器的最新版本的标准模式下,这种行为现在在技术上是安全的。但是,尽管命名访问看起来有点方便,但不应该使用它。
为什么?关于为什么全局变量不好,本文可以总结出很多原因。简单地说,有一堆额外的全局变量会导致更多的错误。假设您不小心输入了一个变量的名称,并碰巧输入了一个DOM节点的id, SURPRISE!
此外,尽管已经标准化,但在浏览器的命名访问实现中仍然存在相当多的差异。
IE错误地使name属性的值可以被表单元素(input, select等)访问。
Gecko和Webkit不正确地使<a>标记不能通过其name属性访问。
Gecko不正确地处理具有相同名称的多个命名元素(它返回对单个节点的引用,而不是引用数组)。
如果您尝试在边缘情况下使用命名访问,我相信会有更多。
正如在其他答案中提到的,使用文档。getElementById通过id获取对DOM节点的引用。如果你需要通过一个节点的name属性获取它的引用,请使用document.querySelectorAll。
请,请不要在您的站点中使用命名访问来传播这个问题。许多web开发人员浪费时间试图追踪这种神奇的行为。我们真的需要采取行动,让呈现引擎在标准模式下关闭命名访问。在短期内,它将打破一些做坏事的网站,但从长远来看,它将有助于推动网络前进。
如果你感兴趣,我会在我的博客https://www.tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/上详细介绍。
应该发生的是“命名元素”被添加为文档对象的明显属性。这是一个非常糟糕的主意,因为它允许元素名称与文档的实际属性冲突。
IE还将命名元素作为窗口对象的属性,使情况变得更糟。这是加倍糟糕的,因为现在您必须避免以您(或项目中的任何其他库代码)可能想要使用的文档或窗口对象的任何成员命名元素。
这也意味着这些元素作为类全局变量可见。幸运的是,在这种情况下,代码中任何真正的全局变量或函数声明都会隐藏它们,所以你不需要太担心这里的命名,但如果你试图对一个名称冲突的全局变量进行赋值,而你忘记将其声明为var,你将在IE中得到一个错误,因为它试图将值赋值给元素本身。
It's generally considered bad practice to omit var, as well as to rely on named elements being visible on window or as globals. Stick to document.getElementById, which is more widely-supported and less ambiguous. You can write a trivial wrapper function with a shorter name if you don't like the typing. Either way, there's no point in using an id-to-element lookup cache, because browsers typically optimise the getElementById call to use a quick lookup anyway; all you get is problems when elements change id or are added/removed from the document.
Opera复制了IE,然后WebKit也加入了进来,现在无论是之前未被标准化的将命名元素放在文档属性上的做法,还是之前只有IE才有的将它们放在窗口上的做法,都被HTML5标准化了,HTML5的方法是记录和标准化浏览器作者强加给我们的每一个可怕的做法,使它们永远成为网络的一部分。Firefox 4也支持这个。
什么是“命名元素”?任何带有id的东西,以及任何用于“识别”目的的带有名称的东西:即表单、图像、锚和其他一些东西,但不包括名称属性的其他不相关实例,如表单输入字段中的控件名称,<param>中的参数名称或<meta>中的元数据类型。为了使用id,应该避免使用“标识”名称。
这个问题听起来应该是::“带有提供id的HTML标签会成为全局可访问的DOM元素吗?”
答案是肯定的!
这就是它的工作方式,这也是W3C最初引入id的原因:
在解析过的脚本环境中,HTML标签的ID变成了它对应的DOM元素句柄。
然而,Netscape Mozilla拒绝遵守W3C(对他们来说是侵入性的),固执地继续使用已弃用的Name属性来制造混乱,因此破坏了由W3C引入唯一标识符带来的脚本功能和编码便利。
在Netscape Navigator 4.7的惨败之后,他们的开发人员都去了W3C,而他们的同事却用错误的实践和错误的例子取代了Web。强制使用和重用已弃用的Name属性[!这并不意味着是唯一的],与ID属性相同,因此使用ID句柄访问特定DOM元素的脚本将简单地崩溃!
他们还编写并发布了大量的编码课程和示例(他们的浏览器无论如何都不会识别),比如document.all.ElementID.property而不是ElementID。属性,至少使它效率低,并给浏览器更多的开销,以防它没有简单地在HTML域中使用相同的令牌(现在[1996-97],已弃用)Name和标准ID属性提供相同的令牌值而破坏它。
他们很容易地说服了——当时——绝大多数无知的代码编写业余爱好者,他们认为Name和ID实际上是一样的,只是ID属性比古代的Name属性更短,因此节省字节,对编码员来说更方便。这当然是一个谎言。或者,在他们已经发布的HTML文章中,让人信服的文章中,您需要为标记提供Name和ID,以便脚本引擎能够访问它们。
马赛克杀手(代号“Mozilla”)非常生气,他们认为“如果我们垮了,互联网也应该垮”。
另一方面,正在崛起的微软是如此天真,他们认为他们应该保留已弃用并标记为删除的Name属性,并将其视为唯一标识符的ID,这样他们就不会破坏Netscape学员编写的旧页面的脚本功能。他们大错特错了……
返回ID冲突元素的数组集合也不是这个人为问题的解决方案。事实上,这违背了整个目的。
这就是W3C变得丑陋的唯一原因,给了我们像文档这样的白痴。getElementById和伴随的洛可可的该死的恼人的语法…
(...)