为什么JS函数名和元素ID冲突?

IT技术 javascript html
2021-01-12 20:48:52

我有两个几乎相同的简单 JS 小提琴,在选择更改时调用函数。在这两种情况下,函数名称都与 select ID 相同,但由于某种原因,第一个小提琴工作正常,第二个失败并出现 JavaScript 错误is not a function

http://jsfiddle.net/AZkfy/7/ - 在 FF9 (Linux)、Chromium 16 (Linux)、IE8 (Windows) 中工作正常:

<script>
    function border(border) { alert(border); }
</script>

<select id='border' name='border' onchange='border(this.value)'>
    <option value='foo'>foo</option>
    <option value='bar'>bar</option>
</select>

http://jsfiddle.net/cYVzk/ - 在 FF9 (Linux)、Chromium 16 (Linux)、IE8 (Windows) 中失败:

<script>
    function border(border) { alert(border); }
</script>

<form>
<select id='border' name='border' onchange='border(this.value)'>
    <option value='foo'>foo</option>
    <option value='bar'>bar</option>
</select>
</form>

首先,我不明白为什么第一个工作正常,而第二个失败。

其次- 是否有关于冲突的 JS 函数名称和元素 ID 的任何 JS 规范或限制?

3个回答

这是一个遗留范围链问题,起源于 JavaScript 1.0 到 1.3,当时编程语言和我们现在称之为 DOM API(当时的“动态 HTML”)之间没有区别。

如果您的表单控件(此处:select元素)是表单的一部分(form元素的后代),则Form表示该form元素对象在控件的事件处理程序属性值(第二个-下一个是表单控件对象本身,下一个是该代码的变量对象)。

JavaScript™ 是由 Brendan Eich(当时在 Netscape 工作)设计的,是一种易于初学者使用的编程语言,并且可以很好地处理 HTML 文档(作为 Sun 的 Java 的补充;因此名称总是令人困惑)。因为在早期语言和 (Netscape) DOM API 是一回事,这种(过度)简化也适用于 DOM API:一个Form对象具有包含在表单中的控件名称,它表示为它的属性名称参考对应的表单控件对象IOW,你可以写

myForm.border

这是符合标准(W3C DOM Level 2 HTML的专有简写,但同样向后兼容

document.forms["myForm"].elements["border"]

现在,如果你在一个窗体控件的事件处理程序的属性值使用窗体控件的名称的形式,像

<form …>
  <… name="border" onchange='border(this.value)' …>
</form>

这就像你写了半专有

<form …>
  <… name="border" onchange='this.form.border(this.value)' …>
</form>

或符合标准

<form …>
  <… name="border" onchange='this.form.elements["border"](this.value)' …>
</form>

因为一个潜在的全球border()功能是的属性的ECMAScript其去年来的时候,全局对象之后Form对象(实施对象HTMLFormElement在W3C DOM接口),作用域链。

但是,这里引用的表单控件对象border是不可调用的(没有实现 ECMAScript 内部的[[Call]]方法或实现它以便在调用时抛出异常)。因此,如果您尝试使用 调用对象border(this.value)TypeError则会引发异常,您应该在脚本控制台中看到异常(例如 Chromium 16.0.912.77 [Developer Build 118311 Linux] 的开发人员工具中的“TypeError: border is not a function”) .

1990 年代 Netscape 的竞争对手 Microsoft 不得不为MSHTML DOM复制该功能,以便为 Netscape 编写的代码也可以在带有JScript (1.0) 的Internet Explorer (3.0) 中运行微软的竞争对手出于完全相同的原因将其复制到他们的 DOM 实现中。它成为准标准(现在称为“ DOM Level 0 ”)的一部分。

然后是 DOM Level 2 HTML 规范,这是对当时现有 DOM 实现的通用功能进行标准化和扩展的持续努力。自 2003-01-09 以来的 W3C 建议,其ECMAScript 语言绑定指定HTMLCollection可以使用括号属性访问器语法...通过其名称 ID访问 s 的项目,相当于调用实现接口的对象方法[]namedItem()HTMLCollection

form表单中的表单控件的元素对象和元素对象分别HTMLCollection是 W3C DOM 中sHTMLDocument::formsHTMLFormElement::elements但是为了在浏览器中向后兼容,

document.forms["myForm"].elements["myControl"]

需要等价

document.myForm.myControl

因此,随着最新的 W3C DOM Level 2 HTML 接口的实现,此功能也开始应用于具有 IDid属性值)的元素(例如,可以在 Chromium 中看到)。

因此,16 年前在 JavaScript™ 中引入的便利特性仍然像今天客户端 DOM 脚本中的错误一样让您感到厌烦。

如果您避免对用作用户定义函数标识符的表单控件和表单使用相同的名称或 ID,并且已用于内置表单属性(如actionsubmit、 和reset),那么这不会成为问题. 此外,为函数使用相同的标识符及其参数之一是一个坏主意(将混淆代码放在一边)使函数对象无法从函数内部访问(函数上下文的变量对象首先出现在其作用域链中) )。

获取全局对象引用的内联调用仅在全局上下文未声明为在严格模式下执行时才有效。我实际上不确定全局严格模式声明 ( 'use strict';) 是按文件还是按元素,或者script分别在包含的脚本资源或元素内容中使用时传递到全局 HTML 上下文
2021-03-18 20:48:52
@Christoph 不客气 :) 由于问题是关于 ID 的,我对此进行了进一步研究,并添加了解释 W3C DOM 2 HTML 如何将此问题与元素 ID 相关联。还应该注意的是,表单对象在作用域链中排在第三位。如果不是为了消除歧义,我们就不需要写this.value,只要value(!)。
2021-04-01 20:48:52
我刚刚Chrome Version 94.0.4606.81 (Official Build) (64-bit) 在 2021 年 10 月使用时遇到了这个问题!!!- 这让我疯狂了一个小时。就我而言:` <button id="showPass" style="width:60px;margin-left:10%;min-width:unset;float:right;" type="button" class="btn btn-primary buttonPrimary" onclick="showPass();"><i style="color:#FA9800 !important;" class="icon icon-eye"></i></button> `
2021-04-02 20:48:52
@rolinger:这个标记还有很多需要改进的地方。而且你不应该有全局自定义showPass函数;它应该是用户定义对象的方法。但是,如果您无法帮助按钮的命名(“btn-showPass”将是替代方案)或showPass函数的定义,那么您可以使用onclick='(function () { return this; }()).showPass()',其中函数返回对 ECMAScript 全局对象的引用。如果在多个地方需要,您可以var _global = this;在全局执行上下文中定义,然后使用_global.showPass();
2021-04-03 20:48:52

IE 自动var ID = domElement;为每个 DOM 元素在全局空间中保留一个ID. 其他一些浏览器采用了这种行为。

始终尽量避免使用相同的 ID 和变量名!或者,在 JS 中使用您自己的命名空间来避免冲突。

编辑:

我不知道为什么你的一个例子失败了,而另一个有效。这可能是由 wrapping 引起的一个简单的时间/执行顺序问题<form>

一个的@Christoph创建属性选择元素。
2021-03-17 20:48:52
@Mathias 我不知道,这也适用于某些元素的 name 属性。所以问题确实是,为选项元素创建一个变量会影响附加到全局对象的函数。
2021-03-22 20:48:52
对于更改,这与 MSHTML 通过作用域链中的全局宿主对象的属性访问命名/ID 元素的对象无关,也与计时无关。这是一个遗留的作用域链问题,在标准合规模式中也会发生(而名称/ID 问题在除 IE 之外的几个浏览器中都没有)。
2021-04-03 20:48:52
@Christoph HTML5 规范中提到了您所描述的内容:whatwg.org/specs/web-apps/current-work/multipage/...将来可能会被删除
2021-04-04 20:48:52
正如我在回答中提到的,我认为表单可能会延迟 dom 元素对 var 的占用。因此,该函数首先分配给 var,然后被 dom 元素覆盖。
2021-04-08 20:48:52

http://jsfiddle.net/x79ey/1/

在我看来,form标签围绕内联事件处理程序创建了一个额外的作用域,并且表单元素被定义为这个局部作用域中的变量:

<form>
    <element id="foo"....
    <element onclick="foo is a local variable here"

在我的测试中,没有全局自动定义变量,但这可能因浏览器/模式而异。