En este modelo, cada elemento o etiqueta HTML define su propia lista de posibles eventos que se le pueden asignar. Un mismo tipo de evento (por ejemplo, pinchar el botón izquierdo del ratón) puede estar definido para varios elementos HTML diferentes y un mismo elemento HTML puede tener asociados varios eventos diferentes.
El nombre de cada evento se construye mediante el prefijo on
, seguido del nombre en inglés de la acción asociada al evento. Así, el evento de pinchar un elemento con el ratón se denomina onclick
y el evento asociado a la acción de mover el ratón se denomina onmousemove
.
La siguiente tabla resume los eventos más importantes definidos por JavaScript:
Los eventos más utilizados en las aplicaciones web tradicionales son onload
para esperar a que se cargue la página por completo, los eventos onclick
, onmouseover
, onmouseout
para controlar el ratón y onsubmit
para controlar el envío de los formularios.
Algunos eventos de la tabla anterior (onclick
, onkeydown
, onkeypress
, onreset
, onsubmit
) permiten evitar la "acción por defecto" de ese evento. Más adelante se muestra en detalle este comportamiento, que puede resultar muy útil en algunas técnicas de programación.
Las acciones típicas que realiza un usuario en una página web pueden dar lugar a una sucesión de eventos. Al pulsar por ejemplo sobre un botón de tipo <input type="submit">
se desencadenan los eventos onmousedown
, onclick
, onmouseup
y onsubmit
de forma consecutiva.
Un evento de JavaScript por sí mismo carece de utilidad. Para que los eventos resulten útiles, se deben asociar funciones o código JavaScript a cada evento. De esta forma, cuando se produce un evento se ejecuta el código indicado, por lo que la aplicación puede responder ante cualquier evento que se produzca durante su ejecución.
Las funciones o código JavaScript que se definen para cada evento se denominan "manejador de eventos" y como JavaScript es un lenguaje muy flexible, existen varias formas diferentes de indicar los manejadores:
Manejadores de eventos como atributos HTML
Se trata del método más sencillo y a la vez menos profesional de indicar el código JavaScript que se debe ejecutar cuando se produzca un evento. En este caso, el código se incluye en un atributo del propio elemento HTML. En el siguiente ejemplo, se quiere mostrar un mensaje cuando el usuario pinche con el ratón sobre un botón:
<input type="button" value="Pinchame y verás" onclick="alert('Gracias por pinchar');" />
En este método, se definen atributos HTML con el mismo nombre que los eventos que se quieren manejar. El ejemplo anterior sólo quiere controlar el evento de pinchar con el ratón, cuyo nombre es onclick
. Así, el elemento HTML para el que se quiere definir este evento, debe incluir un atributo llamado onclick
.
El contenido del atributo es una cadena de texto que contiene todas las instrucciones JavaScript que se ejecutan cuando se produce el evento. En este caso, el código JavaScript es muy sencillo (alert('Gracias por pinchar');
), ya que solamente se trata de mostrar un mensaje.
En este otro ejemplo, cuando el usuario pincha sobre el elemento <div>
se muestra un mensaje y cuando el usuario pasa el ratón por encima del elemento, se muestra otro mensaje:
<div onclick="alert('Has pinchado con el ratón');" onmouseover="alert('Acabas de pasar el ratón por encima');"> Puedes pinchar sobre este elemento o simplemente pasar el ratón por encima </div>
Este otro ejemplo incluye una de las instrucciones más utilizadas en las aplicaciones JavaScript más antiguas:
<body onload="alert('La página se ha cargado completamente');"> ... </body>
El mensaje anterior se muestra después de que la página se haya cargado completamente, es decir, después de que se haya descargado su código HTML, sus imágenes y cualquier otro objeto incluido en la página.
El evento onload
es uno de los más utilizados ya que, como se vio en el capítulo de DOM, las funciones que permiten acceder y manipular los nodos del árbol DOM solamente están disponibles cuando la página se ha cargado completamente.
Manejadores de eventos y variable this
JavaScript define una variable especial llamada this
que se crea automáticamente y que se emplea en algunas técnicas avanzadas de programación. En los eventos, se puede utilizar la variable this
para referirse al elemento XHTML que ha provocado el evento. Esta variable es muy útil para ejemplos como el siguiente:
Cuando el usuario pasa el ratón por encima del <div>
, el color del borde se muestra de color negro. Cuando el ratón sale del <div>
, se vuelve a mostrar el borde con el color gris claro original.
Elemento <div>
original:
<div id="contenidos" style="width:150px; height:60px; border:thin solid silver"> Sección de contenidos... </div>
Si no se utiliza la variable this
, el código necesario para modificar el color de los bordes, sería el siguiente:
<div id="contenidos" style="width:150px; height:60px; border:thin solid silver" onmouseover="document.getElementById('contenidos').style.borderColor='black';" onmouseout="document.getElementById('contenidos').style.borderColor='silver';"> Sección de contenidos... </div>
El código anterior es demasiado largo y demasiado propenso a cometer errores. Dentro del código de un evento, JavaScript crea automáticamente la variable this
, que hace referencia al elemento HTML que ha provocado el evento. Así, el ejemplo anterior se puede reescribir de la siguiente manera:
<div id="contenidos" style="width:150px; height:60px; border:thin solid silver" onmouseover="this.style.borderColor='black';" onmouseout="this.style.borderColor='silver';"> Sección de contenidos... </div>
El código anterior es mucho más compacto, más fácil de leer y de escribir y sigue funcionando correctamente aunque se modifique el valor del atributo id
del <div>
.
Manejadores de eventos como funciones externas
La definición de los manejadores de eventos en los atributos XHTML es el método más sencillo pero menos aconsejable de tratar con los eventos en JavaScript. El principal inconveniente es que se complica en exceso en cuanto se añaden algunas pocas instrucciones, por lo que solamente es recomendable para los casos más sencillos.
Si se realizan aplicaciones complejas, como por ejemplo la validación de un formulario, es aconsejable agrupar todo el código JavaScript en una función externa y llamar a esta función desde el elemento XHTML.
Siguiendo con el ejemplo anterior que muestra un mensaje al pinchar sobre un botón:
<input type="button" value="Pinchame y verás" onclick="alert('Gracias por pinchar');" />
Utilizando funciones externas se puede transformar en:
function muestraMensaje() { alert('Gracias por pinchar');} <input type="button" value="Pinchame y verás" onclick="muestraMensaje()" />
Esta técnica consiste en extraer todas las instrucciones de JavaScript y agruparlas en una función externa. Una vez definida la función, en el atributo del elemento XHTML se incluye el nombre de la función, para indicar que es la función que se ejecuta cuando se produce el evento.
La llamada a la función se realiza de la forma habitual, indicando su nombre seguido de los paréntesis y de forma opcional, incluyendo todos los argumentos y parámetros que se necesiten.
El principal inconveniente de este método es que en las funciones externas no se puede seguir utilizando la variable this
y por tanto, es necesario pasar esta variable como parámetro a la función:
function resalta(elemento) { switch(elemento.style.borderColor) { case 'silver': case 'silver silver silver silver': case '#c0c0c0': elemento.style.borderColor = 'black'; break; case 'black': case 'black black black black': case '#000000': elemento.style.borderColor = 'silver'; break; }} <div style="width:150px; height:60px; border:thin solid silver" onmouseover="resalta(this)" onmouseout="resalta(this)"> Sección de contenidos... </div>
En el ejemplo anterior, la función externa es llamada con el parámetro this
, que dentro de la función se denomina elemento
. La complejidad del ejemplo se produce sobre todo por la forma en la que los distintos navegadores almacenan el valor de la propiedad borderColor
.
Mientras que Firefox almacena (en caso de que los cuatro bordes coincidan en color) el valor black
, Internet Explorer lo almacena como black black black black
y Opera almacena su representación hexadecimal #000000
.
Manejadores de eventos semánticos
Los métodos que se han visto para añadir manejadores de eventos (como atributos HTML y como funciones externas) tienen un grave inconveniente: "ensucian" el código HTML de la página.
Como es conocido, una de las buenas prácticas básicas en el diseño de páginas y aplicaciones web es la separación de los contenidos (HTML) y su aspecto o presentación (CSS). Siempre que sea posible, también se recomienda separar los contenidos (HTML) y su comportamiento o programación (JavaScript).
Mezclar el código JavaScript con los elementos HTML solamente contribuye a complicar el código fuente de la página, a dificultar la modificación y mantenimiento de la página y a reducir la semántica del documento final producido.
Afortunadamente, existe un método alternativo para definir los manejadores de eventos de JavaScript. Esta técnica es una evolución del método de las funciones externas, ya que se basa en utilizar las propiedades DOM de los elementos HTML para asignar todas las funciones externas que actúan de manejadores de eventos. Así, el siguiente ejemplo:
<input id="pinchable" type="button" value="Pinchame y verás" onclick="alert('Gracias por pinchar');" />
Se puede transformar en:
// Función externafunction muestraMensaje() { alert('Gracias por pinchar');} // Asignar la función externa al elemento document.getElementById("pinchable").onclick = muestraMensaje; // Elemento HTML<input id="pinchable" type="button" value="Pinchame y verás" />
La técnica de los manejadores semánticos consiste en:
id
.El último paso es la clave de esta técnica. En primer lugar, se obtiene el elemento al que se desea asociar la función externa:
document.getElementById("pinchable");
A continuación, se utiliza una propiedad del elemento con el mismo nombre que el evento que se quiere manejar. En este caso, la propiedad es onclick
:
document.getElementById("pinchable").onclick = ...
Por último, se asigna la función externa mediante su nombre sin paréntesis. Lo más importante (y la causa más común de errores) es indicar solamente el nombre de la función, es decir, prescindir de los paréntesis al asignar la función:
document.getElementById("pinchable").onclick = muestraMensaje;
Si se añaden los paréntesis después del nombre de la función, en realidad se está ejecutando la función y guardando el valor devuelto por la función en la propiedad onclick
de elemento.
// Asignar una función externa a un evento de un elemento document.getElementById("pinchable").onclick = muestraMensaje; // Ejecutar una función y guardar su resultado en una propiedad de un elemento document.getElementById("pinchable").onclick = muestraMensaje();
La gran ventaja de este método es que el código XHTML resultante es muy "limpio", ya que no se mezcla con el código JavaScript. Además, dentro de las funciones externas asignadas sí que se puede utilizar la variable this
para referirse al elemento que provoca el evento.
El único inconveniente de este método es que la página se debe cargar completamente antes de que se puedan utilizar las funciones DOM que asignan los manejadores a los elementos XHTML. Una de las formas más sencillas de asegurar que cierto código se va a ejecutar después de que la página se cargue por completo es utilizar el evento onload
:
window.onload = function() { document.getElementById("pinchable").onclick = muestraMensaje;}
La técnica anterior utiliza el concepto de funciones anónimas, que no se va a estudiar, pero que permite crear un código compacto y muy sencillo. Para asegurarse que un código JavaScript va a ejecutarse después de que la página se haya cargado completamente, sólo es necesario incluir esas instrucciones entre los símbolos {
y }
:
window.onload = function() { ... }
En el siguiente ejemplo, se añaden eventos a los elementos de tipo input=text
de un formulario complejo:
function resalta() { // Código JavaScript} window.onload = function() { var formulario = document.getElementById("formulario"); var camposInput = formulario.getElementsByTagName("input"); for(var i=0; i<camposInput.length; i++) { if(camposInput[i].type == "text") { camposInput[i].onclick = resalta; } }}
Normalmente, los manejadores de eventos requieren información adicional para procesar sus tareas. Si una función por ejemplo se encarga de procesar el evento onclick
, quizás necesite saber en que posición estaba el ratón en el momento de pinchar el botón.
No obstante, el caso más habitual en el que es necesario conocer información adicional sobre el evento es el de los eventos asociados al teclado. Normalmente, es muy importante conocer la tecla que se ha pulsado, por ejemplo para diferenciar las teclas normales de las teclas especiales (ENTER
, tabulador, Alt
, Ctrl.
, etc.).
JavaScript permite obtener información sobre el ratón y el teclado mediante un objeto especial llamado event
. Desafortunadamente, los diferentes navegadores presentan diferencias muy notables en el tratamiento de la información sobre los eventos.
La principal diferencia reside en la forma en la que se obtiene el objeto event
. Internet Explorer considera que este objeto forma parte del objeto window
y el resto de navegadores lo consideran como el único argumento que tienen las funciones manejadoras de eventos.
Aunque es un comportamiento que resulta muy extraño al principio, todos los navegadores modernos excepto Internet Explorer crean mágicamente y de forma automática un argumento que se pasa a la función manejadora, por lo que no es necesario incluirlo en la llamada a la función manejadora. De esta forma, para utilizar este "argumento mágico", sólo es necesario asignarle un nombre, ya que los navegadores lo crean automáticamente.
En resumen, en los navegadores tipo Internet Explorer, el objeto event
se obtiene directamente mediante:
var evento = window.event;
Por otra parte, en el resto de navegadores, el objeto event
se obtiene mágicamente a partir del argumento que el navegador crea automáticamente:
function manejadorEventos(elEvento) { var evento = elEvento;}
Si se quiere programar una aplicación que funcione correctamente en todos los navegadores, es necesario obtener el objeto event
de forma correcta según cada navegador. El siguiente código muestra la forma correcta de obtener el objeto event
en cualquier navegador:
function manejadorEventos(elEvento) { var evento = elEvento || window.event;}
Una vez obtenido el objeto event
, ya se puede acceder a toda la información relacionada con el evento, que depende del tipo de evento producido.
La propiedad type
indica el tipo de evento producido, lo que es útil cuando una misma función se utiliza para manejar varios eventos:
var tipo = evento.type;
La propiedad type
devuelve el tipo de evento producido, que es igual al nombre del evento pero sin el prefijo on
.
Mediante esta propiedad, se puede rehacer de forma más sencilla el ejemplo anterior en el que se resaltaba una sección de contenidos al pasar el ratón por encima:
function resalta(elEvento) { var evento = elEvento || window.event; switch(evento.type) { case 'mouseover': this.style.borderColor = 'black'; break; case 'mouseout': this.style.borderColor = 'silver'; break; }} window.onload = function() { document.getElementById("seccion").onmouseover = resalta; document.getElementById("seccion").onmouseout = resalta;} <div id="seccion" style="width:150px; height:60px; border:thin solid silver"> Sección de contenidos... </div>
De todos los eventos disponibles en JavaScript, los eventos relacionados con el teclado son los más incompatibles entre diferentes navegadores y por tanto, los más difíciles de manejar. En primer lugar, existen muchas diferencias entre los navegadores, los teclados y los sistemas operativos de los usuarios, principalmente debido a las diferencias entre idiomas.
Además, existen tres eventos diferentes para las pulsaciones de las teclas (onkeyup
, onkeypress
y onkeydown
). Por último, existen dos tipos de teclas: las teclas normales (como letras, números y símbolos normales) y las teclas especiales (como ENTER
, Alt
, Shift
, etc.)
Cuando un usuario pulsa una tecla normal, se producen tres eventos seguidos y en este orden: onkeydown
, onkeypress
y onkeyup
. El evento onkeydown
se corresponde con el hecho de pulsar una tecla y no soltarla; el evento onkeypress
es la propia pulsación de la tecla y el evento onkeyup
hace referencia al hecho de soltar una tecla que estaba pulsada.
La forma más sencilla de obtener la información sobre la tecla que se ha pulsado es mediante el evento onkeypress
. La información que proporcionan los eventos onkeydown
y onkeyup
se puede considerar como más técnica, ya que devuelven el código interno de cada tecla y no el carácter que se ha pulsado.
A continuación se incluye una lista con todas las propiedades diferentes de todos los eventos de teclado tanto en Internet Explorer como en el resto de navegadores:
keydown
:keyCode
: código interno de la teclacharCode
: no definidokeypress
:keyCode
: el código del carácter de la tecla que se ha pulsadocharCode
: no definidokeyCode
: para las teclas normales, no definido. Para las teclas especiales, el código interno de la tecla.charCode
: para las teclas normales, el código del carácter de la tecla que se ha pulsado. Para las teclas especiales, 0
.keyup
:keyCode
: código interno de la teclacharCode
: no definidoPara convertir el código de un carácter (no confundir con el código interno) al carácter que representa la tecla que se ha pulsado, se utiliza la función String.fromCharCode()
.
A continuación se incluye un script que muestra toda la información sobre los tres eventos de teclado:
window.onload = function() { document.onkeyup = muestraInformacion; document.onkeypress = muestraInformacion; document.onkeydown = muestraInformacion;} function muestraInformacion(elEvento) { var evento = window.event || elEvento; var mensaje = "Tipo de evento: " + evento.type + "<br>" + "Propiedad keyCode: " + evento.keyCode + "<br>" + "Propiedad charCode: " + evento.charCode + "<br>" + "Carácter pulsado: " + String.fromCharCode(evento.charCode); info.innerHTML += "<br>--------------------------------------<br>" + mensaje } ... <div id="info"></div>