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

我有两个几乎相同的简单的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规范或有关冲突的JS函数名称和元素ID的限制?

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

如果您的表单控件(这里是一个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对象(实现W3C DOM中的HTMLFormElement接口的对象)之后。

但是,这里引用的表单控件对象是不可调用的(不实现ECMAScript内部[[Call]]方法或实现它,以便在调用时抛出异常)。 所以如果你尝试调用带有border(this.value)的对象border(this.value) ,就会抛出一个TypeError异常,你应该在脚本控制台中看到这个异常(例如,在开发工具Chromium 16.0.912.77中的“TypeError:border不是函数” [开发人员构建118311 Linux])。

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

然后是DOM Level 2 HTML Specification,这是继续努力标准化和扩展当前DOM实现的常见功能。 从2003-01-09开始的W3C推荐标准,它的ECMAScript语言绑定指定可以通过它们的名称 标识符来使用HTMLCollection项来访问它们,方括号属性访问器语法[等同于调用实现对象的namedItem()方法 HTMLCollection接口。

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

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

需要等同

 document.myForm.myControl 

因此,最近W3C DOM Level 2 HTML接口的实现,这个特性也开始适用于IDid属性值)的元素 (例如在Chromium中可以看到)。

因此,16年前在JavaScript™中引入的便利功能仍然像现在的客户端DOM脚本中的一样。

如果您避免使用相同的名称或ID作为用户定义函数的标识符,并且已经用于内置表单属性(如actionsubmitreset )的表单控件和表单,那么这将变得更少的问题。 另外,使用相同的标识符作为函数及其参数之一(混淆代码),使得函数对象在函数内不可访问(函数上下文的变量对象在其范围链中首先出现) )。

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

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

编辑:

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