
📱 JavaScript: cómo detectar el ancho de pantalla, el equivalente de @media query en código
A veces la maquetación ya está lista, los media queries de CSS están en su lugar, pero usted necesita detectar el comportamiento en un punto de corte específico directamente en JavaScript. Mostrar un popup solo en dispositivos móviles, reorganizar una cuadrícula al redimensionar, disparar una animación cuando la pantalla es «estrecha»: todo esto requiere que el script entienda el ancho actual de la ventana.
El problema es que los desarrolladores a menudo toman el camino difícil: analizan window.innerWidth, añaden throttle al evento resize, comparan contra números mágicos y terminan con código frágil que vive separado de los puntos de corte de CSS. Sin embargo, los navegadores tienen desde hace tiempo un método que funciona con las mismas expresiones de medios que CSS.
A continuación, tres enfoques prácticos: desde el moderno matchMedia (funciona como @media en CSS) hasta una variante con jQuery para proyectos heredados. Con ejemplos en vivo que puede copiar y ejecutar ahora mismo.
💡 Resumen rápido:
matchMedia: un método nativo que acepta una expresión de medios CSS e informa si coincide actualmente; ideal para sincronizar la lógica de JS con los puntos de corte de CSSresize+matchMedia: una combinación que responde a los cambios de la ventana del navegador; el script se entera del cruce de un punto de corte al instante, sin consultar periódicamenteinnerWidth- Variante jQuery: para proyectos donde jQuery ya está en la página; el mismo
resize, pero sinmatchMedianativo; la comparación se hace mediante$(window).width()
MatchMedia: la fuente única de verdad para el ancho de pantalla
El principal inconveniente de window.innerWidth es que no sabe nada de sus puntos de corte de CSS. Usted establece 768px en los media queries, luego escribe if (window.innerWidth < 768) en JS y, tarde o temprano, el redondeo o la barra de desplazamiento rompen la sincronización.
window.matchMedia() resuelve este problema de raíz: acepta la misma cadena de expresión de medios que la regla @media de CSS. El resultado es un objeto MediaQueryList con una propiedad .matches (true / false). Sin números mágicos, sin desajustes con la maquetación.
Sintaxis básica:
1 const mq = window.matchMedia("(min-width: 768px)"); 2 3 if (mq.matches) { 4 console.log("Tablet or wider — 768px+"); 5 } else { 6 console.log("Mobile resolution — less than 768px"); 7 }
La misma llamada matchMedia("(min-width: 768px)") es evaluada por el navegador usando las mismas reglas que @media (min-width: 768px) en CSS. Si la barra lateral se oculta en este punto de corte en CSS, JS «ve» lo mismo y puede, por ejemplo, ocultar el menú móvil.
Además de .matches, el objeto MediaQueryList proporciona una propiedad .media (la cadena de consulta original) y un método addEventListener para suscribirse a los cambios. Esto significa que, una vez que declare un punto de corte en una configuración, puede usarlo tanto en CSS como en JS sin duplicar números mágicos: simplemente extraiga 768 a una constante e insértela en ambos lugares.
Responder al resize sin throttle ni soluciones alternativas
Verificar «ahora mismo» es solo la mitad de la batalla. La verdadera magia comienza cuando el script se entera del cruce de un punto de corte en el momento en que la ventana cambia.
MediaQueryList tiene un evento change que se dispara exactamente cuando el valor de .matches cambia. No en cada píxel del resize, sino solo al cruzar el límite:
1 const mq = window.matchMedia("(min-width: 500px)"); 2 3 mq.addEventListener("change", function (e) { 4 if (e.matches) { 5 console.log("Screen expanded to 500px or more"); 6 } else { 7 console.log("Screen narrowed to less than 500px"); 8 } 9 });
Para compatibilidad con navegadores más antiguos, puede lograr el mismo resultado mediante un manejador general de resize en window:
1 window.addEventListener("resize", function () { 2 if (window.matchMedia("(min-width: 500px)").matches) { 3 console.log("Screen width — at least 500px"); 4 } else { 5 console.log("Less than 500px"); 6 } 7 });

La diferencia es simple: change en MediaQueryList es un enfoque dirigido por eventos (sin llamadas adicionales durante el resize dentro de un rango), mientras que resize en window es una alternativa familiar para cualquier desarrollador.
Rango de ancho: entre dos puntos de corte
Una tarea común es «de 769px a 1024px». matchMedia funciona como CSS también aquí: combine min-width y max-width en una sola expresión:
1 window.addEventListener("resize", function () { 2 if ( 3 window.matchMedia("(min-width: 769px)").matches && 4 window.matchMedia("(max-width: 1024px)").matches 5 ) { 6 console.log("Tablet range: 769px – 1024px"); 7 } else { 8 console.log("Outside the tablet range"); 9 } 10 });
O como una expresión única (los navegadores entienden las consultas de medios compuestas igual que en CSS):
1 const tablet = window.matchMedia("(min-width: 769px) and (max-width: 1024px)"); 2 3 tablet.addEventListener("change", function (e) { 4 console.log(e.matches ? "Entered tablet range" : "Left tablet range"); 5 });
¿Qué variante elegir? change en MediaQueryList cuando necesite capturar el momento exacto del cruce del límite (por ejemplo, para reestructurar el árbol DOM). resize + matchMedia cuando la lógica sea más simple y solo necesite «verificar ahora» sin suscribirse a transiciones futuras.
Variante jQuery: cuando matchMedia no está disponible
Si el proyecto usa jQuery y los polyfills no son una opción, el mismo resultado se logra comparando $(window).width() con un valor umbral:
1 jQuery(document).ready(function ($) { 2 if ($(window).width() > 1000) { 3 console.log("Screen width greater than 1000px"); 4 } else { 5 console.log("Screen width 1000px or less"); 6 } 7 });
Este código se ejecuta una vez al cargar la página. Para seguir el resize, envuelva la verificación en un manejador:
1 jQuery(document).ready(function ($) { 2 function checkWidth() { 3 if ($(window).width() > 1000) { 4 console.log("Width > 1000px"); 5 } else { 6 console.log("Width ≤ 1000px"); 7 } 8 } 9 10 checkWidth(); // initial run 11 $(window).on("resize", checkWidth); 12 });
Pero recuerde: $(window).width() y matchMedia pueden diferir en unos pocos píxeles debido a la barra de desplazamiento. matchMedia trabaja con el viewport, igual que las reglas CSS. Por eso recomiendo el método nativo para todo código nuevo.
MatchMedia vs innerWidth: una comparación breve
Criterio |
|
|
|---|---|---|
Sincronización con CSS | Total (mismas expresiones) | Ajuste manual de números |
Evento al cruzar punto de corte |
| Solo |
Manejo de barra de desplazamiento | Igual que CSS (viewport) | Depende del navegador |
Detección de tema oscuro / | Sí (cualquier expresión de medios) | No |
⁉️🤔 Preguntas frecuentes
¿Qué es mejor: matchMedia o window.innerWidth?
matchMediasiempre es mejor cuando la lógica está vinculada a los puntos de corte de CSS. Usa las mismas reglas que@media, eliminando discrepancias de un píxel o dos debido a la barra de desplazamiento o al zoom.innerWidthsolo es apropiado cuando necesita el valor numérico exacto (por ejemplo, para calcular cuántos elementos caben), no el hecho de «pantalla más ancha que N píxeles».
¿Funciona matchMedia en navegadores antiguos?
Sí, el soporte es amplio: todos los navegadores modernos, incluidos los móviles, e Internet Explorer 10+. IE9 e inferiores quedan atrás; para ellos tendrá que usar
window.innerWidtho la variante jQuery de este artículo. En la práctica, la cuota de IE9 en 2026 se acerca a cero.
¿Puede matchMedia verificar algo más que el ancho?
Sí, el método acepta cualquier expresión de medios CSS válida. Por ejemplo:
(orientation: portrait)para la orientación del dispositivo;(prefers-color-scheme: dark)para el tema oscuro en el sistema operativo;(prefers-reduced-motion: reduce)para una solicitud de desactivar animaciones. Funciona exactamente como en CSS:window.matchMedia("(prefers-color-scheme: dark)").matchesdevuelvetruesi el usuario tiene el tema oscuro activado.
¿Necesito eliminar el manejador change al salir de la página?
En código moderno, no. El navegador limpia la memoria automáticamente al descargar la página. En SPAs (React, Vue), donde un componente se monta y desmonta sin recargar la página, debe guardar una referencia al manejador y eliminarla mediante
removeEventListenerencomponentWillUnmount/onUnmounted; de lo contrario, obtendrá fugas de memoria y disparos repetidos en componentes «muertos».
¿Por qué $(window).width() y matchMedia a veces muestran anchos diferentes?
Porque miden cosas diferentes.
matchMediatrabaja con el ancho del viewport (el área de visualización CSS), la misma que usan los media queries.$(window).width()/window.innerWidthincluye el ancho de la barra de desplazamiento vertical, si está presente. La diferencia suele ser de 15-17 px, exactamente el ancho de la barra de desplazamiento. De ahí la regla: si vincula la lógica a puntos de corte de CSS, usematchMedia; si necesita el ancho «real» de la ventana en píxeles, useinnerWidth.
Entonces, ¿qué usar?: la conclusión final
Para código nuevo la respuesta es clara: window.matchMedia(). Vive en el mismo contrato que su CSS, no requiere ajuste manual de números para cada punto de corte y proporciona un modelo de eventos de «cambió, usted lo sabe» en lugar de consultar constantemente el ancho.
Un escenario típico donde la diferencia se nota de inmediato: está construyendo una ficha de producto que muestra una galería de cuatro imágenes en escritorio, pero una sola imagen deslizable en móvil. En CSS tiene @media (max-width: 768px) cambiando la maquetación. En JS, en lugar de if (window.innerWidth <= 768) escribe matchMedia("(max-width: 768px)"), y el navegador garantiza que la condición de JS se disparará exactamente cuando la maquetación cambie. Sin «casi funcionaba», sin errores a 767px por culpa de la barra de desplazamiento.
Deje el enfoque de jQuery con $(window).width() para mantener proyectos antiguos: funciona, pero le obliga a duplicar puntos de corte en el código y diverge silenciosamente del CSS por el ancho de la barra de desplazamiento.
Y si quiere ver todo lo descrito anteriormente en acción, aquí tiene un tutorial de 10 minutos donde se explica matchMedia desde la invocación hasta el modelo de eventos:
Pruebe a reemplazar el if (innerWidth < 768) más cercano por matchMedia("(min-width: 768px)") y sentirá de inmediato cuánto más limpio se vuelve el código.



