Logotipo de Zephyrnet

Los ID de elementos con nombre pueden referenciarse como globales de JavaScript

Fecha:

¿Sabía que los elementos DOM con ID son accesibles en JavaScript como variables globales? Es una de esas cosas que ha existido desde siempre, pero realmente estoy investigando por primera vez.

Si es la primera vez que lo escuchas, ¡prepárate! Podemos verlo en acción simplemente agregando una ID a un elemento en HTML:

Normalmente, definiríamos una nueva variable usando querySelector("#cool") or getElementById("cool") para seleccionar ese elemento:

var el = querySelector("#cool");

Pero en realidad ya tenemos acceso a #cool sin esa bronca:

Así que cualquiera id - o name atributo, para el caso, en el HTML se puede acceder en JavaScript usando window[ELEMENT_ID]. Una vez más, esto no es exactamente "nuevo", pero es muy poco común de ver.

Como puede suponer, acceder al alcance global con referencias nombradas no es la mejor idea. Algunas personas han llegado a llamar a esto el "contaminador de alcance global". Explicaremos por qué es así, pero primero...

Algun contexto

Este enfoque es descrito en la especificación HTML, donde se describe como "acceso designado en el Window objeto."

Internet Explorer fue el primero en implementar la característica. Todos los demás navegadores también lo agregaron. Gecko era el único navegador en ese momento que no lo admitía directamente en el modo estándar, sino que optaba por convertirlo en una característica experimental. Hubo vacilación en implementarlo en absoluto, pero avanzó en nombre de la compatibilidad del navegador (Gecko incluso trató de convencer WebKit para sacarlo del modo estándar) y eventualmente llegó al modo estándar en Firefox 14.

Una cosa que podría no ser muy conocida es que los navegadores tuvieron que implementar algunas medidas de precaución, con diversos grados de éxito, para garantizar que los globales generados no rompan la página web. Una de esas medidas es…

Sombreado variable

Probablemente la parte más interesante de esta función es que las referencias a elementos con nombre no sombrear las variables globales existentes. Entonces, si un elemento DOM tiene un id que ya está definido como global, no anulará el existente. Por ejemplo:


  
    window.foo = "bar";
  


  
I won't override window.foo
console.log(window.foo); // Prints "bar"

Y lo contrario también es cierto:

I will be overridden :(
window.foo = "bar"; console.log(window.foo); // Prints "bar"

Este comportamiento es esencial porque anula anulaciones peligrosas como

, que de otro modo crearía un conflicto al invalidar el alert API. Esta técnica de protección bien puede ser la razón por la que usted, si es como yo, está aprendiendo sobre esto por primera vez.

El caso contra los globales nombrados

Anteriormente, dije que usar elementos globales con nombre como referencias podría no ser la mejor idea. Hay muchas razones para eso, que TJ VanToll ha cubierto muy bien en su blog y lo resumiré aquí:

  • Si el DOM cambia, también lo hace la referencia. Eso hace que algunos sean realmente "frágiles" (el término de la especificación para ello) código donde la separación de preocupaciones entre HTML y JavaScript podría ser demasiado.
  • Las referencias accidentales son demasiado fáciles. Un simple error tipográfico puede muy bien terminar haciendo referencia a un nombre global y darte resultados inesperados.
  • Se implementa de manera diferente en los navegadores. Por ejemplo, deberíamos poder acceder a un ancla con un id - p.ej — pero algunos navegadores (a saber, Safari y Firefox) devuelven un ReferenceError en la consola.
  • Puede que no devuelva lo que piensas. De acuerdo con la especificación, cuando hay varias instancias del mismo elemento con nombre en el DOM, por ejemplo, dos instancias de

    — el navegador debería devolver un HTMLCollection con una matriz de las instancias. Firefox, sin embargo, solo devuelve la primera instancia. Entonces otra vez, la especificación dice deberíamos usar una instancia de un id en el árbol de un elemento de todos modos. Pero hacerlo no impedirá que una página funcione ni nada por el estilo.

  • Tal vez hay un costo de rendimiento? Quiero decir, el navegador tiene que hacer esa lista de referencias y mantenerla. Un par de personas hicieron pruebas en este hilo de StackOverflow, donde los globales con nombre eran en realidad más rendimiento en una prueba y menos rendimiento en una prueba más reciente.

Consideraciones adicionales

Digamos que descartamos las críticas contra el uso de globales con nombre y los usamos de todos modos. Está todo bien. Pero hay algunas cosas que quizás desee considerar mientras lo hace.

polirellenos

Por más exagerado que parezca, este tipo de comprobaciones globales son un requisito de configuración típico para los polyfills. Mira el siguiente ejemplo donde configuramos una cookie usando el nuevo CookieStore API, polillenándolo en navegadores que aún no lo admiten:


  
  
    // Polyfill the CookieStore API if not yet implemented.
    // https://developer.mozilla.org/en-US/docs/Web/API/CookieStore
    if (!window.cookieStore) {
      window.cookieStore = myCookieStorePolyfill;
    }
    cookieStore.set("foo", "bar");
  

Este código funciona perfectamente bien en Chrome, pero arroja el siguiente error en Safari:

TypeError: cookieStore.set is not a function

Safari carece de soporte para el CookieStore API a partir de este escrito. Como resultado, el polyfill no se aplica porque el img elemento ID crea una variable global que choca con el cookieStore global.

Actualizaciones de la API de JavaScript

Podemos cambiar la situación y encontrar otro problema donde las actualizaciones del motor de JavaScript del navegador pueden romper las referencias globales de un elemento con nombre.

Por ejemplo:


  
  
    window.BarcodeDetector.focus();
  

Ese script toma una referencia al elemento de entrada e invoca focus() en eso. Funciona correctamente. Aún así, no sabemos cómo Corto seguirá funcionando.

Verá, la variable global que estamos usando para hacer referencia al elemento de entrada dejará de funcionar tan pronto como los navegadores comiencen a admitir el BarcodeDetector API. En ese punto, el window.BarcodeDetector global ya no será una referencia al elemento de entrada y .focus() lanzará un “window.BarcodeDetector.focus no es un error de función”.

Bonificación: no todos los elementos con nombre generan referencias globales

¿Quieres escuchar algo gracioso? Para colmo de males, los elementos con nombre son accesibles como variables globales solo si los nombres no contienen más que letras. Los navegadores no crearán una referencia global para un elemento con una ID que contenga caracteres especiales y números, como hello-world y item1.

Conclusión

Resumamos cómo llegamos aquí:

  • Todos los principales navegadores crean automáticamente referencias globales a cada elemento DOM con un id (o, en algunos casos, un name atributo).
  • Acceder a estos elementos a través de sus referencias globales no es confiable y es potencialmente peligroso. Usar querySelector or getElementById preferiblemente.
  • Dado que las referencias globales se generan automáticamente, pueden tener algunos efectos secundarios en su código. Esa es una buena razón para evitar usar el id atributo a menos que realmente lo necesite.

Al final del día, probablemente sea una buena idea evitar el uso de globales con nombre en JavaScript. Cité la especificación anteriormente sobre cómo conduce a un código "frágil", pero aquí está el texto completo para aclarar el punto:

Como regla general, confiar en esto conducirá a un código frágil. Los ID que terminan mapeándose en esta API pueden variar con el tiempo, a medida que se agregan nuevas funciones a la plataforma web, por ejemplo. En lugar de esto, utilice document.getElementById() or document.querySelector().

Creo que el hecho de que la propia especificación HTML recomiende mantenerse alejado de esta función habla por sí solo.

punto_img

Información más reciente

punto_img

Habla con nosotros!

¡Hola! ¿Le puedo ayudar en algo?