diff --git a/15_event.md b/15_event.md new file mode 100644 index 000000000..2a24a4658 --- /dev/null +++ b/15_event.md @@ -0,0 +1,1155 @@ +# Manejo de Eventos + +{{quote {author: "Marco Aurelio", title: Meditaciones, chapter: true} + +Tienes poder sobre tu mente, no sobre los acontecimientos. Date cuenta de esto, +y encontrarás la fuerza. + +quote}} + +{{index stoicism, "Marcus Aurelius", input, timeline}} + +{{figure {url: "img/chapter_picture_15.jpg", alt: "Imagínese una máquina de Rube Goldberg", chapter: "framed"}}} + +Algunos programas funcionan con la entrada directa del usuario, como las +acciones del mouse y el teclado. Ese tipo de entrada no está disponible como una +estructura de datos bien organizada, viene pieza por pieza, en tiempo real, y se +espera que el programa responda a ella a medida que sucede. + +## Manejador de eventos + +{{index polling, button, "real-time"}} + +Imagina una interfaz en donde la única forma de saber si una tecla del +((teclado)) está siendo presionada es leer el estado actual de esa tecla. Para +poder reaccionar a las pulsaciones de teclas, tendrías que leer constantemente +el estado de la tecla para poder detectarla antes de que se vuelva a soltar. +Esto sería peligroso al realizar otros cálculos que requieran mucho tiempo, ya +que se podría perder una pulsación de tecla. + +Algunas máquinas antiguas manejan las entradas de esa forma. Un paso adelante +de esto sería que el hardware o el sistema operativo detectaran la pulsación de +la tecla y lo pusieran en una cola. Luego, un programa puede verificar +periódicamente la cola por nuevos eventos y reaccionar a lo que encuentre allí. + +{{index responsiveness, "user experience"}} + +Por supuesto, este tiene que recordar de mirar la cola, y hacerlo con +frecuencia, porque en cualquier momento entre que se presione la tecla y que el +programa se de cuenta del evento causará que que el programa no responda. Este +enfoque es llamado _((sondeo))_. La mayororía de los programadores prefieren +evitarlo. + +{{index "callback function", "event handling"}} + +Un mejor mecanismo es que el sistema notifique activamente a nuestro código +cuando un evento ocurre. Los navegadores hacen esto permitiéndonos registrar +funciones como _manejadores_ (_((manejadores))_) para eventos específicos. + +```{lang: "text/html"} +
Da clic en este documento para activar el manejador.
+ +``` + +{{index "click event", "addEventListener method", "window object", [browser, window]}} + +La _vinculación_ `window` se refiere a un objeto integrado proporcionado por el +navegador. Este representa la ventana del navegador que contiene el documento. +Llamando a su método `addEventListener` se registra el segundo argumento que se +llamará siempre que ocurra el evento descrito por su primer argumento. + +## Eventos y nodos DOM + +{{index "addEventListener method", "event handling", "window object", browser, [DOM, events]}} + +Cada manejador de eventos del navegador es registrado dentro de un contexto. En +el ejemplo anterior llamamos a `addEventListener` en el objeto `window` para +registrar un manejador para toda la ventana. Este método puede también ser +encontrado en elementos DOM y en algunos otros tipos de objetos. Los +controladores de eventos son llamados únicamente cuando el evento ocurra en el +contexto del objeto en que están registrados. + +```{lang: "text/html"} + +No hay manejadores aquí.
+ +``` + +{{index "click event", "button (HTML tag)"}} + +Este ejemplo adjunta un manejador al nodo del botón. Los clics sobre el botón +hacen que se ejecute ese manejador, pero los clics sobre el resto del documento +no. + +{{index "onclick attribute", encapsulation}} + +Dar a un nodo un atributo `onclick` tiene un efecto similar. Esto funciona para +la mayoría de tipos de eventos—se puede adjuntar un manejador a través del +atributo cuyo nombre es el nombre del evento con `on` en frente de este. + +Pero un nodo puede tener únicamente un atributo `onclick`, por lo que se puede +registrar únicamente un manejador por nodo de esa manera. El método +`addEventListener` permite agregar cualquier número de manejadores siendo seguro +agregar manejadores incluso si ya hay otro manejador en el elemento. + +{{index "removeEventListener method"}} + +El método `removeEventListener`, llamado con argumentos similares a +`addEventListener`, remueve un manejador: + +```{lang: "text/html"} + + +``` + +{{index [function, "as value"]}} + +La función dada a `removeEventListener` tiene que ser el mismo valor de función +que se le dio a `addEventListener`. Entonces, para desregistrar un manejador, se +le tiene que dar un nombre a la función (`unaVez`, en el ejemplo) para poder +pasar el mismo valor de función a ambos métodos. + +## Objetos de evento + +{{index "button property", "event handling"}} + +Aunque lo hemos ignorado hasta ahora, las funciones del manejador de eventos +reciben un argumento: el _((objeto de evento))_. Este objeto contiene +información adicional acerca del evento. Por ejemplo, si queremos saber _cuál_ +((botón del mouse)) fue presionado, se puede ver la propiedad `button` del +objeto de evento. + +```{lang: "text/html"} + + +``` + +{{index "event type", "type property"}} + +La información almacenada en un objeto de evento es diferente por cada tipo de +evento. Se discutirán los distintos tipos de eventos más adelante en el +capítulo. La propiedad `type` del objeto siempre contiene una cadena que +identifica al evento (como `"click"` o `"mousedown"`) + +## Propagación + +{{index "event propagation", "parent node"}} + +{{indexsee bubbling, "event propagation"}} + +{{indexsee propagation, "event propagation"}} + +Para la mayoría de tipos de eventos, los manejadores registrados en nodos con +hijos también recibirán los eventos que sucedan en los hijos. Si se hace clic a +un botón dentro de un párrafo, los manejadores de eventos del párrafo también +verán el evento clic. + +{{index "event handling"}} + +Pero si tanto el párrafo como el botón tienen un manejador, el manejador más +específico—el del botón—es el primero en lanzarse. Se dice que el evento se +_propaga_ hacia afuera, desde el nodo donde este sucedió hasta el nodo padre del +nodo y hasta la raíz del documento. Finalmente, después de que todos los +manejadores registrados en un nodo específico hayan tenido su turno, los +manejadores registrados en general ((ventana)) tienen la oportunidad de +responder al evento. + +{{index "stopPropagation method", "click event"}} + +En cualquier momento, un manejador de eventos puede llamar al método +`stopPropagation` en el objeto de evento para evitar que los manejadores que se +encuentran más arriba reciban el evento. Esto puede ser útil cuando, por +ejemplo, si tienes un botón dentro de otro elemento en el que se puede hacer clic +y que no se quiere que los clics sobre el botón activen el comportamiento de +clic del elemento exterior. + +{{index "mousedown event", "pointer event"}} + +El siguiente ejemplo registra manejadores `"mousedown"` tanto en un botón como +el párrafo que lo rodea. Cuando se hace clic con el botón derecho del mouse, el +manejador del botón llama a `stopPropagation`, lo que evitará que se ejecute el +manejador del párrafo. Cuando se hace clic en el botón con otro ((botón del +mouse)), ambos manejadores se ejecutarán. + +```{lang: "text/html"} +Un párrafo con un .
+ +``` + +{{index "event propagation", "target property"}} + +La mayoría de objetos de eventos tienen una propiedad `tarjet` que se refiere al +nodo donde se originaron. Se puede usar esta propiedad para asegurar de que no +se está manejando accidentalmente algo que se propagó desde un nodo que no se +desea manejar. + +También es posible utilizar la propiedad `target` para lanzar una red amplia para +un evento específico. Por ejemplo, si tienes un nodo que contiene una gran +cantidad de botones, puede ser más conveniente el registrar un manejador en un +solo clic en el nodo externo y hacer que use la propiedad `target` para +averiguar si se hizo clic en un botón, en lugar de registrar manejadores +individuales en todos los botones. + +```{lang: "text/html"} + + + + +``` + +## Acciones por defecto + +{{index scrolling, "default behavior", "event handling"}} + +La mayoría de eventos tienen una acción por defecto asociada a ellos. Si haces +clic en un ((enlace)), se te dirigirá al destino del enlace. Si presionas la +flecha hacia abajo, el navegador desplazará la página hacia abajo. Si das clic +derecho, se obtendrá un menú contextual. Y así. + +{{index "preventDefault method"}} + +Para la mayoría de los tipos de eventos, los manejadores de eventos de +JavaScript se llamarán _antes_ de que el comportamiento por defecto se produzca. +Si el manejador no quiere que suceda este comportamiento por defecto, +normalmente porque ya se ha encargado de manejar el evento, se puede llamar al +método `preventDefault` en el objeto de evento. + +{{index expectation}} + +Esto puede ser utilizado para implementar un atajo de ((teclado)) propio o +((menú contextual)). Esto también puede ser utilizado para interferir de forma +desagradable el comportamiento que los usuarios esperan. Por ejemplo, aquí hay +un enlace que no se puede seguir: + +```{lang: "text/html"} +MDN + +``` + +{{index usability}} + +Trata de no hacer tales cosas a menos que tengas una buena razón para hacerlo. +Será desagradable para las personas que usan tu página cuando el comportamiento +esperado no funcione. + +Dependiendo del navegador, algunos eventos no pueden ser interceptados en lo +absoluto. En Chrome, por ejemplo, el atajo de ((teclado)) para cerrar la pestaña +actual ([control]{keyname}-W o [command]{keyname}-W) no se puede manejar con +JavaScript. + +## Eventos de teclado + +{{index keyboard, "keydown event", "keyup event", "event handling"}} + +Cuando una tecla del teclado es presionado, el navegador lanza un evento +`"keydown"`. Cuando este es liberado, se obtiene un evento `"keyup"`. + +```{lang: "text/html", focus: true} +Esta página se pone violenta cuando se mantiene presionado la tecla V.
+ +``` + +{{index "repeating key"}} + +A pesar de su nombre, `"keydown"` se lanza no solamente cuando la tecla es +físicamente presionada. Cuando una tecla se presiona y se mantiene presionada, +el evento se lanza una vez más cada que la tecla se _repite_. Algunas veces se +debe tener cuidado con esto. Por ejemplo, si tienes un botón al DOM cuando el +botón es presionado y removido cuando la tecla es liberada, puedes agregar +accidentalmente cientos de botones cuando la tecla se mantiene presionada por +más tiempo. + +{{index "key property"}} + +El ejemplo analizó la propiedad `key` del objeto de evento para ver de qué tecla +se trata el evento. Esta propiedad contiene una cadena que, para la mayoría de +las teclas, corresponde a lo que escribiría al presionar esa tecla. Para teclas +especiales como [enter]{keyname}, este contiene una cadena que nombre la tecla +{`"Enter"`, en este caso}. Si mantienes presionado [shift]{keyname} mientras +que presionas una tecla, esto también puede influir en el nombre de la +tecla-`"v"` se convierte en `"V"` y `"1"` puede convertirse en `"!"`, es lo que +se produce al presionar [shift]{keyname}-1 en tu teclado. + +{{index "modifier key", "shift key", "control key", "alt key", "meta key", +"command key", "ctrlKey property", "shiftKey property", "altKey property", +"metaKey property"}} + +La teclas modificadoras como [shift]{keyname}, [control]{keyname}, +[alt]{keyname} y [meta]{keyname} ([command]{keyname} en Mac) generan eventos de +teclado justamente como las teclas normales. Pero cuando se busque combinaciones +de teclas, también se puede averiguar si estas teclas se mantienen presionadas +viendo las propiedades `shiftKey`, `ctrlKey`, `altKey` y `metaKey` de los +eventos de teclado y mouse. + +```{lang: "text/html", focus: true} +Presiona Control-Espacio para continuar.
+ +``` + +{{index "button (HTML tag)", "tabindex attribute", [DOM, events]}} + +El nodo DOM donde un evento de teclado se origina depende en el elemento que +tiene el ((foco)) cuando la tecla es presionada. La mayoría de los nodos no +pueden tener el foco a menos que se les de un atributo `tabindex`, pero +elementos como ((enlace))s, botones y campos de formularios sí pueden. +Volveremos a los ((campo))s de formularios en el capítulo [Chapter?](http#forms). +Cuando nadie en particular tiene el foco, `document.body` actua +como el nodo objetivo de los eventos de teclado. + +Cuando el usuario está escribiendo texto, usando los eventos de teclado para +averiguar qué se está escribiendo es problematico. Algunas plataformas, sobre +todo el ((teclado virtual)) en ((teléfono))s ((Android)), no lanzan eventos de +teclado. Pero incluso cuando se tiene un teclado antiguo, algunos tipos de +entradas de texto no coinciden con las pulsaciones de teclas de forma sencilla, +como el software _editor de métodos de entrada_ (((IME))) usado por personas +cuyos _guiones_ (_((script))_) no encajan en un teclado, donde se combinan +varias teclas para crear caracteres. + +Para notar cuando se escribió algo, los elementos en los que se puede escribir, +como las etiquetas `` y `