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

时间:2012-02-06 09:44:53

标签: javascript html

我有两个几乎相同的简单JS小提琴在选择更改时调用一个函数。在这两种情况下,函数名称与选择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 个答案:

答案 0 :(得分:62)

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

如果表单控件(此处为:select元素)是表单的一部分(form元素的后代),则表示Form的{​​{1}}对象} element是控件的事件处理程序属性值中的代码范围链中的第三个下一个(第二个是表单控件对象本身,接下来是该代码的变量对象)。

JavaScript™由Brendan Eich(当时在Netscape)设计,作为一种易于初学者使用的编程语言,可以很好地与HTML文档配合使用(作为Sun Java的补充;因此名称不断混淆)。因为在早期的语言和(Netscape)DOM API是一体的,这种(过度)简化也应用于DOM API:A Form object has the names of the controls contained in the form that it represents as the names of its properties that refer to the corresponding form control objects。 IOW,你可以写

form

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

myForm.border

现在,如果您在表单中的表单控件的事件处理程序属性值中使用表单控件的名称,例如

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> 函数是最后出现的ECMAScript全局对象的属性,之后的 border()对象(实现{的对象)范围链中的{1}} W3C DOM中的接口。

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

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

然后出现了DOM Level 2 HTML规范,这是继续努力标准化和扩展当前现有DOM实现的常见功能。自2003-01-09以来的W3C建议书,其ECMAScript Language Binding指定border(this.value)项可以通过其名称​​或 ID 访问括号属性访问器语法TypeError ... HTMLCollection,相当于调用实现[接口的对象的namedItem() method

表单中表单控件的

]元素对象和元素对象分别是W3C DOM HTMLDocument::formsHTMLFormElement::elements中的HTMLCollection个项。但是为了在浏览器中向后兼容,

form

需要等效

HTMLCollection

因此,最近使用W3C DOM Level 2 HTML界面的实现,此功能也开始应用于ID (document.forms["myForm"].elements["myControl"] 属性值)的元素(可以是例如,在Chromium中看到的。

因此,16年前JavaScript™中引入的便利功能仍然让您感觉像是当今客户端DOM脚本中的一个错误。

如果您避免对用作用户定义函数标识符的表单控件和表单使用相同的名称或ID,并且已经用于内置表单属性(例如document.myForm.myControl ,{{1 }}和id),那么这就变得不那么重要了。此外,对函数使用相同的标识符并使​​用其中一个参数(使代码混淆)使得函数对象在函数内不可访问是一个坏主意(函数上下文的变量对象首先出现在其作用域链中) )。

答案 1 :(得分:5)

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

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

编辑:

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

答案 2 :(得分:1)

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

在我看来,form标记围绕内联事件处理程序创建了一个额外的范围,表单元素在本地范围内定义为变量:

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

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