JASoft.org

El blog de José Manuel Alarcón Aguín. Programación web y mucho más...

MENÚ - JASoft: JM Alarcón

Reaccionar a Media Queries desde JavaScript

Hoy en día casi no se concibe una página o aplicación web que no sea Responsive. El Responsive Web Design busca crear diseños web que se adapten de manera automática a diferentes tamaños y resoluciones de pantalla. De esta manera en lugar de tener un diseño diferente para cada tamaño de pantalla (¡o incluso una versión diferente de la interfaz para cada dispositivo!), se busca que un solo diseño, refinado de manera inteligente, sirva para todos los tamaños.

El nombre viene de que el diseño "responde" de manera automática a las diferentes condiciones del entorno de la página, adaptándose automáticamente y en tiempo real a los cambios de tamaño que haya. Puedes verlo en acción si visitas, por ejemplo, la página de campusMVP. Si la abres en una ventana aparte y cambias el tamaño de ésta (el ancho fundamentalmente) verás que a medida que el espacio disminuye el diseño se adapta para que se vea bien incluso con poco espacio. Puedes probarlo no solo en la portada (donde es menos vistoso) sino también en el catálogo en la parte de recursos, donde todavía hay más cambios para poder adaptar el contenido mejor. Este mismo blog también es Responsive.

Responsive_BlogEngine

Lo más interesante de estos efectos es que no se necesita escribir código para conseguirlos. Es suficiente con utilizar CSS puro y duro. Para ello se saca partido, entre otras cosas, a la característica de CSS denominada Media Queries. Éstas nos permiten definir conjuntos de reglas CSS que se aplican únicamente cuando se cumplen ciertas condiciones respecto al medio en el que se visualiza la página.

Por ejemplo, si escribimos:

@media screen and (max-device-width: 480px) and (orientation: landscape) {
   /* REGLAS CSS*/
}

Con esto estamos indicando al navegador que si la página se está visualizando en una pantalla (podría ser en otro medio, como por ejemplo en papel porque la imprimimos), cuyo ancho no supera los 480 píxeles (ancho por defecto de una pantalla apaisada de un iPhone hasta la versión 4) y que además la orientación del dispositivo es apaisada (es decir, está en horizontal), entonces que le aplique las reglas CSS específicas para este caso que tenemos dentro de las llaves.

Esto da una libertad enorme para definir diferentes estilos según tamaños, resoluciones de pantalla y otras condiciones, y permite en gran medida la "magia" de los sitios Responsivos.

Ejecución innecesaria de código en páginas Responsive

Un caso habitual es, por ejemplo, querer tener dos menús de navegación completamente diferentes en resoluciones altas y bajas. Así, por ejemplo, si la pantalla es muy grande podemos poner un menú horizontal con botones grandes y fáciles de pulsar, que ocupen mucho a lo ancho. Sin embargo si la pantalla o la ventana es pequeña, podemos ocultar el menú anterior y mostrar uno diferente en forma de lista desplegable o usando un simple control de lista de HTML.

Aquí tienes un caso real con su correspondiente implementación. Lo que se explica en este artículo es cómo convertir mediante JavaScript (o más bien jQuery) un menú CSS puro ancho en control de lista desplegable para el caso de pantallas pequeñas, como la de un teléfono. Como mantener dos menús por separado es propenso a errores porque tarde o temprano dejarán de estar sincronizados si lo hacemos a mano, el ejemplo construye la lista desplegable a partir de los elementos del menú CSS, y así no hay fallo. Luego con Media Queries muestran uno o el otro en función del ancho de la pantalla. Muy fácil y muy útil.

El problema de esto es que el menú desplegable pequeño y todo el código JavaScript que lo genera, se ejecutan siempre, haga falta o no. Lo ideal sería que el código que genera el menú pequeño se ejecutase únicamente cuando sea necesario, y no antes. Pero el JavaScript necesario para conseguirlo se ejecuta siempre nada más cargar la página y solo depende de CSS el mostrar un menú u otro según sea necesario.

Media Matching en JavaScript

Dado que el Responsive Web Design ha sido tradicionalmente más un tema de diseñadores web que de programadores, poca gente conoce que tiene una contrapartida en el lado de código JavaScript. Se trata de una API muy sencilla para verificar la coincidencia de condiciones de Media Queries con el estado actual de la pantalla. A esta funcionalidad se accede mediante una función del objeto global (window) denominada matchMedia.

A esta función se le puede pasar una cadena con una expresión de consulta de medios exactamente igual que las que se usan en CSS y nos devuelve un objeto de tipo MediaQueryList con información sobre el estado actual.

Por ejemplo, si escribimos lo siguiente:

var mql = window.matchMedia("screen and (max-device-width: 480px) and (orientation: landscape)");

Estaremos preguntando si la página actual cumple con las condiciones de la Media Query especificada, que como vemos es exactamente lo mismo que escribimos en CSS, pero sin el @media delante.

Lo que obtenemos en la variable mql es un objeto JavaScript muy sencillo que tiene dos propiedades:

  • matches: es un valor booleano que devuelve true si las condiciones de la Media Query coinciden con las de la ventana actual, y false en caso contrario.
  • media: es la cadena que estamos probando. Más adelante veremos qué sentido tiene esta propiedad, aparentemente inútil.

La verdaderamente útil y que utilizaremos siempre es la primera, matches, claro.

La verdad es que esto está muy bien porque nos permite verificar en cualquier momento si se cumple una determinada condición de entorno, pero es poco útil porque implicaría estar llamándolo constantemente para detectar si se ha producido algún cambio.

Podríamos usar un temporizador para que cada pocos milisegundos hiciera algo así:

function verificarMediaQuery(sMQ){
    var mql = window.matchMedia(sMQ);
    if (mql.matches)
        //Hacer lo que sea
}

y nos permitiese detectar cambios en las condiciones casi en el momento en el que se producen. Pero sería una pérdida de recursos de computación tonta, que realmente sería peor que ejecutar otro código innecesario como el de crear la lista desplegable que comentaba en el ejemplo anterior.

Eventos JavaScript para Media Queries

Por suerte la gente del W3C no son unos chapuzas (Oh, wait!) y han añadido capacidad para gestionar eventos relacionados con estas consultas. Para ello las Media QueryLists disponen de dos métodos específicos:

  • addListener: añade una referencia a un manejador que será llamado cada vez que el estado de la ventana cambie.
  • removeListener: quita la referencia a un manejador (lo usaremos raramente).

Con esto es posible definir eventos que salten automáticamente cuando se cumpla la Media Query especificada. Por ejemplo, crea una nueva página y en la cabecera mete el siguiente código:

function cambioMediaQuery(mql) {
	alert(mql.matches);
}

var mql = window.matchMedia("screen and (max-width: 750px)");
cambioMediaQuery(mql);
mql.addListener(cambioMediaQuery);

Ahora cárgala en cualquier navegador moderno. Al cargar la página se mostrará una alerta con un "false" (salvo que tu resolución de pantalla sea de 750px de ancho o menos, cosa que dudo). Ahora cambia el tamaño de la ventana despacio reduciéndolo poco a poco hasta que llegue a 750px de ancho. Te saltará la alerta de nuevo, ya que se notificará el evento en cuanto se cumpla la condición y estés en 750px de ancho o menos.

Dos cosas importantes a destacar aquí:

  1. La propiedad media del objeto MediaQueryList adquiere sentido en este punto ya que gracias a ella podemos reutilizar el mismo manejador para varias Media Queries diferentes y así poder distinguir fácilmente entre unas y otras.
  2. En el código anterior fíjate en que antes de llamar a addListener se llama al manejador previamente pasándole la actual MediaQueryList. Esto es importante porque si da la casualidad de que la Media Query se cumple ya con el tamaño inicial de la pantalla no se detectaría (el evento solo salta cuando las condiciones de la ventana cambian) y podría fallar. Por eso es importante hacer la llamada "a mano" al menos una vez.

Soporte de navegadores

Estas técnicas están soportadas por todos los navegadores modernos, incluyendo Internet Explorer 10 o posterior, como indica CanIUSe:

matchMedia-Soporte

De hecho esto supone en la práctica que está soportado por todos los navegadores que también soportan Media Queries, excepto IE9 y Opera Mini (IE8 ya ni siquiera soporta Media Queries tampoco). Para estos navegadores antiguos podríamos usar un polyfill que reemplaza a la funcionalidad no soportada, y de hecho nos puede servir -con mucho trabajo- para suplir la carencia de soporte en CSS en el caso de IE8 y anteriores.

En resumen

Aunque estamos acostumbrados a ver Media Queries utilizadas constantemente en el lenguaje CSS es poco conocido que también podemos sacarle partido desde JavaScript. Esto nos permite dotar de inteligencia a las reglas de composición de nuestra página y conseguir efectos mejores u optimizar el uso de algunas partes de la página que no deben ejecutarse siempre.

En este artículo he tratado de explicarlo con el mayor detalle posible, aunque se trata de una técnica bastante sencilla.

¡Espero que te sea útil!

¿Este post te ha ayudado?, ¿has aprendido algo nuevo? Pues ¡ayúdame a ayudar a otros! No te costará nada y puedes conseguir premio. Es muy importante. Pulsa para saber más...
José Manuel Alarcón José Manuel Alarcón
Fundador de campusMVP.es, el proyecto de referencia en formación on-line para programadores en lengua española. Autor de varios libros y cientos de artículos. Galardonado como MVP de Microsoft desde 2004. Gallego de Vigo, amante de la ciencia y la tecnología, la música y la lectura. Ayudando a la gente en Internet desde 1996.
Descarga GRATIS mi último libro (no técnico): "Tres Monos, Diez Minutos".
Banner

Comentarios (4) -

Peru Luis Rene Mas Mas

Buen post amigo lo voy a implementar en mi sitio gracias

Responder

Peru Luis Rene Mas Mas

Buen post amigo lo voy a implementar en mi sitio gracias

Responder

Hola José, tengo una pequeña duda y no se si con esto que has explicado se puede conseguir.

Tengo un archivo de js puesto de esta forma <script src="js/skrollr.min.js"></script> y me gustaría que este archivo funcionara para resoluciones superiores a 1024px, en casa de una resolución menor que no haga caso a este archivo, como si no estuviera.

No se si me expliqué bien... Gracias de todos modos!

Un saludo.

Responder

German Pace

Hola , como haria para las tabs o pestañas se conviertan en select en mobile?
Mucha gracias

Responder

Agregar comentario