Почему имя функции JS конфликтует с идентификатором элемента? - PullRequest
40 голосов
/ 06 февраля 2012

У меня есть две почти идентичные простые скрипты JS, вызывающие функцию при изменении выбора.Имя функции совпадает с идентификатором выбора в обоих случаях, но по какой-то причине первая скрипка работает просто отлично, а вторая не работает с ошибкой 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 и идентификатора элемента?

Ответы [ 3 ]

62 голосов
/ 06 февраля 2012

Это устаревшая проблема цепочки областей видимости, возникшая в JavaScript 1.0-1.3, когда не было никакого различия между языком программирования и тем, что мы сейчас называем DOM API (тогда «Динамический HTML»).

Если элемент управления формы (здесь: элемент select) является частью формы (потомок элемента form), то объект Form, представляющий элемент form, занимает третье-следующее место в область видимости кода в значениях атрибутов обработчика событий элемента управления (второй-следующий - сам объект элемента управления формы, следующий - объект-переменная этого кода).

JavaScript ™ был спроектирован Бренданом Айхом (тогда в Netscape) как язык программирования, который прост в использовании для начинающих и который хорошо работает с HTML-документами (как дополнение к Java от Sun; отсюда и постоянно запутывающее имя). Поскольку в те ранние времена язык и (Netscape) DOM API были единым целым, это (чрезмерное) упрощение также применялось к DOM API: A 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 Global Object, который идет последним, после Form объекта (объекта, реализующего интерфейс HTMLFormElement в W3C DOM), в цепочке областей действия.

Однако объект управления формой, на который здесь ссылается border, не вызывается (не реализует встроенный в ECMAScript метод [[Call]] или реализует его так, чтобы он вызывал исключение при вызове). Поэтому, если вы попытаетесь вызвать объект с border(this.value), будет выдано исключение TypeError, которое вы должны увидеть в консолях скрипта (например, «TypeError: border не является функцией» в инструментах разработчика Chromium 16.0.912.77 [ Разработчик Build 118311 Linux]).

Microsoft, конкурент Netscape в 1990-х годах, пришлось скопировать эту функцию для MSHTML DOM , чтобы код, написанный для Netscape, также работал в Internet Explorer (3.0) с JScript (1.0). И конкуренты Microsoft скопировали его в свои реализации DOM по той же причине. Он стал частью квази-стандарта (теперь называется " DOM Level 0 ").

Затем появилась HTML-спецификация DOM уровня 2 - постоянная попытка стандартизировать и расширить общие возможности существующих реализаций DOM того времени. Рекомендация W3C с 2003-01-09, ее Связывание языка ECMAScript указывает, что элементы HTMLCollection s могут быть доступны по их имени или ID с помощью Синтаксис метода доступа к скобкам [], эквивалентный вызову namedItem() метода объекта, реализующего интерфейс HTMLCollection.

Элементы

form и элементы элементов для элементов управления форм в формах - это элементы HTMLCollection s в DOM W3C, HTMLDocument::forms и HTMLFormElement::elements, соответственно. Но для обратной совместимости в браузерах,

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

должен быть эквивалент

document.myForm.myControl

Итак, с последними реализациями HTML-интерфейсов W3C DOM Level 2 эта функция стала применяться и к элементам с идентификатором (id значение атрибута), а также (что можно увидеть в Chromium). например).

В результате удобная функция, появившаяся в JavaScript ™ 16 лет назад, все еще кусает вас, как баг в сценариях DOM на стороне клиента.

Если вы избегаете использования того же имени или идентификатора для элементов управления формы и форм, которые вы используете в качестве идентификатора пользовательских функций и которые уже используются для встроенных свойств формы (например, action, submit и reset ), тогда это становится меньшей проблемой. Кроме того, плохой идеей является использование того же идентификатора для функции и одного из ее аргументов, что и (смущая код в стороне), что делает объект функции недоступным изнутри функции (объект переменной контекста функции занимает первое место в своей цепочке областей действия ).

5 голосов
/ 06 февраля 2012

IE автоматически резервирует var ID = domElement; в глобальном пространстве для каждого DOM-элемента с ID.Некоторые другие браузеры приняли это поведение.

Всегда старайтесь избегать использования одинаковых идентификаторов и имен!В качестве альтернативы, используйте свое собственное пространство имен в JS, чтобы избежать коллизий.

РЕДАКТИРОВАТЬ:

Я не знаю, почему один из ваших примеров дает сбой, а другой работает.Это может быть простой выбор времени / порядка выполнения, вызванный переносом <form>.

1 голос
/ 06 февраля 2012

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

Мне кажется, что тег form создает дополнительную область вокруг встроенных обработчиков событий, а элементы формы определяются как переменные в этой локальной области:

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

В моих тестах глобально не определены переменные, но они могут различаться в разных браузерах / режимах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...