lunes, 26 de mayo de 2014

CSS hover puede crear problemas en ipad

En el iPad y en el iPhone (en dispositivos táctiles iOS en general) nos encontramos a veces un comportamiento extraño: tocamos un link pero no nos lleva a la URL correspondiente, tocamos una segunda vez y entonces funciona.

En el primer toque se comporta como si fuera un hover, en el segundo como un click. El mapeo automático de click a touch parece no funcionar correctamente ¿de que depende? ¿porqué algunos links funcionan y otros no?

:hover en dispositivos táctiles

En un dispositivo táctil el concepto de hover no tiene sentido. No podemos colocar el cursor sobre un link, simplemento lo tocamos o no.

Parece lógico suponer que los dispositivos táctiles ignorarán el hover, pero no siempre es así. Los desarrolladores de Apple consideran que si se ha definido un comportamiento mediante CSS :hover puede haber información importante asociada y debe mostrarse. El primer toque funcionará como un hover y el segundo como un click.

En realidad iOS sólamente considera el comportamiento asociado a hover cuando modifica la visibilidad de un elemento de la página (visibility o display). Cualquier otra propiedad que cambiemos no tiene este efecto, los links responderán con un solo toque.

Este comportamiento realmente tiene sentido porque hay menús desplegables que se muestran al pasar el ratón por encima. iOS permite de esta manera que el primer touch muestre el submenú desplegable (hover) para despues hacer click en lo opción del submenu.

Si se muestran elementos nuevos en el hover, el primer click se considera una activación del nuevo contenido.

Este comportamiento está documentado en la iOS developer library:

"Si el usuario toca un elemento clickable, los eventos llegarán en este orden: mouseover, mousemove, mousedown, mouseup, and click. [...] Si el contenido de la página cambia en el evento mousemove, no se envía ninguno de los eventos posteriores. Este comportamiento permite al usuario pulsar en el contenido nuevo"

Se resume en la siguiente imagen:

El click funcionará con un sólo toque si no mostramos elementos nuevos. Por ejemplo, el siguiente CSS no supone ningún problema:

a:hover {
    color: blue;
}

Sin embargo es muy común mostrar un tooltip en el hover sobre un botón o un enlace. Cada vez que hagamos esto, ese botón necesitará dos toques en iOS para funcionar adecuadamente.

Solución para evitar el doble click

Lo primero que tenemos que considerar es que este comportamiento no es igual en todos los dispositivos táctiles, por eso debemos evitar mostrar elementos importantes en el hover.

Si utilizamos Modernizr podemos aprovechar la clase CSS 'no-touch' que crea para los dispositivos no táctiles:

    .no-touch p:hover span {
        display: block;
    }

De esta forma el span con nuestro tooltip se mostrará sólo en dispositivos no táctiles.

Otra solución es evitar el mapeo automático de touch a click y manejar explicitamente el evento touchstart junto con el click. Tendríamos el siguiente JS:

    ('#myElement').on( 'touchstart click', myClickCallback );

En vez de:

    ('#myElement').on( 'click', myClickCallback );



Fuentes:
jQuery click events on the iPad
stackOverflow: Javascript Events are not working in Tablet-pc
Fixing jQuery Click Events for the iPad
iOS has a :hover problem

jueves, 15 de mayo de 2014

Redirecciones en JavaScript

En JavaScript utilizamos el objeto Location, accesible desde la propiedad window.location, para obtener información y modificar la URL de la página actual.

Podemos hacer una redirección de varias maneras:

//la forma más habitual y recomendada
window.location.href = "http://google.com";

//Equivalente a la anterior. Es simplemente un atajo sintáctico
window.location = "http://google.com";

//Equivalente a las anteriores pero con formato 'método'
window.location.assign("http://google.com");

//Equivalente a una redirección HTTP. El botón 'back' no volverá a la pg actual
window.location.replace("http://google.com");
  

Lo más sencillo y corto sería utilizar el atajo sintáctico:

window.location = "http://google.com";

Aunque lo que se recomienda generalmente, por expresar mejor lo que hace, es:

window.location.href = "http://google.com";

La diferencia de location.replace("url") con las demás es que que sustituye la URL actual en el historial del navegador. Esto implica que al pulsar el botón back o hacer window.history.back() no volvemos a la página original, que ya no existe en el historial, sino a la anterior.

En resumen, podríamos decir que para simular un click en un link (cambiamos debido a una acción del usuario) utilizamos:

window.location.href = "http://google.com";

o sus equivalentes.

Para simular una redirección utilizamos:

window.location.replace( "http://google.com" );

Esta última es la que debemos usar cuando la página redirecciona automáticamente, para evitar el problema del bucle al pulsar back desde la página final, que nos lleva a la página anterior que nos vuelve a redireccionar a la final, y asi ad infinitum.

¿cual es la diferencia entre document.location y window.location?

En los navegadores actuales no hay diferencia, document.location se mapea a window.location y se cumple:

    document.location === window.location //true

NOTA: document.location era originalmente una propiedad de 'solo lectura' que devolvía la URL actual como un string en vez de un objeto Location.

¿Se ejecuta el javaScript en lineas posteriores a la redirección?

Aunque parezca poco intuitivo, si tenemos alguna línea de código despues de la asignación de window.location, sí se ejecutará. Por ejemplo, en el siguiente código:

    window.location.href = "http://google.com";
    window.location.href = "http://yahoo.com";
    alert( "test" );

La página final será yahoo.com, no google.com, y el alert aparecerá antes de redireccionarnos.

El script en curso continuará ejecutándose hasta el final, cuando devolverá el control al navegador para iniciar la carga de la nueva página. El navegador disparará entonces los eventos 'beforeunload' y 'unload'. En este momento la página original ya queda inactiva. El comportamiento puede ser inconsistente entre navegadores.

Fuentes:
StackOverflow
MDN - window.location