Los identificadores de elementos con nombre se pueden especificar como JavaScript Globals | trucos CSS
¿Sabía que los elementos DOM con identificadores están disponibles en JavaScript como variables globales? Es una de esas cosas que ha existido desde siempre, pero realmente estoy investigando por primera vez.
Si estás escuchando sobre esto por primera vez, ¡prepárate! Podemos verlo en acción simplemente agregando una identificación a un elemento en el HTML:
<div id="cool"></div>
Normalmente definiríamos una nueva variable usando querySelector("#cool")
o getElementById("cool")
para seleccionar este elemento:
var el = querySelector("#cool");
Pero en realidad ya tenemos acceso a #cool
sin este rigmorale:
Así que cualquiera id
- o name
atributo, para el caso - en HTML se puede acceder en JavaScript usando el window[ELEMENT_ID]
Nuevamente, esto no es exactamente "nuevo", pero es realmente inusual de ver.
Como puede suponer, acceder al ámbito global con referencias nombradas no es la mejor idea. Algunas personas han llegado a llamar a esto el "contaminante de rango global". Llegaremos a por qué es así, pero primero...
algo de contexto
Este enfoque es descrito en la especificación HTMLdonde se describe como “acceso designado de Window
objeto."
Internet Explorer fue el primero en implementar la función. Todos los demás navegadores también lo agregaron. Gecko era el único navegador en ese momento que no lo admitía directamente en modo estándar, eligiendo en su lugar convertirlo en una función experimental. Hubo vacilación en implementarlo en absoluto, pero avanzado en nombre de la compatibilidad del navegador (Gecko incluso intentó convencer WebKit para sacarlo del modo estándar) y terminaste moviéndolo al modo estándar en Firefox 14.
Una cosa que puede no ser muy conocida es que los navegadores han tenido que implementar varias medidas de seguridad, con diversos grados de éxito, para garantizar que los datos globales generados no dañen 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 eclipsa las variables globales existentesEntonces, si un elemento DOM tiene id
que ya está definido como global, no reemplazará al existente. Por ejemplo:
<head>
<script>
window.foo = "bar";
</script>
</head>
<body>
<div id="foo">I won't override window.foo</div>
<script>
console.log(window.foo); // Prints "bar"
</script>
</body>
Y lo contrario también es cierto:
<div id="foo">I will be overridden :(</div>
<script>
window.foo = "bar";
console.log(window.foo); // Prints "bar"
</script>
Este comportamiento es esencial porque cancela sustituciones peligrosas como <div id="alert" />
que de otro modo crearía un conflicto al invalidar el alert
API Esta técnica de seguridad puede ser la razón por la que usted, si es como yo, está aprendiendo sobre ella por primera vez.
El caso contra los globales nombrados
Dije anteriormente que usar elementos globales con nombre como referencias podría no ser la mejor idea. Hay muchas razones para esto que TJ VanToll tiene un buen vistazo a su blog y lo resumiré aquí:
- Si el DOM cambia, también lo hace la referencia. Esto hace que algunos sean realmente "frágiles" (el término de la especificación para él) código donde la separación de preocupaciones entre HTML y JavaScript puede ser demasiado grande.
- Las referencias aleatorias son demasiado fáciles. Un simple error tipográfico podría conducir a una referencia a un global con nombre y brindarle resultados inesperados.
- Se implementa de manera diferente en los navegadores. Por ejemplo, necesitamos acceder a un ancla con
id
- p.ej<a id="cool">
— pero algunos navegadores (a saber, Safari y Firefox) devuelven unReferenceError
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
<div class="cool">
— el navegador debería devolver unHTMLCollection
con una matriz de instancias. Sin embargo, Firefox solo devuelve la primera instancia. Entonces otra vez, la especificacion dice debemos usar una instancia de unid
de cualquier manera en el árbol de un elemento. Pero no impedirá que la página funcione ni nada. - ¿Quizás hay una tarifa de rendimiento? Quiero decir que el navegador tiene que hacer esta lista de referencias y mantenerla. Varias personas hicieron pruebas en este hilo de StackOverflowdonde estaban realmente los globales nombrados más eficiente en una sola prueba y menos productivo en una prueba más reciente.
Consideraciones adicionales
Digamos que rechazamos las críticas contra el uso de globales con nombre y los usamos de todos modos. Todo esta bien. Pero hay algunas cosas que quizás desee considerar mientras lo hace.
Polífilos
Por extraño que parezca, este tipo de controles globales son un requisito de configuración típico para un polyfill. Vea el siguiente ejemplo donde configuramos una cookie usando el nuevo CookieStore
APIrellenándolo con poly en los navegadores que aún no lo admiten:
<body>
<img id="cookieStore"></img>
<script>
// 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");
</script>
</body>
Este código funciona perfectamente en Chrome, pero arroja el siguiente error en Safari:
TypeError: cookieStore.set is not a function
Safari no tiene soporte para CookieStore
API en el momento de escribir este artículo. Como resultado, el polyfill no se aplica porque img
El ID del elemento crea una variable global que choca con cookieStore
global.
Actualizaciones de la API de JavaScript
Podemos revertir la situación y encontrar otro problema donde las actualizaciones del motor de JavaScript del navegador pueden fallar. las referencias globales de un elemento nombrado.
Por ejemplo:
<body>
<input id="BarcodeDetector"></input>
<script>
window.BarcodeDetector.focus();
</script>
</body>
Este script toma una referencia al elemento de entrada y llama focus()
a él. Trabaja apropiadamente. Aunque no sabemos cómo largo seguirá trabajando.
Verá, la variable global que usamos para hacer referencia al elemento de entrada dejará de funcionar tan pronto como los navegadores comiencen a admitir BarcodeDetector
APIEn este momento, window.BarcodeDetector
global ya no será una referencia al elemento de entrada y .focus()
tirará"window.BarcodeDetector.focus
no es un error de función”.
Bonificación: no todos los elementos con nombre generan referencias globales
¿Quieres escuchar algo divertido? Para colmo de males, los elementos con nombre solo son accesibles como variables globales si los nombres no contienen más que letras. Los navegadores no crearán una referencia global para un elemento con un identificador que contenga caracteres especiales y números, como hello-world
y item1
.
Conclusión
Recapitulemos cómo llegamos aquí:
- Todos los principales navegadores crean automáticamente referencias globales a cada elemento DOM con
id
(o, en algunos casos, unname
atributo). - Acceder a estos elementos a través de sus referencias globales no es confiable y es potencialmente peligroso
querySelector
ogetElementById
en cambio. - Debido a que las referencias globales se generan automáticamente, pueden tener algunos efectos secundarios en su código. Esta es una buena razón para evitar el uso
id
atributo a menos que realmente lo necesite.
En última instancia, probablemente sea una buena idea evitar el uso de globales con nombre en JavaScript. Anteriormente cité la especificación sobre cómo esto conduce a un código "frágil", pero aquí está el texto completo para llamar su atención:
Como regla general, confiar en esto dará como resultado un código inestable. Los ID que finalmente se asignan a esta API pueden variar con el tiempo, ya que, por ejemplo, se agregan nuevas funciones a la plataforma web. En su lugar, utiliza
document.getElementById()
odocument.querySelector()
.
Creo que el hecho de que la propia especificación HTML recomiende mantenerse alejado de esta función habla por sí mismo.
Deja una respuesta