¿Por qué el nombre de la función JS entra en conflicto con el ID del elemento?

Resuelto Oleg Mikheev asked hace 12 años • 3 respuestas

Tengo dos violines JS simples casi idénticos que llaman a una función al cambiar de selección. El nombre de la función es el mismo que el ID de selección en ambos casos, pero por alguna razón el primer violín funciona bien y el segundo falla con un error de JavaScript is not a function:

http://jsfiddle.net/AZkfy/7/ - funciona bien en 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>

y

http://jsfiddle.net/cYVzk/ - falla en 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>

En primer lugar, no entiendo por qué el primero funciona bien y el segundo falla.

En segundo lugar , ¿existen especificaciones o restricciones de JS con respecto a los nombres de funciones de JS y el ID de elemento en conflicto?

Oleg Mikheev avatar Feb 06 '12 16:02 Oleg Mikheev
Aceptado

Este es un problema de cadena de alcance heredado que se origina desde JavaScript 1.0 a 1.3 cuando no había distinción entre el lenguaje de programación y lo que ahora llamamos API DOM ("HTML dinámico" en aquel entonces).

Si su control de formulario (aquí: un selectelemento) es parte de un formulario (descendiente de un formelemento), entonces el Formobjeto que representa el formelemento es el tercero en la cadena de alcance de código en los valores de atributos del controlador de eventos del control (segundo- el siguiente es el objeto de control del formulario en sí, el siguiente es el objeto variable de ese código).

JavaScript™ fue diseñado por Brendan Eich (entonces en Netscape) como un lenguaje de programación fácil de usar para principiantes y que funciona bien con documentos HTML (como complemento de Java de Sun; de ahí el nombre siempre confuso). Debido a que en aquellos primeros días el lenguaje y la API DOM (Netscape) eran uno, esta (sobre)simplificación se aplicaba también a la API DOM: un Formobjeto tiene los nombres de los controles contenidos en el formulario que representa como los nombres de sus propiedades que Consulte los objetos de control de formulario correspondientes . OIA, puedes escribir

myForm.border

que es la abreviatura patentada de compatible con los estándares ( W3C DOM Nivel 2 HTML ), pero igualmente compatible con versiones anteriores

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

Ahora, si usa el nombre de un control de formulario en un valor de atributo de controlador de eventos de un control de formulario en un formulario , como

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

eso es lo mismo que si hubieras escrito la media propiedad

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

o el que cumple con las normas

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

porque una border()función global potencial es una propiedad del objeto global ECMAScript que viene en último lugar, después del Formobjeto (un objeto que implementa la HTMLFormElementinterfaz en el DOM del W3C), en la cadena de alcance.

Sin embargo, el objeto de control de formulario al que se hace referencia aquí borderno se puede invocar (no implementa el [[Call]]método interno de ECMAScript ni lo implementa de modo que genere una excepción cuando se llama). Entonces, si intenta llamar al objeto con border(this.value), se genera una TypeErrorexcepción, que debería ver en las consolas de script (como "TypeError: el borde no es una función" en las Herramientas de desarrollo de Chromium 16.0.912.77 [Desarrollador Build 118311 Linux]) .

Microsoft, el competidor de Netscape en la década de 1990, tuvo que copiar esa característica para MSHTML DOM para que el código escrito para Netscape también se ejecutara en Internet Explorer (3.0), con JScript (1.0). Y los competidores de Microsoft lo copiaron en sus implementaciones DOM exactamente por la misma razón. Se convirtió en parte de un cuasi estándar (ahora llamado " DOM Nivel 0 ").

Luego vino la especificación HTML DOM Nivel 2, un esfuerzo continuo para estandarizar y ampliar las características comunes de las implementaciones DOM existentes en ese momento. Una recomendación del W3C desde el 9 de enero de 2003, su enlace de lenguaje ECMAScript especifica que HTMLCollectionse puede acceder a los elementos de s por su nombre o ID con la sintaxis del accesor de propiedad de corchetes [... ], equivalente a llamar al namedItem()método del objeto que implementa la HTMLCollectioninterfaz.

formLos objetos de elemento y los objetos de elemento para controles de formulario en formularios son elementos de HTMLCollections en el DOM del W3C HTMLDocument::formsy HTMLFormElement::elements, respectivamente. Pero para compatibilidad con versiones anteriores de los navegadores,

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

debe ser equivalente a

document.myForm.myControl

Entonces, con las implementaciones de las interfaces HTML W3C DOM Nivel 2 a más tardar, esta característica también comenzó a aplicarse a elementos con ID ( valor de atributo) (que se puede ver en Chromium, por ejemplo).id

Como resultado, la característica de conveniencia introducida en JavaScript™ hace 16 años todavía te molesta como un error en las secuencias de comandos DOM del lado del cliente en la actualidad.

Si evita usar el mismo nombre o ID para los controles de formulario y los formularios que usa como identificador de funciones definidas por el usuario y que ya se usan para las propiedades de formulario integradas (como action, submity reset), entonces esto se vuelve un problema menor. . Además, es una mala idea usar el mismo identificador para la función y uno de sus argumentos (aparte del código confuso) que hace que el objeto de la función sea inaccesible desde dentro de la función (el objeto variable del contexto de la función viene primero en su cadena de alcance). ).

PointedEars avatar Feb 06 '2012 12:02 PointedEars

IE reserva automáticamente un var ID = domElement;espacio global para cada elemento DOM con un archivo ID. Algunos otros navegadores adoptaron este comportamiento.

¡Intente siempre evitar el uso de los mismos ID y nombres de usuario! Alternativamente, use su propio espacio de nombres en JS para evitar colisiones.

EDITAR:

No sé por qué uno de tus ejemplos falla y el otro funciona. Podría ser un simple problema de tiempo/orden de ejecución causado por el ajuste <form>.

Christoph avatar Feb 06 '2012 09:02 Christoph