informatica:programacion:cursos:programacion_avanzada_javascript:eventos_navegadores
Diferencias
Muestra las diferencias entre dos versiones de la página.
| 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:01] – [DEMO: Uso práctico de la biblioteca de validación] tempwin | informatica:programacion:cursos:programacion_avanzada_javascript:eventos_navegadores [2024/10/30 15:45] (actual) – [Prácticas propuestas para el módulo] tempwin | ||
|---|---|---|---|
| Línea 7: | Línea 7: | ||
| 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%> | + | {{ : |
| - | pistola | + | |
| - | </ | + | |
| Un documento HTML consta de un amplio conjunto de elementos, como párrafos, enlaces, formularios, | Un documento HTML consta de un amplio conjunto de elementos, como párrafos, enlaces, formularios, | ||
| Línea 414: | Línea 412: | ||
| Como vemos es un simple '' | Como vemos es un simple '' | ||
| - | <WRAP center round todo 60%> | + | {{ : |
| - | Flujo-Eventos | + | |
| - | </ | + | |
| Si pulsamos en el enlace, al estar contenido dentro del '' | Si pulsamos en el enlace, al estar contenido dentro del '' | ||
| Línea 428: | Línea 424: | ||
| 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%> | + | {{ : |
| - | fase-eventos | + | |
| - | </ | + | |
| * **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, | * **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, | ||
| Línea 716: | Línea 710: | ||
| | '' | | '' | ||
| | '' | | '' | ||
| - | | '' | + | | '' |
| | '' | | '' | ||
| | '' | | '' | ||
| Línea 888: | Línea 882: | ||
| Además, otra página interesante para tener como referencia, aunque ya tiene unos años, es [[http:// | Además, otra página interesante para tener como referencia, aunque ya tiene unos años, es [[http:// | ||
| - | <WRAP center round todo 60%> | + | {{ : |
| - | Dottoro | + | |
| - | </ | + | |
| 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. | ||
| Línea 1651: | Línea 1643: | ||
| ==== Eventos de formularios y controles de entrada ==== | ==== Eventos de formularios y controles de entrada ==== | ||
| - | <WRAP center round todo 60%> | + | {{ : |
| - | formulario | + | |
| - | </ | + | |
| Para el caso del formulario ya hemos visto el más importante de ellos, '' | Para el caso del formulario ya hemos visto el más importante de ellos, '' | ||
| Línea 1768: | Línea 1758: | ||
| < | < | ||
| < | < | ||
| - | <meta charset=" | + | < |
| - | <meta name=" | + | <style type=" |
| - | | + | body |
| - | <style type=" | + | { |
| - | body | + | font-family:" |
| - | { | + | font-size: |
| - | font-family:" | + | } |
| - | font-size: | + | .cabecera |
| - | } | + | { |
| - | .cabecera | + | text-align: |
| - | { | + | font-weight: |
| - | text-align: | + | font-size: |
| - | font-weight: | + | color: |
| - | font-size: | + | } |
| - | color: | + | .Encabezado |
| - | } | + | { |
| - | .Encabezado | + | font-weight: |
| - | { | + | font-size: |
| - | font-weight: | + | } |
| - | font-size: | + | .Obligatorio |
| - | } | + | { |
| - | .Obligatorio | + | color: |
| - | { | + | } |
| - | color: | + | } |
| - | } | + | </ |
| - | #formulario { | + | <link rel=" |
| - | float: | + | <script type=" |
| - | margin-right: | + | <script type=" |
| - | } | + | function miValidacionEspecial(marcadoSiONo) { |
| - | + | marcadoSiONo = new Boolean(marcadoSiONo); | |
| - | #errores { | + | return (this.checked == marcadoSiONo); |
| - | float: | + | } |
| - | width: | + | </ |
| - | border: 1px dashed red; | + | |
| - | padding: | + | |
| - | visibility: | + | |
| - | } | + | |
| - | </ | + | |
| - | + | ||
| - | | + | |
| - | <script type=" | + | |
| - | <script type=" | + | |
| - | function miValidacionEspecial(marcadoSiONo) { | + | |
| - | marcadoSiONo = new Boolean(marcadoSiONo); | + | |
| - | return (this.checked == marcadoSiONo); | + | |
| - | } | + | |
| - | </ | + | |
| </ | </ | ||
| < | < | ||
| - | | + | <h1 class=" |
| - | | + | <form name=" |
| - | <label for=" | + | <label for=" |
| - | | + | |
| - | | + | </ |
| - | | + | < |
| - | | + | <label for=" |
| - | <input type=" | + | <input type=" |
| - | </ | + | </ |
| - | < | + | < |
| - | <label for=" | + | <label for=" |
| - | | + | |
| - | </ | + | </ |
| - | < | + | <label for=" |
| <input type=" | <input type=" | ||
| </ | </ | ||
| Línea 1839: | Línea 1815: | ||
| <label for=" | <label for=" | ||
| Edad (18-100): | Edad (18-100): | ||
| - | <input type=" | + | <input type=" |
| </ | </ | ||
| <br /> | <br /> | ||
| - | <label for=" | + | <label for=" |
| <input type=" | <input type=" | ||
| </ | </ | ||
| <br/> | <br/> | ||
| - | <label for=" | + | <label for=" |
| - | <input type=" | + | <input type=" |
| </ | </ | ||
| <br/> | <br/> | ||
| - | <p> | + | <p> |
| - | <input type=" | + | <input type=" |
| <label for=" | <label for=" | ||
| </p> | </p> | ||
| - | <p> | + | < |
| <input type=" | <input type=" | ||
| <input type=" | <input type=" | ||
| Línea 1861: | Línea 1837: | ||
| </ | </ | ||
| </ | </ | ||
| + | |||
| </ | </ | ||
| Vemos que se han añadido en los campos una serie de atributos que comienzan por '' | Vemos que se han añadido en los campos una serie de atributos que comienzan por '' | ||
| - | Y este es el contenido del fichero '' | + | En este otro formulario HTML la validación se realiza al perder el foco en un campo: |
| + | |||
| + | <code html> | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <style type=" | ||
| + | body | ||
| + | { | ||
| + | font-family:" | ||
| + | font-size: | ||
| + | } | ||
| + | .cabecera | ||
| + | { | ||
| + | text-align: | ||
| + | font-weight: | ||
| + | font-size: | ||
| + | color: | ||
| + | } | ||
| + | .Encabezado | ||
| + | { | ||
| + | font-weight: | ||
| + | font-size: | ||
| + | } | ||
| + | .Obligatorio | ||
| + | { | ||
| + | color: | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | <link rel=" | ||
| + | <script type=" | ||
| + | <script type=" | ||
| + | function miValidacionEspecial(marcadoSiONo) { | ||
| + | marcadoSiONo = new Boolean(marcadoSiONo); | ||
| + | return (this.checked == marcadoSiONo); | ||
| + | } | ||
| + | </ | ||
| + | </ | ||
| + | < | ||
| + | |||
| + | <h1 class=" | ||
| + | |||
| + | <form name=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | < | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | < | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | < | ||
| + | <label for=" | ||
| + | Edad (18-100): | ||
| + | <input type=" | ||
| + | </ | ||
| + | <br /> | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | < | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | < | ||
| + | <p> | ||
| + | <input type=" | ||
| + | <label for=" | ||
| + | </ | ||
| + | <p> | ||
| + | <input type=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | ==== DEMO: Explicación del código de la biblioteca de validación ==== | ||
| + | |||
| + | Este es el contenido del fichero '' | ||
| <code javascript> | <code javascript> | ||
| Línea 1876: | Línea 1941: | ||
| // Licencia Creative Commons - CC BY (http:// | // Licencia Creative Commons - CC BY (http:// | ||
| /////////////////////////////////////////////////////////////////////////////////// | /////////////////////////////////////////////////////////////////////////////////// | ||
| - | (function () { | + | (function() { |
| - | //Prefijo de los atributos de validación | + | |
| - | var PREFIJO = "data-val-"; | + | var PREFIJO = " |
| - | //Función que valida todos los campos de un formulario dado, verificando las reglas propias creadas con atributos | + | |
| - | //frm: el formulario a validar | + | //frm: el formulario a validar |
| - | // | + | // |
| - | function _validaFormulario(frm, | + | function _validaFormulario(frm, |
| - | //Me aseguro de que el segundo parámetro tiene un booleano válido | + | //Me aseguro de que el segundo parámetro tiene un booleano válido |
| - | mostrarMensajes = !!mostrarMensajes; | + | mostrarMensajes = !!mostrarMensajes; |
| - | var res = true; | + | |
| - | for (var i = 0; i < frm.elements.length; | + | for (var i = 0; i < frm.elements.length; |
| - | var campo = frm.elements[i]; | + | var campo = frm.elements[i]; |
| - | if (!_validaCampo(campo, | + | if (!_validaCampo(campo, |
| - | //Si la validación del campo ha fallado | + | //Si la validación del campo ha fallado |
| - | // | + | // |
| - | res = false; | + | res = false; |
| - | } | + | } |
| - | } | + | } |
| - | //Añadimos o cambiamos la propiedad " | + | //Añadimos o cambiamos la propiedad " |
| - | frm.esValido = res; | + | frm.esValido = res; |
| - | return res; | + | return res; |
| - | }; | + | }; |
| - | //Función que valida un campo determinado que se le pase como parámetro | + | |
| - | function _validaCampo(campo, | + | function _validaCampo(campo, |
| - | var atributos = campo.attributes; | + | // La propiedad estándar del DOM ' |
| - | | + | // los atributos que tiene un elemento HTML, tanto los estándar |
| - | for (var i = 0; i < atributos.length; | + | // como los que nosotros añadamos (como los que empiezan por ' |
| - | var nombre = atributos[i].name.toLowerCase(); | + | |
| - | if (nombre.indexOf(PREFIJO) == 0) //si empieza por el prefijo de los atributos de validación, | + | //Recorro los atributos del campo y verifico los que empiecen por " |
| - | if (!_verificaAtributo(campo, | + | for (var i = 0; i < atributos.length; |
| - | //Si así se ha indicado, mostramos el mensaje asociado | + | var nombre = atributos[i].name.toLowerCase(); |
| - | if (mostrarMensaje) _muestraMensajeValidacion(campo); | + | if (nombre.indexOf(PREFIJO) == 0) //si empieza por el prefijo de los atributos de validación, |
| - | //Marcamos el campo como no válido con la propiedad " | + | if (!_verificaAtributo(campo, |
| - | campo.esValido = false; | + | //Si así se ha indicado, mostramos el mensaje asociado |
| - | return false; //Si no tiene éxito la validación, | + | if (mostrarMensaje) _muestraMensajeValidacion(campo); |
| - | //Salimos del bucle: ya no seguimos validando el resto de atributos del mismo campo (con que haya uno no válido, se muestra el mensaje) | + | //Marcamos el campo como no válido con la propiedad " |
| - | } else { | + | campo.esValido = false; |
| - | //Si se ha validado correctamente quitamos el mensaje de validación en caso de estar habilitado | + | return false; //Si no tiene éxito la validación, |
| - | if (mostrarMensaje) _quitaMensajeDeValidacion(campo); | + | //Salimos del bucle: ya no seguimos validando el resto de atributos del mismo campo (con que haya uno no válido, se muestra el mensaje) |
| - | //Marcamos el campo como válido con la propiedad " | + | } else { |
| - | campo.esValido = true; | + | //Si se ha validado correctamente quitamos el mensaje de validación en caso de estar habilitado |
| - | } | + | if (mostrarMensaje) _quitaMensajeDeValidacion(campo); |
| - | } | + | //Marcamos el campo como válido con la propiedad " |
| - | campo.esValido = true; | + | campo.esValido = true; |
| - | return true; | + | } |
| - | }; | + | } |
| + | campo.esValido = true; | ||
| + | return true; | ||
| + | }; | ||
| - | //Verifica un determinado atributo de validación para un determinado campo | + | |
| - | function _verificaAtributo(campo, | + | function _verificaAtributo(campo, |
| - | //Llamo a la función apropiada en función del atributo que sea. Esto permite ampliar fácilmente los atributos de validación | + | //Llamo a la función apropiada en función del atributo que sea. Esto permite ampliar fácilmente los atributos de validación |
| - | //No depende de mayúscula ni minúscula | + | //No depende de mayúscula ni minúscula |
| - | switch (atributo.name.toLowerCase()) { | + | switch (atributo.name.toLowerCase()) { |
| - | // | + | // |
| - | | + | case "val-obligatorio": |
| - | return | + | return |
| - | break; | + | break; |
| - | | + | case "val-longmin": |
| - | return _longMinima(campo, | + | return _longMinima(campo, |
| - | break; | + | break; |
| - | | + | case "val-longmax": |
| - | return _longMaxima(campo, | + | return _longMaxima(campo, |
| - | break; | + | break; |
| - | | + | case "val-num": |
| - | return _esNumero(campo); | + | return _esNumero(campo); |
| - | break; | + | break; |
| - | | + | case "val-entero": |
| - | return _esNumeroEntero(campo); | + | return _esNumeroEntero(campo); |
| - | break; | + | break; |
| - | | + | case "val-fecha": |
| - | return _esFecha(campo); | + | return _esFecha(campo); |
| - | break; | + | break; |
| - | | + | case "val-rango": |
| - | return _verificarRango(campo, | + | return _verificarRango(campo, |
| - | break; | + | break; |
| - | | + | case "val-rangofechas": |
| - | return _verificarRangoFechas(campo, | + | return _verificarRangoFechas(campo, |
| - | break; | + | break; |
| - | | + | case "val-email": |
| - | return _verificarEmail(campo); | + | return _verificarEmail(campo); |
| - | break; | + | break; |
| - | | + | case "val-custom": |
| - | return _llamarFuncionPersonalizada(campo, | + | return _llamarFuncionPersonalizada(campo, |
| - | break; | + | break; |
| - | default: | + | default: |
| - | | + | // |
| - | return true; | + | return true; |
| - | } | + | } |
| - | }; | + | }; |
| - | ////////////////////////////////////////////////////////////////////////////////////////////////////// | + | |
| - | // FUNCIONES DE VALIDACIÓN | + | // FUNCIONES DE VALIDACIÓN |
| - | // Simplemente validan los campos y devuelve true o false según se haya pasado o no la validación. | + | // Simplemente validan los campos y devuelve true o false según se haya pasado o no la validación. |
| - | // No interfieren con la interfaz de usuario | + | // No interfieren con la interfaz de usuario |
| - | ////////////////////////////////////////////////////////////////////////////////////////////////////// | + | ////////////////////////////////////////////////////////////////////////////////////////////////////// |
| - | //Funcion que verifica si un campo del formulario es obligatorio o no | + | |
| - | function | + | function |
| - | var valor = campo.value.trim(); | + | var valor = campo.value.trim(); |
| - | return (valor | + | return (valor |
| - | }; | + | }; |
| - | //Función que verifica que el campo tenga un valor entero con una longitud mínima | + | |
| - | function _longMinima(campo, | + | function _longMinima(campo, |
| - | | + | valor = parseInt(valor, |
| - | valor = parseInt(valor, | + | return (!isNaN(valor) && campo.value.length >= valor); |
| - | return (!isNaN(valor) && campo.value.length >= valor); | + | }; |
| - | }; | + | |
| - | //Función que verifica que el campo tenga un valor entero que no supere una longitud máxima | + | |
| - | function _longMaxima(campo, | + | function _longMaxima(campo, |
| - | valor = parseInt(valor, | + | valor = parseInt(valor, |
| - | return (!isNaN(valor) && campo.value.length <= valor); | + | return (!isNaN(valor) && campo.value.length <= valor); |
| - | }; | + | }; |
| - | //Función que verifica si el valor de un campo es numérico o no | + | |
| - | function _esNumero(campo) { | + | function _esNumero(campo) { |
| - | if (_estaVacio(campo)) return | + | var valor = parseFloat(campo.value); |
| + | if (isNaN(valor)) return | ||
| + | //Si es un número aún tiene que coincidir con el valor del campo (la covnersión hace caso omiso de los valores no válidos, por lo que no llega con ver si es numérico). | ||
| + | return (valor.toString() == campo.value.trim()); | ||
| + | }; | ||
| - | var valor = parseFloat(campo.value); | + | //Función que verifica si el valor de un campo es un número entero o no |
| - | if (isNaN(valor)) return false; //Si no es un número ya no valida | + | function _esNumeroEntero(campo) { |
| - | //Si es un número aún tiene que coincidir con el valor del campo (la covnersión hace caso omiso de los valores no válidos, por lo que no llega con ver si es numérico). | + | |
| - | return (valor.toString() == campo.value.trim()); | + | if (isNaN(valor)) return false; //Si no es un número ya no valida |
| - | }; | + | //Si es un número aún tiene que coincidir con el valor del campo (la covnersión hace caso omiso de los valores no válidos, por lo que no llega con ver si es numérico). |
| + | return (valor.toString() == campo.value.trim()); | ||
| + | }; | ||
| - | //Función que verifica si el valor de un campo es un número entero | + | |
| - | function | + | function |
| - | | + | |
| + | return !isNaN(valor); | ||
| + | }; | ||
| - | var valor = parseInt(campo.value, 10); | + | //Verifica que el campo tenga un valor comprendido en el rango que se especifique, |
| - | if (isNaN(valor)) return false; //Si no es un número ya no valida | + | function _verificarRango(campo, |
| - | //Si es un número aún tiene que coincidir con el valor del campo (la covnersión hace caso omiso de los valores | + | //Si está vacío no se verifica |
| - | return (valor.toString() == campo.value.trim()); | + | if (campo.value |
| - | }; | + | var rango = valor.split(" |
| + | | ||
| + | var min = parseFloat(rango[0]); | ||
| + | if (isNaN(min)) | ||
| + | var max = parseFloat(rango[1]); | ||
| + | if (isNaN(max)) return false; | ||
| + | //El valor del campo debe ser un número | ||
| + | var val = parseFloat(campo.value); | ||
| + | if (isNaN(val)) return false; | ||
| + | //La validación propiamente dicha | ||
| + | return (val >= min && val <= max); | ||
| + | }; | ||
| - | //Función que verifica si el valor de un campo es una fecha o no | + | |
| - | function | + | //Si solo se especifica uno, se comprueba solo el rango inferior |
| - | if (_estaVacio(campo)) return true; //Si está vacío | + | |
| - | var valor = _convertirAFecha(campo.value); | + | //Si está vacío no se verifica |
| - | return !isNaN(valor); | + | if (campo.value == "" |
| - | }; | + | var rango = valor.split(" |
| + | var min = _convertirAFecha(rango[0]); //Los valores deben ser fechas | ||
| + | var max = null; //El rango superior | ||
| + | if (isNaN(min)) return false; | ||
| + | if (rango.length > 1) { | ||
| + | max = _convertirAFecha(rango[1]); | ||
| + | if (isNaN(max)) return false; //Si esta debe ser válido | ||
| + | } | ||
| + | //El valor del campo debe ser una fecha (esto es implícito a este tipo de validación) | ||
| + | var val = _convertirAFecha(campo.value); | ||
| + | if (isNaN(val)) | ||
| + | //La validación propiamente dicha | ||
| + | if (max != null) { | ||
| + | return | ||
| + | } else { | ||
| + | return (val >= min); | ||
| + | } | ||
| + | | ||
| - | //Verifica que el campo tenga un valor comprendido en el rango que se especifique, | + | |
| - | function | + | function |
| - | if (_estaVacio(campo)) return true; //Si está vacío no se comprueba | + | //Si está vacío no se verifica |
| + | if (campo.value == "" | ||
| + | var valor = campo.value.trim(); | ||
| + | | ||
| + | //La mayor parte de los emails | ||
| + | return / | ||
| + | }; | ||
| - | var rango = valor.split(" | + | //Esta función ejecuta el código que se indica como función de validación personalizada. |
| - | if (rango.length != 2) return | + | //La función debe estar declarada globalmente |
| - | var min = parseFloat(rango[0]); | + | //Para facilitar su uso la ejecuta con el contexto |
| - | if (isNaN(min)) return false; | + | //Detecta los posibles parámetros (de haberlos) y se los pasa a la función indicada (como cadenas, OJO). |
| - | var max = parseFloat(rango[1]); | + | function _llamarFuncionPersonalizada(campo, |
| - | if (isNaN(max)) return false; | + | //Si está vacío no se verifica |
| - | //El valor del campo debe ser un número | + | if (campo.value == "" |
| - | var val = parseFloat(campo.value); | + | |
| - | if (isNaN(val)) return false; | + | //Usaré simples funciones de cadena para ello |
| - | //La validación propiamente dicha | + | if (valor.length < 0) return false; |
| - | return (val >= min && val <= max); | + | var nomFunc, parametros |
| - | }; | + | var posParentesis1 = valor.indexOf("("), |
| + | posParentesis2 = valor.indexOf(" | ||
| + | | ||
| + | | ||
| + | | ||
| + | } else { | ||
| + | nomFunc = valor; | ||
| + | } | ||
| + | | ||
| + | if (window[nomFunc]) | ||
| + | | ||
| + | else | ||
| + | return false; | ||
| + | }; | ||
| - | //Verifica un rago de fechas separadas con guiones | + | ///////////////////////////////////////////////// |
| - | //Si solo se especifica uno, se comprueba solo el rango inferior | + | // FUNCIONES AUXILIARES |
| - | function _verificarRangoFechas(campo, | + | ///////////////////////////////////////////////// |
| - | if (_estaVacio(campo)) return true; //Si está vacío no se comprueba (ya se comprobará después en caso de ser obligatorio) | + | |
| - | var rango = valor.split(" | + | |
| - | var min = _convertirAFecha(rango[0]); | + | //Espera fechas en formato DD/MM/YY o DD/ |
| - | var max = null; //El rango superior no es obligatorio | + | |
| - | if (isNaN(min)) return false; | + | |
| - | if (rango.length > 1) { | + | }; |
| - | max = _convertirAFecha(rango[1]); | + | |
| - | if (isNaN(max)) return false; //Si esta debe ser válido | + | |
| - | } | + | |
| - | //El valor del campo debe ser una fecha (esto es implícito a este tipo de validación) | + | |
| - | var val = _convertirAFecha(campo.value); | + | |
| - | if (isNaN(val)) return false; | + | |
| - | //La validación propiamente dicha | + | |
| - | if (max != null) { | + | |
| - | return (val >= min && val <= max); | + | |
| - | } else { | + | |
| - | return (val >= min); | + | |
| - | } | + | |
| - | }; | + | |
| - | //Verifica que el campo contenga | + | |
| - | function | + | //Si están soportadas usa las funciones estándar del W3C, sino lo hace "a pelo" |
| - | if (_estaVacio(campo)) return true; //Si está vacío no se comprueba (ya se comprobará después en caso de ser obligatorio) | + | |
| + | if (elemento.classList) { | ||
| + | elemento.classList.add(clase); | ||
| + | } else { | ||
| + | var re = new RegExp(" | ||
| + | if (!re.test(elemento.className)) | ||
| + | elemento.className += " " + clase; | ||
| + | } | ||
| + | }; | ||
| - | var valor = campo.value.trim(); | + | |
| - | //Usaremos | + | |
| - | //La mayor parte de los emails se pueden validar así, pero no todos: http://www.regular-expressions.info/email.html | + | if (elemento.classList) { |
| - | return / | + | elemento.classList.remove(clase); |
| - | }; | + | } else { |
| + | var re = new RegExp(" | ||
| + | | ||
| + | elemento.className.replace(re, "" | ||
| + | } | ||
| + | | ||
| - | //Esta función ejecuta | + | |
| - | //La función debe estar declarada globalmente | + | function |
| - | //Para facilitar su uso la ejecuta con el contexto (this) apuntando al campo que se está verificando | + | if (campo.attributes[" |
| - | //Detecta los posibles parámetros | + | |
| - | function | + | else |
| - | if (_estaVacio(campo)) return | + | return "" |
| + | } | ||
| - | //Analizamos | + | |
| - | //Usaré simples funciones | + | |
| - | if (valor.length < 0) return false; | + | |
| - | var nomFunc, parametros = []; | + | |
| - | var posParentesis1 = valor.indexOf(" | + | |
| - | if (posParentesis1 >= 0 && posParentesis2 >= 0 && posParentesis2 > posParentesis1) { | + | |
| - | nomFunc = valor.slice(0, | + | |
| - | parametros = valor.slice(posParentesis1 + 1, posParentesis2).split("," | + | |
| - | } else { | + | |
| - | nomFunc = valor; | + | |
| - | } | + | |
| - | // | + | |
| - | if (window[nomFunc]) | + | |
| - | return window[nomFunc].apply(campo, | + | |
| - | else | + | |
| - | return false; | + | |
| - | }; | + | |
| - | ///////////////////////////////////////////////// | + | |
| - | // FUNCIONES AUXILIARES | + | var msgVal = _extraerMsgCampo(campo); |
| - | /////////////////////////////////////////////////// | + | |
| - | //Función auxiliar para ayudar a convertir fechas en texto a verdaderas fechas de JavaScript | + | |
| - | //Espera fechas en formato DD/MM/YY o DD/ | + | |
| - | function _convertirAFecha(strFecha) { | + | |
| - | return new Date(strFecha.split('/' | + | |
| - | }; | + | |
| - | //Añade una clase a las clases CSS aplicadas a un elemento | + | |
| - | //Si están soportadas usa las funciones estándar del W3C, sino lo hace "a pelo" | + | if (campo.parentNode.getElementsByClassName(" |
| - | function _addClass(elemento, | + | var spanMsg |
| - | if (elemento.classList) { | + | |
| - | elemento.classList.add(clase); | + | |
| - | } else { | + | campo.parentNode.appendChild(spanMsg); |
| - | var re = new RegExp("\\b" + clase + "\\b"); | + | } |
| - | if (!re.test(elemento.className)) //Si NO tiene la clase aplicada, se la añadimos | + | }; |
| - | elemento.className += " " + clase; | + | |
| - | } | + | |
| - | }; | + | |
| - | //Quita una clase de las clases CSS aplicadas a un elemento | + | |
| - | function _removeClass(elemento, clase) { | + | function |
| - | if (elemento.classList) { | + | |
| - | elemento.classList.remove(clase); | + | // |
| - | } else { | + | var spansMsg = campo.parentNode.getElementsByClassName(" |
| - | var re = new RegExp(" | + | for (var i = 0; i < spansMsg.length; |
| - | //Le quitamos la clase si es necesario | + | campo.parentNode.removeChild(spansMsg[i]); |
| - | elemento.className.replace(re, "" | + | } |
| - | } | + | }; |
| - | }; | + | |
| - | //Extrae | + | /////////////////////////////////////////////////// |
| - | function _extraerMsgCampo(campo) { | + | // OBJETO QUE ENCAPSULA LA FUNCIONALIDAD |
| - | | + | /////////////////////////////////////////////////// |
| - | return campo.attributes[PREFIJO + " | + | //Si no existe |
| - | else | + | if (!window.campusMVP) window.campusMVP = {}; |
| - | return ""; | + | //Si no existe ya un objeto " |
| - | } | + | if (!window.campusMVP.Validador) { |
| + | window.campusMVP.Validador = { | ||
| + | validaCampo: | ||
| + | | ||
| + | getMensajeValidacion: | ||
| + | | ||
| + | } | ||
| + | })(); | ||
| + | </ | ||
| - | //Se encarga | + | ===== Creación |
| - | function _muestraMensajeValidacion(campo) { | + | |
| - | //Se extrae el mensaje asociado al campo (de haberlo) | + | No es frecuente tener que hacerlo, pero en JavaScript es posible también **definir eventos propios** que luego se comportarán como cualquier otro evento, pudiendo gestionarlos |
| - | var msgVal = _extraerMsgCampo(campo); | + | |
| - | // | + | Por ejemplo, podemos hacer que, en un cuadro de texto, cuando |
| - | _addClass(campo, PREFIJO + " | + | |
| - | //Si no está ya, añado una etiqueta con la información | + | Se trata de un ejemplo bastante trivial, pero si estamos creando un componente reutilizable, |
| - | if (campo.parentNode.getElementsByClassName(PREFIJO + "mensaje").length == 0) { | + | |
| - | var spanMsg = document.createElement(" | + | |
| - | spanMsg.className = PREFIJO + " | + | |
| - | spanMsg.textContent = msgVal; | + | |
| - | campo.parentNode.appendChild(spanMsg); | + | |
| - | } | + | |
| - | }; | + | |
| - | //Retira las muestras visbles de que el campo no ha validado correctamente | + | Luego veremos un ejemplo real práctico |
| - | function _quitaMensajeDeValidacion(campo) { | + | |
| - | _removeClass(campo, PREFIJO + " | + | |
| - | // | + | |
| - | var spansMsg = campo.parentNode.getElementsByClassName(PREFIJO + " | + | |
| - | for (var i = 0; i < spansMsg.length; | + | |
| - | campo.parentNode.removeChild(spansMsg[i]); | + | |
| - | } | + | |
| - | }; | + | |
| - | /////////////////////////////////////////////////// | + | ==== Crear un evento ==== |
| - | // OBJETO QUE ENCAPSULA LA FUNCIONALIDAD | + | |
| - | /////////////////////////////////////////////////// | + | Para crear nuestro propio evento solo tenemos que instanciar |
| - | //Si no existe el " | + | |
| - | if (!window.campusMVP) window.campusMVP = {}; | + | En el primer caso solamente le pasaremos el nombre del evento al constructor, así: |
| - | //Si no existe ya un objeto | + | |
| - | if (!window.campusMVP.Validador) { | + | <code javascript> |
| - | window.campusMVP.Validador = { | + | var miEvento = new Event(' |
| - | validaCampo: | + | |
| - | validaFormulario: _validaFormulario, | + | |
| - | getMensajeValidacion: | + | |
| - | }; | + | |
| - | } | + | |
| - | })(); | + | |
| </ | </ | ||
| - | ==== DEMO: Explicación del código de la biblioteca de validación ==== | ||
| - | ===== Creación | + | Mientras que en el segundo caso, con '' |
| + | Por ejemplo, en nuestro evento '' | ||
| + | |||
| + | <code javascript> | ||
| + | var miEvento = new CustomEvent(' | ||
| + | bubbles: false, | ||
| + | detail: { email: eMailObtenidoDelInput} | ||
| + | }); | ||
| + | </ | ||
| + | |||
| + | Como vemos, el constructor, | ||
| + | |||
| + | Las propiedades disponibles para el evento personalizado son [[https:// | ||
| + | |||
| + | Aparte de '' | ||
| + | |||
| + | ==== Lanzar un evento personalizado ==== | ||
| + | |||
| + | Vale, ya sabemos cómo crear un evento, pero ¿cómo lo lanzamos? La respuesta es que, al igual que cada elemento de la página dispone de los métodos '' | ||
| + | |||
| + | En realidad, todos los elementos de una página que pueden gestionar eventos implementan una interfaz denominada '' | ||
| + | |||
| + | Este método simplemente toma el evento que queremos lanzar y lo notifica, forzando al navegador a llamar a todos los manejadores que se le hayan asociado usando '' | ||
| + | |||
| + | Por ejemplo, en el caso de nuestro hipotético ''< | ||
| + | |||
| + | <code javascript> | ||
| + | miInput.dispatchEvent(miEvento); | ||
| + | </ | ||
| + | |||
| + | siendo '' | ||
| + | |||
| + | Si alguien lo quiere capturar lo único que tendría que hacer es usar un '' | ||
| + | |||
| + | <code javascript> | ||
| + | var miInput = document.getElementById(' | ||
| + | miInput.addEventListener(' | ||
| + | console.log(' | ||
| + | }); | ||
| + | </ | ||
| + | |||
| + | Como vemos, se gestiona como cualquier otro evento, y en este caso además podemos obtener cierta información extra (el email introducido) recurriendo a la propiedad '' | ||
| + | |||
| + | Una cuestión importante de este tipo de eventos personalizados es que cuando llamamos a '' | ||
| + | |||
| + | <code javascript> | ||
| + | miInput.dispatchEvent(miEvento); | ||
| + | console.log(' | ||
| + | </ | ||
| + | |||
| + | No veríamos el mensaje en consola hasta que se hayan ejecutado todos. Debemos tenerlo en cuenta. | ||
| + | |||
| + | ==== Valor devuelto por un evento ==== | ||
| + | |||
| + | La función '' | ||
| + | |||
| + | Por lo tanto, si nuestro código debe hacer algo tras el evento que queremos que se pueda cancelar, debemos comprobar el valor devuelto y si es '' | ||
| + | |||
| + | Por ejemplo, imagina que en nuestro ejemplo del ''< | ||
| + | |||
| + | Este ejemplo sencillo del evento '' | ||
| + | |||
| + | ==== Ejemplo: eventos para detectar la aparición y desaparición de elementos en la página ==== | ||
| + | |||
| + | Una cuestión que puede resultar muy útil en una página o aplicación web es la posibilidad de detectar **cuándo aparece o desaparece de la pantalla un elemento determinado debido a las acciones del usuario**, tanto total como parcialmente. | ||
| + | |||
| + | Por ejemplo, si desaparece una pieza de información importante porque el usuario hace scroll moviendo los contenidos podemos sacar una nota resumen, recordatorio o acceso directo para poder verla de nuevo, y ocultarlo otra vez cuando vuelva a aparecer. Cosas por el estilo. | ||
| + | |||
| + | Para conseguir algo así nos vendría muy bien disponer de eventos para los elementos que **nos informasen de cuándo aparecen o desaparecen de la parte visible de la página**. Nos suscribiríamos a este evento de la manera convencional y recibiríamos automáticamente notificaciones si el elemento aparece o desaparece. | ||
| + | |||
| + | El problema es que **no existe ningún evento como este** en HTML/ | ||
| + | |||
| + | En esta lección y un próximo vídeo práctico vamos a aprovechar lo que conocemos de creación de eventos para desarrollar desde cero la funcionalidad necesaria para conseguir tener varios eventos relacionados con la visibilidad, | ||
| + | |||
| + | En concreto vamos a crear 4 eventos para cualquier elemento de una página relacionados con su visibilidad, | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | Los eventos añaden una propiedad '' | ||
| + | |||
| + | En esta lección vamos a ver algunos detalles generales de la " | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Esta biblioteca de eventos la he creado como un proyecto Open Source que puedes [[https:// | ||
| + | </ | ||
| + | |||
| + | La funcionalidad la vamos a dividir en varios bloques independientes que, en conjunto, nos permitirán obtener lo que necesitamos. | ||
| + | |||
| + | === 1.- Detectar la ubicación relativa de cualquier elemento de la página === | ||
| + | |||
| + | En HTML la forma de detectar la ubicación real de un elemento en la página es utilizar el método '' | ||
| + | |||
| + | Este método no toma ningún parámetro y devuelve un objeto especial de tipo '' | ||
| + | |||
| + | <code javascript> | ||
| + | var caja = elto.getBoundingClientRect(); | ||
| + | console.log(caja.top); | ||
| + | </ | ||
| + | |||
| + | En este caso se mostraría en la consola la posición del borde superior del elemento respecto a la parte de arriba del viewport. | ||
| + | |||
| + | === 2.- Determinar si el elemento está visible en la página o no === | ||
| + | |||
| + | Como las coordenadas anteriores son relativas al área visible (o viewport) de la página, esto quiere decir que varían en cada momento en función de donde esté colocado el elemento. Si hacemos scroll irán cambiando, al igual que con otras acciones posibles en la página por parte del usuario. Si el elemento desaparece por la parte superior de la página, la propiedad '' | ||
| + | |||
| + | Podríamos determinar fácilmente **si el elemento está o no por completo dentro de la página** en un momento dado definiendo una función como esta: | ||
| + | |||
| + | <code javascript> | ||
| + | function isElementTotallyVisible(elt) { | ||
| + | var viewportWidth = window.innerWidth || document.documentElement.clientWidth; | ||
| + | var viewportHeight = window.innerHeight || document.documentElement.clientHeight; | ||
| + | //Posición de la caja del elemento | ||
| + | var box = elt.getBoundingClientRect(); | ||
| + | return ( box.top >= 0 && | ||
| + | | ||
| + | | ||
| + | | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Lo que hacemos es, antes de nada, **determinar el ancho y el alto del área visible** actual, para lo cual usamos la propiedad '' | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Nota: Se incluye también el '' | ||
| + | </ | ||
| + | |||
| + | A continuación se obtienen los límites de la caja del elemento, y se comparan con los bordes del viewport para ver si el elemento está totalmente contenido dentro de éste o no. Devuelve '' | ||
| + | |||
| + | Es un poco más complicado determinar **si el elemento está contenido parcialmente en la página o no**. A lo mejor nos interesa determinar no solo cuándo cualquier parte del elemento se sale de la vista (que sería lo anterior) sino cuándo **se sale de la vista por completo**. En este caso sería útil averiguar si el elemento está dentro del área visible, aunque sea parcialmente. Es decir, con que se vea aunque sea un fragmento minúsculo del elemento, poder saberlo. | ||
| + | |||
| + | Tendríamos que crear una versión alternativa de la función anterior cambiando la comprobación final, que ahora sería como esta: | ||
| + | |||
| + | <code javascript> | ||
| + | function isElementPartiallyVisible(elt) { | ||
| + | var viewportWidth = window.innerWidth || document.documentElement.clientWidth; | ||
| + | var viewportHeight = window.innerHeight || document.documentElement.clientHeight; | ||
| + | //Posición de la caja del elemento | ||
| + | var box = elt.getBoundingClientRect(); | ||
| + | var insideBoxH = (box.left >= 0 && box.left <= viewportWidth) || | ||
| + | (box.right >= 0 && box.right <= viewportWidth); | ||
| + | var insideBoxV = (box.top >= 0 && box.top <= viewportHeight) || | ||
| + | (box.bottom> | ||
| + | return (insideBoxH && insideBoxV); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | En este caso las condiciones son más complicadas, | ||
| + | |||
| + | === 3.- Detectar automáticamente si el elemento aparece o desaparece del área visible === | ||
| + | |||
| + | <WRAP center round important 60%> | ||
| + | IMPORTANTE: en este ejemplo utilizo eventos del navegador de forma que practicaquemos también su uso, pero no sería la manera más adecuada de hacer esto. Existe una API específica del navegador, la [[https:// | ||
| + | </ | ||
| + | |||
| + | Ahora que ya sabemos determinar si el elemento está total o parcialmente dentro del área visible, lo primero es determinar por qué motivos es posible que un elemento se desplace y, por lo tanto, pueda aparecer o desaparecer de dentro del área visible de la página. | ||
| + | |||
| + | Existen varias causas para la modificación de la posición de un elemento y por lo tanto para su posible cambio de visibilidad: | ||
| + | |||
| + | * **Scroll de la página**. Este es el más evidente. Lo podemos detectar gracias al **evento '' | ||
| + | * **Cambio de tamaño**: si la ventana cambia de tamaño, la página se redibuja (" | ||
| + | * **Carga de la página**: la carga de la página se produce realmente en dos fases. Primero se carga su código y se interpreta (el DOM estaría listo) y luego se acaban de cargar los elementos externos, como por ejemplo las imágenes. Si alguna de estas imágenes no tiene especificadas sus dimensiones, | ||
| + | |||
| + | Hay algún caso más que veremos un poco después, pero con estos 3 sería suficiente para la mayor parte de las necesidades, | ||
| + | |||
| + | === 4.- Determinar si el elemento ha aparecido o desaparecido del área visible === | ||
| + | |||
| + | Vamos a definir una función que nos va a permitir determinar si un elemento está parcialmente en el área visible y poder ejecutar una función en dicho caso: | ||
| + | |||
| + | <code javascript> | ||
| + | function inViewportPartially(elt, | ||
| + | var prevVisibility = isElementPartiallyVisible(elt); | ||
| + | //Se define un manejador que se llamarña ante posibles cambios | ||
| + | function detectPossibleChange() { | ||
| + | var isVisible = isElementPartiallyVisible(elt); | ||
| + | if (isVisible != prevVisibility) { //ha cambiado el estado de visibilidad | ||
| + | prevVisibility = isVisible; | ||
| + | if (typeof handler == " | ||
| + | handler(isVisible, | ||
| + | } | ||
| + | } | ||
| + | |||
| + | //Gestionar los eventos que nos interesan con la función anterior para notificar la detección | ||
| + | window.addEventListener(" | ||
| + | window.addEventListener(" | ||
| + | window.addEventListener(" | ||
| + | return detectPossibleChange; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Al llevar " | ||
| + | |||
| + | Veamos cómo funciona. | ||
| + | |||
| + | La función recibe como parámetros el elemento a monitorizar y la función que se ha de llamar cuando el elemento entre o salga del área visible de la página. | ||
| + | |||
| + | Lo primero que hacemos es establecer el estado inicial de la visibilidad del elemento, es decir, si cuando se llama a esta función el elemento está visible o no, que será nuestro estado de base para el mismo. Se consigue con la función vista en el paso 2, y en este caso consideramos elementos parcialmente visibles. | ||
| + | |||
| + | Definimos también una función dentro de esta función que será la que se llamará ante cualquiera de los 3 eventos que hemos considerado que cambian el estado de visibilidad del elemento ('' | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Nota: Este método interno tiene acceso a los parámetros de la función (elemento y manejador) y al estado de base gracias a la clausura a la que pertenece, al ser una función interna a la otra. | ||
| + | </ | ||
| + | |||
| + | Finalmente, lo único que se hace es definir estos tres eventos que habíamos identificado para que llamen a esta función interna ante cualquier cambio que se produzca. | ||
| + | |||
| + | Existe otra función gemela de esta, '' | ||
| + | |||
| + | Bien, esto es el código de " | ||
| + | |||
| + | ==== Anexo: Otras formas de cambiar la disposición de la página ==== | ||
| + | |||
| + | Además de los 3 eventos que hemos considerado para detectar cambios, existen otras maneras menos frecuentes de que se modifique la posición de los elementos de la página. Concretamente **3 casos que no vamos a utilizar** en la biblioteca por no mermar el rendimiento de la página: | ||
| + | |||
| + | * **La modificación mediante código del árbol de elementos de la página (DOM)**: si metemos o quitamos elementos, éstos podrían provocar el reflujo de la misma y por lo tanto cambiar la posición de un elemento que nos interese. Si tuviésemos interés en detectar este hecho podríamos usar el evento '' | ||
| + | * **Cambio dinámico de la propiedad '' | ||
| + | * **Zoom de la página**: si el usuario hace zoom en la página, el tamaño de los elementos cambia y por lo tanto cambian de posición. Esto es más común en navegadores móviles, donde se puede hacer zoom con los dedos (si se le permite), pero en los navegadores de escritorio también ocurre. [[https:// | ||
| + | |||
| + | Aunque nuestro código no detecta estos tres casos marginales, en cuanto el usuario mueva mínimamente la página el evento de scroll detectará la posición y nos notificará de cualquier posible cambio que hubiese usando el evento personalizado pertinente, así que en la práctica no compensa preocuparse por ellos salvo quizá en algún caso muy particular. | ||
| + | |||
| + | ==== Funcionamiento práctico ==== | ||
| + | |||
| + | ==== DEMO: Explicación del código ==== | ||
| ===== 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, | ||
| + | * Añade a una página cualquiera (que tenga al menos varios elementos '' | ||
| + | * 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 '' | ||
| + | |||
| + | Una pregunta importante: ¿Funcionan todas las combinaciones? | ||
| + | |||
| + | * 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" | ||
| + | |||
| + | 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 '' | ||
| + | |||
| + | Otra opción, incluso mejor, es anotar la posición de cada marcianito, modificándola en los eventos que se van produciendo, | ||
| + | |||
| + | 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:// | ||
| + | </ | ||
| + | |||
| + | 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:// | ||
| + | |||
| + | ¡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:// | ||
informatica/programacion/cursos/programacion_avanzada_javascript/eventos_navegadores.1728651707.txt.gz · Última modificación: por tempwin
