問題描述
我有兩個幾乎相同的簡單 JS Fiddles 調用一個功能選擇更改。函數名與這兩種情況下的 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 規範或限制?
最佳解決方案
當編程語言與我們現在稱之為 DOM API(“Dynamic HTML”) 之間沒有任何區別時,這是源自 JavaScript 1.0 到 1.3 的遺留範圍鏈問題。
如果您的表單控件 (這裏是 select 元素) 是表單的一部分 (form 元素的後代),則表示 form 元素的 Form 對象在控件的事件處理程序屬性中的代碼範圍鏈中是第三個值 (第二個是表單控件對象本身,接下來是該代碼的變量對象) 。
JavaScript™由 Brendan Eich(然後在 Netscape) 設計為一種易於使用的初學者的編程語言,並且與 HTML 文檔 (作為 Sun 的 Java 的補充; 因此是 ever-confusing 的名稱) 一起使用。因為在早期的語言和 (Netscape)DOM API 中,DOM API 也是一樣,這個 (過) 簡化也適用於 DOM API:Form 對象的名稱包含在表單中的控件的名稱作為其屬性指的是對應的表單控件對象。 IOW,你可以寫
myForm.border
這是 standards-compliant(W3C DOM Level 2 HTML) 的專有縮寫,同樣也是 backwards-compatible
document.forms["myForm"].elements["border"]
現在,如果您在表單控件的 event-handler 屬性值中使用表單控件的名稱,例如
<form …>
<… name="border" onchange='border(this.value)' …>
</form>
那就像你寫的是 half-proprietary 一樣
<form …>
<… name="border" onchange='this.form.border(this.value)' …>
</form>
或 standards-compliant
<form …>
<… name="border" onchange='this.form.elements["border"](this.value)' …>
</form>
因為潛在的全局 border()函數是在範圍鏈中的 Form 對象 (實現 W3C DOM 中的 HTMLFormElement 接口的對象) 之後的最後的 ECMAScript 全局對象的屬性。
但是,border 引用的表單控件對象不可調用 (不實現 ECMAScript-internal [[Call]]方法或實現它,以便在調用時引發異常) 。因此,如果您嘗試使用 border(this.value)調用該對象,則會拋出 TypeError 異常,您將在腳本控制枱中看到 (例如 「Chromium 16.0.912.77 的開發工具」 中的 「TypeError:border is not a function」)[Developer Build 118311 Linux ]) 。
Netscape 在 20 世紀 90 年代的競爭對手微軟必須將該功能複製到 MSHTML DOM,以便為 Netscape 編寫的代碼也將在 Internet Explorer(3.0) 中運行,並使用 JScript(1.0) 。而且微軟的競爭對手將它複製到了 DOM 實現中也是完全相同的。它成為 quasi-standard(現稱為 「DOM Level 0」) 的一部分。
然後來到了 DOM Level 2 的 HTML 規範,這是一個持續的努力,以標準化和擴展現有的 DOM 實現的共同特徵。 W3C 推薦自 2003-01-09 以來,其 ECMAScript Language Binding 指定 HTMLCollection 的項目可以通過其名稱或 ID 使用括號屬性訪問器語法 [ … ]訪問,相當於實現 HTMLCollection 接口的對象的 namedItem()方法。
form 表單中的表單控件的元素對象和元素對象分別是 W3C DOM,HTMLDocument::forms 和 HTMLFormElement::elements 中的 HTMLCollection 的項目。但是對於瀏覽器的向後兼容性,
document.forms["myForm"].elements["myControl"]
需要等同於
document.myForm.myControl
因此,最近 W3C DOM 2 級 HTML 接口的實現,此功能開始適用於 ID(id 屬性值) 的元素 (例如可以在 Chromium 中看到) 。
因此,16 年前的 JavaScript™中引入的便捷功能仍然使您像今天的 client-side DOM 腳本中的錯誤一樣。
如果您避免使用與用作 user-defined 函數標識符的表單控件和表單相同的名稱或 ID,並且已經用於內置表單屬性 (如 action,submit 和 reset),那麼這不再是一個問題。此外,將函數和其參數之一用作相同的標識符 (將混淆代碼放在一邊) 是一個壞主意,使得函數對象在函數內無法訪問 (函數上下文的變量對象首先在其作用域鏈中) 。
次佳解決方案
IE 在 ID 的每個 DOM 元素的全局空間中自動保留 var ID = domElement; 。其他一些瀏覽器採用這種行為。
始終儘量避免使用相同的 ID 和 varnames!或者,在 JS 中使用自己的命名空間來避免衝突。
編輯:
我不知道為什麼你的一個例子失敗,而另一個失敗。這可能是一個簡單的執行順序 – 由包裝<form> 引起的發生。
參考文獻
注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。