¿Cómo funciona Javascript en línea (en HTML)?

129

Sé que esta es una mala práctica. No escriba código como este si es posible.

Por supuesto, siempre nos encontraremos en situaciones en las que un fragmento inteligente de Javascript en línea puede solucionar un problema rápidamente.

Estoy siguiendo esta consulta con el interés de comprender completamente lo que sucede (y las posibles dificultades) cuando se escribe algo como esto:

<a href="#" onclick="alert('Hi')">Click Me</a>

Por lo que puedo decir, esto es funcionalmente el mismo que

<script type="text/javascript">
   $(function(){ // I use jQuery in this example
       document.getElementById('click_me').onclick = 
           function () { alert('Hi'); };
   });
</script>
<a href="#" id="click_me">Click Me</a>

Extrapolando de esto, parece que la cadena asignada al atributo onclickse inserta dentro de una función anónima que se asigna al controlador de clic del elemento. ¿Es este realmente el caso?

Porque estoy empezando a hacer cosas como esta:

<a href="#" onclick="$(this).next().fadeIn(); return false;">Display my next sibling</a> <!-- Return false in handler so as not to scroll to top of page! --> 

Que funciona Pero no sé cuánto de esto es un hack. ¡Parece sospechoso porque no hay una función aparente de la que se está devolviendo!

Puedes preguntar, ¿por qué haces esto, Steve? ¡Inline JS es una mala práctica!

Bueno, para ser honesto, estoy cansado de editar tres secciones diferentes de código solo para modificar una sección de una página, especialmente cuando estoy creando prototipos de algo para ver si funciona. Es mucho más fácil y, a veces, incluso tiene sentido que el código específicamente relacionado con este elemento HTML se defina directamente dentro del elemento: cuando decido 2 minutos más tarde que esta fue una idea terrible, terrible, puedo bombardear todo el div (o lo que sea ) y no tengo un montón de misterioso JS y CSS cruft en el resto de la página, lo que ralentiza el renderizado muy ligeramente. Esto es similar al concepto de localidad de referencia, pero en lugar de errores de caché, estamos viendo errores e hinchazón de código.

Steven Lu
fuente
3
Tienes razón, es una función anónima.
bhamlin
1
Deberá enganchar la consulta #click_meen el evento DOMready o colocar el script después del nodo.
Bergi
1
En una consola, hazlo document.getElementById("click_me").onclick;. O alertarlo. Verás que está en una función.
D. Strout

Respuestas:

96

Lo tiene casi correcto, pero no ha tenido en cuenta el thisvalor proporcionado al código en línea.

<a href="#" onclick="alert(this)">Click Me</a>

en realidad está más cerca de:

<a href="#" id="click_me">Click Me</a>
<script type="text/javascript">
document.getElementById('click_me').addEventListener("click", function(event) {
    (function(event) {
        alert(this);
    }).call(document.getElementById('click_me'), event);
});
</script>

Los controladores de eventos en línea son thisiguales al objetivo del evento. También puede usar la función anónima en el script en línea

<a href="#" onclick="(function(){alert(this);})()">Click Me</a>
apsillers
fuente
77
El script debe aparecer después de la etiqueta, de lo contrario, la etiqueta no existe cuando se ejecuta; sugiero cambiar su respuesta para reflejar esto.
indefinido
¿Cómo propago el eventcon un en línea onclick='myFunction(event)'?
oldboy
9

Lo que hace el navegador cuando tienes

<a onclick="alert('Hi');" ... >

es establecer el valor real de "onclick" en algo efectivamente como:

new Function("event", "alert('Hi');");

Es decir, crea una función que espera un parámetro de "evento". (Bueno, IE no; es más como una simple función anónima simple).

Puntiagudo
fuente
2
Ah ¡Entonces puedo usar la variable eventpara recuperar el evento (y el elemento de origen a través de él event.target), desde mi fragmento JS en línea! Frio.
Steven Lu
1
IE realmente no pasa el eventparámetro, pero si usa eventdentro de esta función anónima, IE lo entenderá como el objeto de evento real. Como la función anónima se establece como una propiedad del windowobjeto en IE, verá esto como window.event.
Rdleal
8

Parece que hay muchas malas prácticas en torno a los atributos del controlador de eventos. Una mala práctica es no conocer y utilizar las funciones disponibles donde es más apropiado. Los atributos del evento son estándares totalmente documentados por el W3C y no hay nada de malo en ellos. No es diferente a colocar estilos en línea, que también está documentado por W3C y puede ser útil en ocasiones. Ya sea que lo coloques envuelto en etiquetas de script o no, se interpretará de la misma manera.

https://www.w3.org/TR/html5/webappapis.html#event-handler-idl-attributes

Daniel B
fuente
2
Sí, me inclino a aceptar, e incluso proporcioné "razones" razonables por las cuales este tipo particular de uso de código en línea podría estar justificado en ciertas situaciones. Pero la realidad es que cuando una aplicación (aplicación web o de otro tipo) llega a ser de cierta complejidad (y esto generalmente no toma mucho tiempo o trabajo para llegar) es probable que haya pequeños fragmentos de código esparcidos sobre el diseño HTML ser arquitectónicamente falsa desde una perspectiva de mantenibilidad. Incluso, incluso comenzando a codificar directamente JS dentro de un archivo HTML, por ejemplo, uno está tentando la pendiente resbaladiza de la spaghettification.
Steven Lu
1
Y ese es un argumento válido en contra, uno en el que estoy de acuerdo. Normalmente no agrego secuencias de comandos en línea, ni estilo para esa materia, yo mismo. Pero las personas tienen gustos diferentes. A muchos, por ejemplo, les gusta agregar guiones y etiquetas de estilo en la sección del cuerpo, no lo soporto. Pero es válido y a algunas personas les gusta. Lo que parece ser bad practice/spaghettificationpara algunos, es good practice/structurepara otros.
Daniel B
He estado mirando github.com/styled-components/styled-components y, si combina esto con reaccionar, por ejemplo, ahora tenemos todo lo relacionado con un componente de su aplicación que realmente vive juntos (con suerte pacíficamente) en un solo lugar. Y en realidad tampoco se ve terrible. Se siente como progreso. Desde este punto de vista, el bloqueo de estilos y código en línea en HTML no es la forma correcta (hasta ahora parece ser el bloqueo de HTML y estilos en el código JS).
Steven Lu
5

La mejor manera de responder a su pregunta es verla en acción.

<a id="test" onclick="alert('test')"> test </a> 

En el js

var test = document.getElementById('test');
console.log( test.onclick ); 

Como puede ver en el console, si está usando Chrome, imprime una función anónima con el objeto de evento pasado, aunque es un poco diferente en IE.

function onclick(event) {
   alert('test')
}

Estoy de acuerdo con algunos de sus puntos sobre los controladores de eventos en línea. Sí, son fáciles de escribir, pero no estoy de acuerdo con su punto de tener que cambiar el código en varios lugares, si estructura bien su código, no debería necesitar hacer esto.

aziz punjani
fuente
Creo que algo así <button onclick="login()"> test login </button>está perfectamente bien para la creación de prototipos. Desea probar algo que requiere la interacción del usuario en este momento, y de todos modos lo eliminará más tarde. ¿Por qué escribir código extra por todo el lugar?
Dagg Nabbit
No es un código adicional, solo una función anónima adicional. Y no está por todas partes, en realidad está todo en un solo lugar, en las etiquetas del script.
aziz punjani
No estoy seguro de que compartamos la misma idea de "estructurar bien su código". Si vincula la interfaz de usuario a sus "funciones principales" en el lugar donde realmente viven sus funciones principales, esto me parece una peor disposición de acoplamiento que simplemente vincularlas en la interfaz de usuario. Por lo general, mantengo todas las cosas vinculantes de la interfaz de usuario separadas de las cosas centrales, y tengo la sensación de que el OP también podría ...
Dagg Nabbit
3

¡Parece sospechoso porque no hay una función aparente de la que se está devolviendo!

Es una función anónima que se ha adjuntado al evento de clic del objeto.

¿Por qué haces esto, Steve?

¿Por qué demonios estás haciendo ...? Ah, no importa, como has mencionado, realmente es una mala práctica ampliamente adoptada :)

mattytommo
fuente
3

Prueba esto en la consola:

var div = document.createElement('div');

div.setAttribute('onclick', 'alert(event)');

div.onclick

En Chrome, muestra esto:

function onclick(event) {
  alert(event)
}

... y la namepropiedad no estándar de div.onclickes "onclick".

Entonces, si esto es anónimo o no, depende de su definición de "anónimo". Compare con algo como var foo = new Function(), donde foo.namehay una cadena vacía, y foo.toString()producirá algo como

function anonymous() {

}
Dagg Nabbit
fuente
Esto comenzó como un comentario sobre la respuesta de Pointy, pero realmente no funcionó como un comentario. La suya es realmente la mejor respuesta aquí, creo.
Dagg Nabbit