UD3 - 1. Eventos¶
- Introducción
- Cómo escuchar un evento
- Tipos de eventos
- Los objetos this y event
- Propagación de eventos (bubbling)
- innerHTML y listeners de eventos
- Eventos personalizados
Introducción¶
Nos permiten detectar acciones que realiza el usuario o cambios que suceden en la página y reaccionar en respuesta a ellas. Existen muchos eventos diferentes (podéis ver la lista en w3schools) aunque nos centraremos en los más comunes.
Javascript permite ejecutar código cuando se produce un evento (por ejemplo el evento click del ratón) asociando al mismo una función. Hay varias formas de hacerlo.
Cómo escuchar un evento¶
La primera manera "estándar" de asociar código a un evento era añadiendo un atributo con el nombre del evento a escuchar (con 'on' delante) en el elemento HTML. Por ejemplo, para ejecutar código al producirse el evento 'click' sobre un botón se escribía:
Una mejora era llamar a una función que contenía el código:
Esto "ensucia" con código la página HTML por lo que se creó el modelo de registro de eventos tradicional que permitía asociar a un elemento HTML una propiedad con el nombre del evento a escuchar (con 'on' delante). En el caso anterior:
NOTA:
Debemos tener cuidado porque si se ejecuta el código antes de que se haya creado el botón estaremos asociando la función al evento click de un elemento que aún no existe así que no hará nada.
Para evitarlo siempre es conveniente poner el código que atiende a los eventos dentro de una función que se ejecute al producirse el evento load de la ventana. Este evento se produce cuando se han cargado todos los elementos HTML de la página y se ha creado el árbol DOM. Lo mismo habría que hacer con cualquier código que modifique el árbol DOM.
Por ejemplo:
Event listeners¶
La forma recomendada de hacerlo es usando el modelo avanzado de registro de eventos del W3C. Se usa el método addEventListener que recibe como primer parámetro el nombre del evento a escuchar (sin 'on') y como segundo parámetro la función a ejecutar, sin paréntesis:
document.getElementById('boton1').addEventListener('click', pulsado);
function pulsado() {
alert('Se ha pulsado');
}
Habitualmente se usan funciones anónimas ya que no necesitan ser llamadas desde fuera del listener:
document.getElementById('boton1').addEventListener('click', function() {
alert('Se ha pulsado');
});
Si queremos pasarle algún parámetro a la función listener (cosa bastante poco usual) debemos usar funciones anónimas:
NOTA:
Igual que antes debemos asegurarnos que se ha creado el árbol DOM antes de poner un listener por lo que se recomienda ponerlos siempre dentro de la función asociada al evento window.onload (o mejor window.addEventListener('load', ...) como en el ejemplo anterior).
Una ventaja de este método es que podemos poner varios listeners para el mismo evento y se ejecutarán todos ellos. Para eliminar un listener se usa el método removeEventListener.
NOTA:
No se puede quitar un listener si hemos usado una función anónima, para quitarlo debemos usar como listener una función con nombre.
Tipos de eventos¶
Según qué o dónde se produce un evento estos se clasifican en:
Eventos de página¶
Se producen en el documento HTML, normalmente en el BODY:
load: se produce cuando termina de cargarse la página (cuando ya está construido el árbol DOM). Es útil para hacer acciones que requieran que el DOM esté cargado como modificar la página o poner listeners de eventos.unload: al destruirse el documento (ej. cerrar).beforeUnload: antes de destruirse (podríamos mostrar un mensaje de confirmación).resize: si cambia el tamaño del documento (porque se redimensiona la ventana).
Eventos de ratón¶
Los produce el usuario con el ratón:
click/dblclick: cuando se hace click/doble click sobre un elemento.mousedown/mouseup: al pulsar/soltar cualquier botón del ratón.mouseenter/mouseleave: cuando el puntero del ratón entra/sale del elemento.mouseover/mouseout: cuando el puntero del ratón entra/sale del elemento o de cualquiera de sus descendientes (se produce en el elemento y en todos sus descendientes).mousemove: se produce continuamente mientras el puntero se mueva dentro del elemento.
NOTA
Si hacemos doble click sobre un elemento la secuencia de eventos que se produciría es: mousedown -> mouseup -> click -> mousedown -> mouseup -> click -> dblclick
ACTIVIDAD 1: 📂 UD3/act01/
Dada la siguiente página: calculadora asocia los eventos necesarios para que funcione la calculadora.
-
El botón
=debe ejecutar la funcióncalcular()que recibe como parámetro el contenido de la caja de texto y muestra el resultado de la operación.- Para realizar el cálculo puedes usar la función
evalde JavaScript que recibe como parámetro una cadena de texto con una expresión matemática y devuelve el resultado de evaluarla. Por ejemplo:eval('2+3')devuelve5.
- Para realizar el cálculo puedes usar la función
-
El botón
Cdebe borrar el contenido de la caja de texto. - Debes poner un listener al resto de botones para que al hacer click se ejecute la función
pulsado()que recibe como parámetro el valor del botón pulsado. Esta función debe mostrar en la caja de texto el valor del botón pulsado.
Eventos de teclado¶
Los produce el usuario al usar el teclado:
keydown: se produce al presionar una tecla y se repite continuamente si la tecla se mantiene pulsadakeyup: cuando se deja de presionar la tecla
Eventos de toque¶
Se producen al usar una pantalla táctil:
touchstart: se produce cuando se detecta un toque en la pantalla táctiltouchend: cuando se deja de pulsar la pantalla táctiltouchmove: cuando un dedo es desplazado a través de la pantallatouchcancel: cuando se interrumpe un evento táctil.
Eventos de formulario¶
Se producen en los formularios:
focus/blur: al obtener/perder el foco el elementochange: al perder el foco un<input>o<textarea>si ha cambiado su contenido o al cambiar de valor un<select>o un<checkbox>input: al cambiar el valor de un<imput>o<textarea>(se produce cada vez que escribimos una letra es estos elementos)select: al cambiar el valor de un<select>o al seleccionar texto de un<input>o<textarea>submit/reset: al enviar/recargar un formulario
Los objetos this y event¶
Al producirse un evento se generan automáticamente el listener 2 objetos:
this: hace referencia al elemento que contiene el código en donde se encuentra la variablethis. En el caso de una función listener será el elemento que tiene el listener que ha recibido el evento.-
event: es un objeto y la función listener lo recibe como parámetro. Tiene propiedades y métodos que nos dan información sobre el evento, como:.type: qué evento se ha producido (click,submit,keyDown, etc.)..target: el elemento donde se produjo el evento (puede serthiso un descendiente dethis, como en el ejemplo siguiente).-
.currentTarget: el elemento que contiene el listener del evento lanzado (normalmente el mismo quethis).Ejemplo
Si tenemos un
<p>al que le ponemos un listener de 'click' que dentro tiene un elemento<span>, si hacemosclicksobre el<span>event.targetserá el<span>que es donde hemos hecho click (está dentro de<p>) pero tantothiscomoevent.currentTargetserá<p>(que es quien tiene el listener que se está ejecutando). -
.relatedTarget: en un evento 'mouseover'event.targetes el elemento donde ha entrado el puntero del ratón yevent.relatedTargetel elemento del que ha salido. En un evento 'mouseout' sería al revés. cancelable: si el evento puede cancelarse. En caso afirmativo se puede llamar aevent.preventDefault()para cancelarlo.preventDefault(): si un evento tiene un listener asociado, se ejecuta el código de dicho listener y después el navegador realiza la acción que correspondería por defecto al evento si no tuviera listener (por ejemplo un listener del eventoclicksobre un hiperenlace hará que se ejecute su código y después saltará a la página indicada en elhrefdel hiperenlace). Este método cancela la acción por defecto del navegador para el evento. Por ejemplo si el evento era elsubmitde un formulario éste no se enviará o si era unclicksobre un hiperenlace no se irá a la página indicada en él..stopPropagation: un evento se produce sobre un elemento y todos su padres. Por ejemplo si hacemos click en un<span>que está en un<p>que está en un<div>que está en el BODY el evento se va propagando por todos estos elementos y saltarían los listeners asociados a todos ellos (si los hubiera). Si alguno llama a este método el evento no se propagará a los demás elementos padre.- Eventos de ratón:
.button: qué botón del ratón se ha pulsado (0: izquierdo,1: central (rueda);2: derecho)..screenX/.screenY: las coordenadas del ratón respecto a la pantalla..clientX/.clientY: las coordenadas del ratón respecto a la ventana cuando se produjo el evento..pageX/.pageY: las coordenadas del ratón respecto al documento (si se ha hecho un scroll será el clientX/Y más el scroll)..offsetX/.offsetY: las coordenadas del ratón respecto al elemento sobre el que se produce el evento..detail: si se ha hecho click, doble click o triple click.
- Eventos de teclado: son los más incompatibles entre diferentes navegadores. En el teclado hay teclas normales y especiales (
Alt,Ctrl,Shift,Enter,Tab, flechas,Supr, ...). En la información del teclado hay que distinguir entre el código del carácter pulsado (e=101,E=69,€=8364) y el código de la tecla pulsada (para los 3 caracteres es el 69 ya que se pulsa la misma tecla). Las principales propiedades deeventson:.key: devuelve el nombre de la tecla pulsada..which: devuelve el código de la tecla pulsada..keyCode/.charCode: código de la tecla pulsada y del carácter pulsado (según navegadores)..shiftKey/.ctrlKey/.altKey/.metaKey: si está o no pulsada la tecla SHIFT / CTRL / ALT / META. Esta propiedad también la tienen los eventos de ratón.
NOTA:
A la hora de saber qué tecla ha pulsado el usuario es conveniente tener en cuenta:
- para saber qué carácter se ha pulsado lo mejor usar la propiedad
keyocharCodedekeyPress, pero varía entre navegadores. - para saber la tecla especial pulsada mejor usar el
keyo elkeyCodedekeyUp - captura sólo lo que sea necesario, se producen muchos eventos de teclado.
- para obtener el carácter a partir del código:
String fromCharCode(código);
Lo mejor para familiarizarse con los diferentes eventos es consultar los ejemplos de w3schools.
ACTIVIDAD 2: 📂 UD3/act02/
Añade a la calculadora del ejercicio anterior los siguientes eventos:
- Al pulsar una tecla numérica se debe mostrar en la caja de texto el número pulsado.
- Al pulsar una tecla de operación se debe mostrar en la caja de texto el símbolo de la operación pulsada.
- Al pulsar la tecla
Enterse debe ejecutar la funcióncalcular()que recibe como parámetro el contenido de la caja de texto y muestra el resultado de la operación. - Al pulsar la tecla
Backspacese debe borrar el último carácter de la caja de texto. - Al pulsar la tecla
EscapeoSuprse debe borrar el contenido de la caja de texto.
Bindeo del objeto this¶
En ocasiones no queremos que this sea el elemento sobre quien se produce el evento sino que queremos conservar el valor que tenía antes de entrar a la función. Por ejemplo, la función listener es un método de una clase, en this tenemos el objeto de la clase sobre el que estamos actuando pero al entrar en la función perdemos esa referencia.
El método .bind() nos permite pasarle a una función el valor que queremos darle a la variable this dentro de dicha función. Por defecto a un listener de eventos se le bindea le valor de event.currentTarget. Si queremos que tenga otro valor se lo indicamos con .bind():
En este ejemplo el valor de this dentro de la función aceptado será variable. En el ejemplo que habíamos comentado de un listener dentro de una clase, para mantener el valor de this y que haga referencia al objeto sobre el que estamos actuando haríamos:
por lo que el valor de this dentro de la función aceptado será el mismo que tenía fuera, es decir, el objeto.
Podemos bindear, es decir, pasarle al listener más variables declarándolas como parámetros de bind. El primer parámetro será el valor de this y los demás serán parámetros que recibirá la función antes de recibir el parámetro event que será el último. Por ejemplo:
document.getElementById('acepto').addEventListener('click', aceptado.bind(var1, var2, var3));
...
function aceptado(param1, param2, event) {
// Aquí dentro tendremos los valores
// this = var1
// param1 = var2
// param2 = var3
// event es el objeto con la información del evento producido
}
Propagación de eventos (bubbling)¶
Normalmente en una página web los elementos HTML se solapan unos con otros, por ejemplo, un <span> está en un <p> que está en un <div> que está en el <body>. Si ponemos un listener del evento click a todos ellos se ejecutarán todos ellos, pero ¿en qué orden?.
Pues el W3C establecíó un modelo en el que primero se disparan los eventos de fuera hacia dentro (primero el <body>) y al llegar al más interno (el <span>) se vuelven a disparar de nuevo pero de dentro hacia afuera. La primera fase se conoce como fase de captura y la segunda como fase de burbujeo. Cuando ponemos un listener con addEventListener el tercer parámetro indica en qué fase debe dispararse:
true: en fase de capturafalse(valor por defecto): en fase de burbujeo
Podéis ver un ejemplo en:
Sin embargo si al método .addEventListener le pasamos un tercer parámetro con el valor true el comportamiento será el contrario, lo que se conoce como captura y el primer listener que se ejecutará es el del <body> y el último el del <span> (podéis probarlo añadiendo ese parámetro al ejemplo anterior).
En cualquier momento podemos evitar que se siga propagando el evento ejecutando el método .stopPropagation() en el código de cualquiera de los listeners.
Podéis ver las distintas fases de un evento en la página domevents.dev.
innerHTML y listeners de eventos¶
Si cambiamos la propiedad innerHTML de un elemento del árbol DOM todos sus listeners de eventos desaparecen ya que es como si se volviera a crear ese elemento.
Por ejemplo, tenemos una tabla de datos y queremos que al hacer doble click en cada fila se muestre su id. La función que añade una nueva fila podría ser:
Sin embargo esto sólo funcionaría para la última fila añadida ya que la línea miTabla.innerHTML += nuevaFila equivale a miTabla.innerHTML = miTabla.innerHTML + nuevaFila. Por tanto estamos asignando a miTabla un código HTML que ya no contiene listeners, excepto el de nuevaFila que lo ponemos después de hacer la asignación.
La forma correcta de hacerlo sería:
De esta forma además mejoramos el rendimiento ya que el navegador sólo tiene que renderizar el nodo correspondiente a la nuevaFila. Si lo hacemos como estaba al principio se deben volver a crear y a renderizar todas las filas de la tabla (todo lo que hay dentro de miTabla).
Eventos personalizados¶
También podemos mediante código lanzar manualmente cualquier evento sobre un elemento con el método dispatchEvent() e incluso crear eventos personalizados, por ejemplo:
| js | |
|---|---|
Incluso podemos añadir datos al objeto event si creamos el evento con new CustomEvent(). Podéis obtener más información en la página de MDN.
| js | |
|---|---|
PROYECTO:
Para comprobar tus conocimientos hasta el momento, puedes realizar el proyecto Clave secreta.