| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa |
| informatica:programacion:cursos:programacion_avanzada_javascript:eventos_navegadores [2024/10/11 15:44] – [Ejemplo: eventos para detectar la aparición y desaparición de elementos en la página] tempwin | informatica:programacion:cursos:programacion_avanzada_javascript:eventos_navegadores [2024/10/30 15:45] (actual) – [Prácticas propuestas para el módulo] tempwin |
|---|
| Hasta ahora hemos visto muchísimas cosas sobre JavaScript. Con los conocimientos adquiridos podemos **crear algoritmos, modificar los contenidos de una página, acceder a cualquier elemento**… Pero, todo eso nos sirve de poco si no podemos **hacerlo en el momento preciso**. Al final, JavaScript dentro de una página web se utiliza casi siempre para interactuar con el usuario. Y precisamente eso es lo que nos proporcionan los eventos. Con lo que veremos en este módulo estaremos cerrando el círculo y llegando a lo que más nos interesa: los usuarios. | Hasta ahora hemos visto muchísimas cosas sobre JavaScript. Con los conocimientos adquiridos podemos **crear algoritmos, modificar los contenidos de una página, acceder a cualquier elemento**… Pero, todo eso nos sirve de poco si no podemos **hacerlo en el momento preciso**. Al final, JavaScript dentro de una página web se utiliza casi siempre para interactuar con el usuario. Y precisamente eso es lo que nos proporcionan los eventos. Con lo que veremos en este módulo estaremos cerrando el círculo y llegando a lo que más nos interesa: los usuarios. |
| |
| <WRAP center round todo 60%> | {{ :informatica:programacion:cursos:programacion_avanzada_javascript:07-bang-gun.jpg |}} |
| pistola | |
| </WRAP> | |
| |
| Un documento HTML consta de un amplio conjunto de elementos, como párrafos, enlaces, formularios, tablas o divs. Una vez que se ha cargado una página web comienza la actividad por parte **del usuario**, que realiza todo tipo de acciones sobre dichos elementos: **pulsarlos**, **seleccionarlos**, **pasarles por encima**... Estas acciones son **eventos: acciones que ocurren en la página**, y el navegador los transmite a nuestro código por si queremos gestionarlos y responder a ellos con alguna acción. Casi todos los eventos que se generan en una página son provocados por la acción del usuario, con la notable excepción de los eventos que se generan tras la carga de la página y de sus elementos constituyentes. | Un documento HTML consta de un amplio conjunto de elementos, como párrafos, enlaces, formularios, tablas o divs. Una vez que se ha cargado una página web comienza la actividad por parte **del usuario**, que realiza todo tipo de acciones sobre dichos elementos: **pulsarlos**, **seleccionarlos**, **pasarles por encima**... Estas acciones son **eventos: acciones que ocurren en la página**, y el navegador los transmite a nuestro código por si queremos gestionarlos y responder a ellos con alguna acción. Casi todos los eventos que se generan en una página son provocados por la acción del usuario, con la notable excepción de los eventos que se generan tras la carga de la página y de sus elementos constituyentes. |
| Como vemos es un simple ''%%<div>%%'' que contiene a un enlace. Dado que todos los elementos del DOM forman una jerarquía, es decir, están contenidos unos en otros, **cuando generamos un evento** en uno de ellos (por ejemplo, pulsar el enlace), **el evento se propaga por toda la jerarquía del DOM** como si fueran las ondas que deja una piedra al tirarla a un estanque: | Como vemos es un simple ''%%<div>%%'' que contiene a un enlace. Dado que todos los elementos del DOM forman una jerarquía, es decir, están contenidos unos en otros, **cuando generamos un evento** en uno de ellos (por ejemplo, pulsar el enlace), **el evento se propaga por toda la jerarquía del DOM** como si fueran las ondas que deja una piedra al tirarla a un estanque: |
| |
| <WRAP center round todo 60%> | {{ :informatica:programacion:cursos:programacion_avanzada_javascript:07-flujo-eventos-dom.png |}} |
| Flujo-Eventos | |
| </WRAP> | |
| |
| Si pulsamos en el enlace, al estar contenido dentro del ''div'' es como si pulsásemos también en el ''div'', y a su vez en el cuerpo de la página, el elemento ''<html>'' (''documentElement'') y finalmente en el propio documento que siempre lo contiene a todo. | Si pulsamos en el enlace, al estar contenido dentro del ''div'' es como si pulsásemos también en el ''div'', y a su vez en el cuerpo de la página, el elemento ''<html>'' (''documentElement'') y finalmente en el propio documento que siempre lo contiene a todo. |
| Lo cierto es que **es algo más complicado que esto**, y un poco antiintuitivo a la vez. En realidad, cuando se produce un evento en un elemento de la página **se producen tres fases diferenciadas a la hora de gestionar y lanzar el evento**. Estas tres fases se ilustran bien en la figura siguiente: | Lo cierto es que **es algo más complicado que esto**, y un poco antiintuitivo a la vez. En realidad, cuando se produce un evento en un elemento de la página **se producen tres fases diferenciadas a la hora de gestionar y lanzar el evento**. Estas tres fases se ilustran bien en la figura siguiente: |
| |
| <WRAP center round todo 60%> | {{ :informatica:programacion:cursos:programacion_avanzada_javascript:07-fase-eventos-dom.png |}} |
| fase-eventos | |
| </WRAP> | |
| |
| * **Fase de captura**: en esta fase se comienza a detectar el evento desde el elemento más alto en la jerarquía (el documento), hacia abajo, hasta llegar justo antes del elemento donde realmente se ha generado el evento. En nuestro símil con el estanque es como si las ondas comenzasen a formarse desde fuera hacia adentro, cerrándose sobre el punto de impacto de la piedra en lugar de al contrario. Bastante poco intuitivo, su origen se remonta a la guerra de los navegadores, como veremos enseguida. | * **Fase de captura**: en esta fase se comienza a detectar el evento desde el elemento más alto en la jerarquía (el documento), hacia abajo, hasta llegar justo antes del elemento donde realmente se ha generado el evento. En nuestro símil con el estanque es como si las ondas comenzasen a formarse desde fuera hacia adentro, cerrándose sobre el punto de impacto de la piedra en lugar de al contrario. Bastante poco intuitivo, su origen se remonta a la guerra de los navegadores, como veremos enseguida. |
| | ''stopPropagation()'' | función | Si la propiedad ''bubbles'' es ''true'', entonces llamando a este evento se cancela la transmisión a la jerarquía del DOM, es decir, no sigue la fase de captura ni el "burbujeo" de la fase de convergencia y no se detecta en los siguientes nodos del DOM. Por ejemplo, si capturamos el clic en un enlace y en su div contenedor, pero no queremos capturarlo en ambos a la vez, podemos cancelar la propagación en el manejador del enlace y así no se detectará también en el ''div''. | | | ''stopPropagation()'' | función | Si la propiedad ''bubbles'' es ''true'', entonces llamando a este evento se cancela la transmisión a la jerarquía del DOM, es decir, no sigue la fase de captura ni el "burbujeo" de la fase de convergencia y no se detecta en los siguientes nodos del DOM. Por ejemplo, si capturamos el clic en un enlace y en su div contenedor, pero no queremos capturarlo en ambos a la vez, podemos cancelar la propagación en el manejador del enlace y así no se detectará también en el ''div''. | |
| | ''stopImmediatePropagation()'' | function | Como sabemos, cuando un mismo elemento tiene más de un manejador asociado para el mismo evento, éstos se ejecutan en el orden en el que fueron especificados. Si desde alguno de ellos llamamos a este método, los demás manejadores posteriores que no se hayan ejecutado todavía, no se ejecutarán, pues cancela toda llamada posterior. | | | ''stopImmediatePropagation()'' | function | Como sabemos, cuando un mismo elemento tiene más de un manejador asociado para el mismo evento, éstos se ejecutan en el orden en el que fueron especificados. Si desde alguno de ellos llamamos a este método, los demás manejadores posteriores que no se hayan ejecutado todavía, no se ejecutarán, pues cancela toda llamada posterior. | |
| | ''target'' | elemento | El elemento de la página que realmente ha producido el evento. En nuestro ejemplo, si pulsamos el enlace pero capturamos en el div, esta propiedad apuntaría al enlace que es el que ha provocado el evento. Es una de las propiedades más importantes de un evento. | | | ''target'' | elemento | El elemento de la página que realmente ha producido el evento. En nuestro ejemplo, si pulsamos el enlace pero capturamos en el ''div'', esta propiedad apuntaría al enlace que es el que ha provocado el evento. Es una de las propiedades más importantes de un evento. | |
| | ''type'' | texto | El tipo de evento que se ha lanzado. Es la misma cadena que usamos para declarar el evento: "click", "load", focus"… Nos sirve para distinguir el tipo de evento cuando varios comparten el mismo manejador. | | | ''type'' | texto | El tipo de evento que se ha lanzado. Es la misma cadena que usamos para declarar el evento: "click", "load", focus"… Nos sirve para distinguir el tipo de evento cuando varios comparten el mismo manejador. | |
| | ''view'' | objeto | Es un objeto de tipo ''AbstractView'' definido por la W3C y que es de donde derivan todas las posibles vistas del documento (se refiere a documentos XML genéricos, que pueden visualizarse de modo diferente según su XSLT). En este caso se refiere a la vista actual del documento, lo cual implica la página actual. Es decir, en la práctica con ella lo que obtenemos es una referencia al objeto ''window'' actual. Más información [[http://www.w3.org/TR/DOM-Level-2-Views/views.html|aquí]]. Solo se ofrece en eventos de interfaz de usuario, o sea, de tipo UIEvent. | | | ''view'' | objeto | Es un objeto de tipo ''AbstractView'' definido por la W3C y que es de donde derivan todas las posibles vistas del documento (se refiere a documentos XML genéricos, que pueden visualizarse de modo diferente según su XSLT). En este caso se refiere a la vista actual del documento, lo cual implica la página actual. Es decir, en la práctica con ella lo que obtenemos es una referencia al objeto ''window'' actual. Más información [[http://www.w3.org/TR/DOM-Level-2-Views/views.html|aquí]]. Solo se ofrece en eventos de interfaz de usuario, o sea, de tipo UIEvent. | |
| Además, otra página interesante para tener como referencia, aunque ya tiene unos años, es [[http://help.dottoro.com/|Dottoro]]. Esta página nos permite localizar cualquier elemento, bien mediante la búsqueda integrada, bien recurriendo al índice, y nos proporciona información detallada y ejemplos de cada uno, indicando además los atributos y propiedades de cada uno, los eventos que soporta... y además muestra mediante iconos el soporte que ofrece cada navegador para el miembro consultado (si bien no la actualizan a menudo y puede que haya algún dato erróneo, pero aún así merece mucho la pena como primer filtro): | Además, otra página interesante para tener como referencia, aunque ya tiene unos años, es [[http://help.dottoro.com/|Dottoro]]. Esta página nos permite localizar cualquier elemento, bien mediante la búsqueda integrada, bien recurriendo al índice, y nos proporciona información detallada y ejemplos de cada uno, indicando además los atributos y propiedades de cada uno, los eventos que soporta... y además muestra mediante iconos el soporte que ofrece cada navegador para el miembro consultado (si bien no la actualizan a menudo y puede que haya algún dato erróneo, pero aún así merece mucho la pena como primer filtro): |
| |
| <WRAP center round todo 60%> | {{ :informatica:programacion:cursos:programacion_avanzada_javascript:07-dottoro.gif |}} |
| Dottoro - Buscar elementos en índice | |
| </WRAP> | |
| |
| Las principales herramientas de escritura de código ofrecen también ayuda contextual mientras escribimos, por lo que nos facilitarán mucho asignar eventos, propiedades y saber qué parámetros utilizar. Aprovecha su potencia. | Las principales herramientas de escritura de código ofrecen también ayuda contextual mientras escribimos, por lo que nos facilitarán mucho asignar eventos, propiedades y saber qué parámetros utilizar. Aprovecha su potencia. |
| ==== Eventos de formularios y controles de entrada ==== | ==== Eventos de formularios y controles de entrada ==== |
| |
| <WRAP center round todo 60%> | {{ :informatica:programacion:cursos:programacion_avanzada_javascript:07-formulario-icono.jpg |}} |
| formulario | |
| </WRAP> | |
| |
| Para el caso del formulario ya hemos visto el más importante de ellos, ''submit'', que salta cuando se intenta enviar el formulario al servidor (y que ya hemos visto en algún vídeo práctico). También dispone de otro relacionado llamado ''reset'' que se notifica cuando se inicializa el formulario pulsando sobre un botón de tipo ''reset''. | Para el caso del formulario ya hemos visto el más importante de ellos, ''submit'', que salta cuando se intenta enviar el formulario al servidor (y que ya hemos visto en algún vídeo práctico). También dispone de otro relacionado llamado ''reset'' que se notifica cuando se inicializa el formulario pulsando sobre un botón de tipo ''reset''. |
| ===== Prácticas propuestas para el módulo ===== | ===== Prácticas propuestas para el módulo ===== |
| |
| | ste módulo se ha centrado en las diversas maneras que tenemos desde JavaScript para gestionar interacciones de los usuarios con el navegador. Esto nos permitirá crear todo tipo de aplicaciones prácticas basadas en lo que hemos aprendido antes sobre el lenguaje. En definitiva, llevar lo aprendido más cerca de la práctica real. Aunque los ejemplos que hemos realizado son intencionadamente sencillos para no desenfocarnos de lo que se pretendía enseñar, ahora tienes prácticamente todas las herramientas que necesitas para crear cualquier aplicación web que puedas necesitar. Es cuestión de juntar las piezas. |
| | |
| | Para reforzar los conceptos de este módulo y practicar un poco más te propongo los siguientes ejercicios: |
| | |
| | * En el ejemplo de la etiqueta que persigue al ratón mostrando sus coordenadas, haz que cuando esté cerca del borde derecho de la página, lo suficiente como para que tape todo o parte de la etiqueta, ésta se muestre hacia el otro lado, de modo que siempre quede dentro del área visible de la página y no salgan barras de scroll. |
| | * Añade a una página cualquiera (que tenga al menos varios elementos ''div'', ''input'' de tipo ''text'' y párrafos de texto) la capacidad de detectar la pulsación de determinadas teclas que actuarán como atajos o teclas rápidas para un hipotético programa que tienes en esta página. Por ejemplo, que se detecte la pulsación de la tecla F1 y que esto haga que se muestre un ''div'' con ayuda, o la combinación de teclas <kbd>CTRL</kbd> + <kbd>T</kbd> para hacer cualquier otra cosa (en nuestro ejemplo, simplemente mostrará un ''alert''). Debes asegurarte de un par de cosas: |
| | * Que las teclas rápidas se detecten pulsándolas cuando el foco esté en cualquier parte de la página. |
| | * Que si el foco está en algún elemento que acepte entrada de texto (un ''input'' de tipo ''text'', por ejemplo) que no tengan efecto dichas teclas. |
| | |
| | Una pregunta importante: ¿Funcionan todas las combinaciones? Por ejemplo, realmente eres capaz de detectar y responder a <kbd>CTRL</kbd> + <kbd>T</kbd> siempre. Prueba con <kbd>CTRL</kbd> + <kbd>Q</kbd> a ver si pasa lo mismo... |
| | |
| | * En el ejemplo de control básico de un marcianito que hemos desarrollado en este módulo, hemos visto que hay un pequeño problema: la imposibilidad de detectar la pulsación de dos teclas a la vez. El efecto de esto es que si queremos que el marcianito se mueva en diagonal, lo conseguiremos pero veremos que lo hace "a saltos" como si pulsásemos las dos flechas (por ejemplo abajo y a la izquierda) de manera alternativa, y no a la vez. |
| | |
| | Además, si esto fuese un juego real existiría otro problema más sutil pero importante: los movimientos no estarían sincronizados con el resto de movimientos de otros elementos del juego. Normalmente los juegos tienen un bucle de ejecución y redibujan todos los elementos del mismo a la vez a intervalos regulares (por ejemplo 40 veces por segundo). Así, cada 25 milisegundos se pinta cada marcianito, asteroide y demás elementos móviles autónomos reflejando su posición. Para los elementos que controla el usuario, si usamos los eventos del teclado, se moverían en un bucle diferente del resto del juego, y no necesariamente coincidente con los intervalos de refresco de los demás elementos. De entrada puede que no lo notásemos, pero generaría problemas y parpadeos indeseados. |
| | |
| | Entonces, ¿cómo se solucionan estos problemas si quisiésemos crear un juego con HTML y JavaScript? |
| | |
| | La solución consiste en detectar las pulsaciones del teclado, pero **en lugar de procesarlas lo que hacemos es anotarlas en algún otro lugar (en un buffer)**. Es decir, en algún objeto auxiliar se van anotando cada una de las pulsaciones relevantes del teclado que hace el usuario (evento ''keydown''), así como cuando deja de pulsar las teclas (''keyup''), pero no hacemos nada más que anotarlas. Es luego, en el bucle de redibujado de los elementos, en donde comprobamos ese buffer de pulsaciones y actuamos en consecuencia moviendo los objetos afectados. Con esto se consiguen procesar varias teclas a la vez (o tener la sensación de ello, más bien) y se consiguen movimientos más fluidos. |
| | |
| | Otra opción, incluso mejor, es anotar la posición de cada marcianito, modificándola en los eventos que se van produciendo, pero sin dibujarla, es decir, sin cambiar la posición real de los elementos móviles. Luego en el bucle de dibujado (un ''setInterval'') es cuando realmente se dibujan los objetos, pero no en el momento mismo de detectar la pulsación de la tecla. |
| | |
| | Como ejercicio especial (no lo hagas si no te ves con fuerzas) te propongo cambiar el código de la página del marcianito por completo y hacerlo siguiendo la técnica que te acabo de explicar. Además deberías crear una clase para almacenar la información del marcianito y así no tener que almacenar su posición (y otros datos que tuviese) en una estructura aparte para poder redibujarla. |
| | |
| | <WRAP center round tip 60%> |
| | Nota: De todos modos si estás pensando en hacer un juego en JavaScript es mejor usar alguna biblioteca especializada que ya nos da estas cosas y muchas otras ya hechas (aunque conviene conocer sus fundamentos). Hay infinidad de ellas en el mercado, casi todas gratuitas y Open Source. Por ejemplo [[http://craftyjs.com/|Crafty]], [[http://darlingjs.github.io/|DarlingJS]], [[http://www.limejs.com/|LimeJS]] o [[http://impactjs.com/|ImpactJS]]. En cualquier caso, ten en cuenta que la creación de juegos es un mundo en sí mismo y necesitarás aprender muchas técnicas específicas más allá de dominar los fundamentos de JavaScript. |
| | </WRAP> |
| | |
| | En los siguientes módulos del curso vamos a estudiar algunas cuestiones adicionales útiles, pero con lo explicado hasta ahora ya conoces todo lo necesario para poder programar con éxito con JavaScript en cualquier entorno, pero especialmente aplicaciones web de lado cliente basadas en navegadores. Solo es cuestión de práctica y plantearse cosas nuevas con las que experimentar y aprender. En la programación para navegadores es muy importante tener a mano una buena documentación para consulta (como [[http://help.dottoro.com/ljsdaoxj.php|Dottoro Web Reference]], la [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference|Mozilla Developer Network]] o la [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference|documentación de Microsoft]] al respecto), probar bien los desarrollos en todos los navegadores posibles y, sobre todo, adquirir experiencia poco a poco haciendo pequeños desarrollos y subiendo paulatinamente la dificultad. |
| | |
| | ¡Ah! y muy importante: a quien tiene un martillo todo le parecen clavos, así que trata de acordarte de que muchas de las cosas que se te planteen quizá las puedas conseguir sin necesidad de escribir código (solo con CSS, por ejemplo). Y lo mismo te digo si vienes de jQuery: multitud de cosas sencillas que se tienden a hacer con jQuery por inercia, es posible hacerlas directamente en JavaScript con el mismo esfuerzo, consiguiendo páginas más ligeras y rápidas. ¡No te olvides! |
| | |
| | ===== Recursos ===== |
| | |
| | * [[https://www.codeproject.com/Articles/878436/Lets-Write-Unobtrusive-JavaScript|Let's Write Unobtrusive JavaScript]] |