为什么不在JavaScript中直接使用元素id作为标识符呢?

Why don't we just use element IDs as identifiers in JavaScript?

本文关键字:id 标识符 元素 JavaScript 为什么不      更新时间:2023-09-26

我使用过的所有浏览器都允许使用id="myDiv"访问元素,只需输入:

myDiv

请看这里:http://jsfiddle.net/L91q54Lt/

无论如何,这个方法的文档似乎很差,事实上,我遇到的资料甚至没有提到它,而是假设人们会使用

document.getElementById("myDiv")

或者

document.querySelector("#myDiv")

来访问一个DOM元素,即使它的ID是事先已知的(即在运行时不计算)。我可以告诉你,如果有人无意中试图在更广泛的范围内重新定义myDiv(虽然不是一个聪明的想法……),用一些不同的值覆盖它,并且在没有注意到冲突的情况下继续进行,后一种方法具有保持代码安全的优势。

但是除此之外呢?除了代码设计之外,使用上面的简短形式还有什么问题吗?或者我在这里还遗漏了什么?

无论如何,这个方法的文档似乎很差,事实上,我遇到的来源甚至没有提到它[…]

除了依赖隐式声明的全局变量之外,缺乏文档是不使用它的一个重要原因。

id值提升为全局变量是不符合标准的(HTML5规范中没有提到ID属性),因此,你不应该假设未来的浏览器会实现它。

编辑:事实证明这种行为是符合标准的-在HTML5中,window应该支持对"命名元素"的属性访问:
对于上述算法而言,带有name的命名对象是:
  • 活动文档的子浏览上下文,其名称为name
  • a、applet、area、embed、form、framset、img或object元素name内容属性,其值为name
  • HTML元素的id content属性值为name

来源:HTML 5规范,"命名访问窗口对象",强调mine

基于此,遵循标准不是避免此模式的理由。然而,规范本身不建议使用它:

作为一般规则,依赖于此将导致脆弱的代码。哪一个映射到此API的id可能随着时间的推移而变化,就像新特性一样添加到Web平台,例如。而不是这个,使用document.getElementById()document.querySelector() .

问得好。正如爱因斯坦可能没有说过的那样,事情应该尽可能简单,而不是更简单。

后一种方法的优点是,如果有人无意中试图在更广泛的范围内重新定义myDiv(虽然不是一个好主意……),用一些不同的值覆盖它,然后继续使用,而不注意冲突

这就是为什么这是一个坏主意的主要原因,而且这已经足够了。依赖全局变量是不安全的。它们可以在任何时候被最终在页面上运行的任何脚本覆盖。

除此之外,仅仅输入myDiv并不是document.getElementById()的"缩写形式"。它是对全局变量的引用。如果元素不存在,document.getElementById()将愉快地返回null,而试图访问不存在的全局变量将抛出引用错误,因此您需要将对全局变量的引用包装在try/catch块中以确保安全。

这是jQuery如此受欢迎的一个原因:如果您执行$("#myDiv").remove(),并且没有id为myDiv的元素,则不会抛出错误—代码将静默地不做任何事情,这通常正是您在进行DOM操作时想要的。

原因如下:

你不希望你的代码和你的标记耦合。

通过使用特定的调用来访问div,您不必担心全局空间被破坏。添加一个在全局空间声明myDiv的库,你将陷入一个难以修复的痛苦世界。

你可以通过ID访问不属于DOM的元素

它们可以是一个片段,一个框架,或者一个已经分离并且还没有重新连接到DOM的元素。

编辑:ID

访问非附加元素的示例

var frag = document.createDocumentFragment();
var span = document.createElement("span");
span.id = "span-test";
frag.appendChild(span);
var span2 = frag.getElementById("span-test");
alert(span === span2);

在我的例子中,我的页面中有一个iframe。我对id属性和name属性感到困惑,这两个属性都影响了一个名为inner_iframe的变量,可以从window访问!

  • 如果我只使用id属性,像id="inner_iframe"一样,window.inner_iframeHTMLIFrameElement。属性包括inner_iframe.contentDocumentinner_iframe.contentWindow,如下所述*

    • 在这种情况下,我假设变量出现在window上,因为在可接受的答案中@joew引用的原因:
      具有id content属性且值为name的 HTML元素
  • 如果我只使用名称属性,像name="inner_iframe"那么window.inner_iframe是一个"框架",又名"窗口对象"。具有contentDocumentcontentWindow属性。

    • 我假设inner_iframe变量出现在window上,因为@joew在可接受的答案中引用了原因
      name为name 的活动文档的子浏览上下文
  • 如果我使用 nameid属性,我给这两个属性相同的值name="inner_iframe" id="inner-iframe";name属性胜过/击败了id属性;我留下了"窗口对象",而不是 HTMLIFrameElement !

所以我的观点是要小心歧义;nameid属性之间的冲突,在同一个对象与两个不同的api:只是一个特定的情况下,隐式行为和附加到一个窗口变量可能会混淆你。

*(仅当<script>加载在HTML中的<iframe>之后/之下,或者<script>在尝试通过id属性访问window.onload之前等待CC_43)

**这个"框架"和DOM元素的区别在Mozilla文档中描述为:

window.frames伪数组中的每一项都代表了对应于给定's或's内容的窗口对象,而不是(i)frame DOM元素(即window.frames[0]与document.getElementsByTagName("iframe")[0]. contentwindow)。

作为一个开发基于web的应用程序超过25年的人,我可以肯定地说,这种奇怪行为的唯一原因是向后兼容性。

方法getElementById甚至不是DOM Level 1的一部分。但是,早期的DOM Level 0允许直接访问页面上的大多数元素:

https://docstore.mik.ua/orelly/webprog/dhtml/ch02_04.htm

正如你所看到的,像Netscape 6和MSIE 5这样的浏览器继续支持旧的约定,甚至在getElementById被采纳为"标准"之后。

最早支持DHTML的浏览器,如Netscape Navigator和MSIE,经常将自己的DOM规范推向市场。所以你最终得到了很多糟糕的设计决策,这些决策最终因为"以前就是这样做的"而标准化了。 https://www.quirksmode.org/js/dom0.html

为了向后兼容,甚至是更高级的浏览器那些支持1级DOM的人,仍然支持旧的,忠实的0级DOM。不支持它意味着最普通脚本突然不再工作了。所以即使0级DOM并不完全适合新的DOM概念,即浏览器将继续支持。

当Netscape引入0级DOM时,人们可能认为基于web的应用程序永远不会超过自动填充几个表单字段和在屏幕上移动一些图像。因此,不用担心使用少量元素id污染全局窗口对象。

显然,这个解决方案根本不能很好地扩展。但是为了不破坏遗留的Javascript代码,我们仍然坚持使用这个古老的0级DOM约定。