Skip to content

Всё для WordPress, веб-разработки — и не только

📱 JavaScript: как определить ширину экрана — аналог @media запросов в коде

📱 JavaScript: как определить ширину экрана — аналог @media запросов в коде

Бывает, вёрстка уже готова, CSS-медиазапросы расставлены, а поведение на конкретном брейкпойнте нужно отловить именно в JavaScript. Показать попап только на мобильных, перестроить сетку при ресайзе, запустить анимацию когда экран «узкий», всё это требует от скрипта понимания текущей ширины окна.

Проблема в том, что разработчики часто идут сложным путём: парсят window.innerWidth, вешают throttle на resize, сравнивают с магическими числами, и получают хрупкий код, который живёт отдельно от CSS-брейкпойнтов. А ведь в браузере давно есть метод, работающий с теми же медиавыражениями, что и CSS.

Ниже, три практических подхода: от современного matchMedia (работает как @media в CSS) до jQuery-варианта для legacy-проектов. С живыми примерами, которые можно скопировать и запустить прямо сейчас.

💡 Быстрый обзор:

  • matchMedia, нативный метод, принимающий CSS-медиавыражение и сообщающий, выполняется ли оно прямо сейчас; идеален для синхронизации JS-логики с CSS-брейкпойнтами
  • resize + matchMedia, связка, реагирующая на изменение окна браузера; скрипт узнаёт о пересечении брейкпойнта мгновенно, без периодического опроса innerWidth
  • jQuery-вариант, для проектов, где jQuery уже на странице; тот же resize, но без нативного matchMedia, сравнение идёт через $(window).width()

matchMedia, единый источник правды для ширины экрана

Главный недостаток window.innerWidth, он ничего не знает о ваших CSS-брейкпойнтах. Вы ставите в медиазапросах 768px, а в JS пишете if (window.innerWidth < 768), и однажды округление или полоса прокрутки ломают синхронизацию.

window.matchMedia() решает эту проблему радикально: он принимает ту же строку медиавыражения, что и CSS-правило @media. Результат, объект MediaQueryList со свойством .matches (true / false). Никаких магических чисел, никакого расхождения с вёрсткой.

Базовый синтаксис:

1const mq = window.matchMedia("(min-width: 768px)");
2
3if (mq.matches) {
4 console.log("Планшет или шире — 768px+");
5} else {
6 console.log("Мобильное разрешение — меньше 768px");
7}

Тот же вызов matchMedia("(min-width: 768px)") браузер выполняет по тем же правилам, что и @media (min-width: 768px) в CSS. Если в CSS для этого брейкпойнта спрятан сайдбар, JS «видит» то же самое и может, например, скрыть мобильное меню.

Кроме .matches, объект MediaQueryList даёт свойство .media (строка исходного запроса) и метод addEventListener для подписки на изменения. Это значит, что один раз объявив брейкпойнт в конфиге, вы можете использовать его одновременно и в CSS, и в JS без дублирования магических чисел, достаточно вынести 768 в константу и подставлять в оба места.

Реакция на ресайз без throttle и костылей

Проверка «сейчас», это полдела. Настоящая магия начинается, когда скрипт узнаёт о пересечении брейкпойнта в момент изменения окна.

У MediaQueryList есть событие change, оно срабатывает ровно когда значение .matches переключается. Не на каждый пиксель ресайза, а только при переходе через границу:

1const mq = window.matchMedia("(min-width: 500px)");
2
3mq.addEventListener("change", function (e) {
4 if (e.matches) {
5 console.log("Экран расширился до 500px или больше");
6 } else {
7 console.log("Экран сузился меньше 500px");
8 }
9});

Для обратной совместимости со старыми браузерами то же самое можно сделать через общий обработчик resize на window:

1window.addEventListener("resize", function () {
2 if (window.matchMedia("(min-width: 500px)").matches) {
3 console.log("Ширина экрана — минимум 500px");
4 } else {
5 console.log("Меньше 500px");
6 }
7});
Консоль браузера с выводом сообщений о ширине экрана при ресайзе

Разница простая: change на MediaQueryList, событийно-чистый подход (нет лишних вызовов при ресайзе внутри диапазона), а resize на window, fallback, понятный любому разработчику.

Диапазон ширины: между двумя брейкпойнтами

Частая задача, «от 769px до 1024px». matchMedia и здесь работает как CSS: комбинируйте min-width и max-width в одном выражении:

1window.addEventListener("resize", function () {
2 if (
3 window.matchMedia("(min-width: 769px)").matches &&
4 window.matchMedia("(max-width: 1024px)").matches
5 ) {
6 console.log("Планшетный диапазон: 769px – 1024px");
7 } else {
8 console.log("За пределами планшетного диапазона");
9 }
10});

Либо одним выражением (браузеры понимают составные медиазапросы так же, как в CSS):

1const tablet = window.matchMedia("(min-width: 769px) and (max-width: 1024px)");
2
3tablet.addEventListener("change", function (e) {
4 console.log(e.matches ? "Вошли в планшетный диапазон" : "Вышли из планшетного диапазона");
5});

Какой вариант выбрать? change на MediaQueryList, когда нужно поймать именно момент пересечения границы (например, перестроить DOM-дерево). resize + matchMedia, когда логика проще и нужно просто «проверить сейчас» без подписки на будущие переходы.

jQuery-вариант: когда matchMedia недоступен

Если проект на jQuery и полифилы не вариант, тот же результат достигается сравнением $(window).width() с пороговым значением:

1jQuery(document).ready(function ($) {
2 if ($(window).width() > 1000) {
3 console.log("Ширина экрана больше 1000px");
4 } else {
5 console.log("Ширина экрана 1000px или меньше");
6 }
7});

Этот код выполняется однократно при загрузке страницы. Для отслеживания ресайза оберните проверку в обработчик:

1jQuery(document).ready(function ($) {
2 function checkWidth() {
3 if ($(window).width() > 1000) {
4 console.log("Ширина > 1000px");
5 } else {
6 console.log("Ширина ≤ 1000px");
7 }
8 }
9
10 checkWidth(); // первый запуск
11 $(window).on("resize", checkWidth);
12});

Но помните: $(window).width() и matchMedia могут разойтись на несколько пикселей из-за полосы прокрутки. matchMedia работает с областью просмотра (viewport), так же, как CSS-правила. Поэтому для всего нового кода я рекомендую именно нативный метод.

matchMedia против innerWidth, коротко

Критерий

matchMedia

innerWidth

Синхронизация с CSS

Полная (те же выражения)

Ручная подгонка чисел

Событие при пересечении брейкпойнта

change на MediaQueryList

Только resize на window

Учёт полосы прокрутки

Как в CSS (viewport)

Зависит от браузера

Обнаружение тёмной темы / prefers-reduced-motion

Да (любое медиавыражение)

Нет

⁉️🤔 Частые вопросы

Что лучше: matchMedia или window.innerWidth?

matchMedia лучше всегда, когда логика привязана к CSS-брейкпойнтам. Он использует те же правила, что и @media, исключено расхождение на пиксель-другой из-за полосы прокрутки или масштабирования. innerWidth уместен только если нужно точное числовое значение (например, для расчёта количества помещающихся элементов), а не факт «экран шире N пикселей».

Работает ли matchMedia в старых браузерах?

Да, поддержка широкая: все современные браузеры, включая мобильные, и Internet Explorer 10+. IE9 и ниже остались за бортом, для них придётся использовать window.innerWidth или jQuery-вариант из статьи. На практике доля IE9 в 2026 году стремится к нулю.

Можно ли через matchMedia проверять не только ширину?

Да, метод принимает любое валидное CSS-медиавыражение. Например: (orientation: portrait), определение ориентации устройства; (prefers-color-scheme: dark), тёмная тема в ОС; (prefers-reduced-motion: reduce), запрос на отключение анимаций. Работает так же, как в CSS: window.matchMedia("(prefers-color-scheme: dark)").matches вернёт true, если у пользователя включена тёмная тема.

Нужно ли удалять обработчик change при уходе со страницы?

В современном коде, нет. Браузер сам очищает память при выгрузке страницы. В SPA (React, Vue), где компонент монтируется и размонтируется без перезагрузки страницы, обязательно сохраните ссылку на обработчик и удалите через removeEventListener в componentWillUnmount / onUnmounted, иначе получите утечку памяти и повторные срабатывания на «мёртвых» компонентах.

Почему $(window).width() и matchMedia иногда показывают разную ширину?

Потому что они измеряют разное. matchMedia работает с шириной viewport (область просмотра CSS), той самой, которую используют медиазапросы. $(window).width() / window.innerWidth включают ширину вертикальной полосы прокрутки, если она есть. Разница обычно 15-17 px, как раз ширина скроллбара. Отсюда правило: привязываете логику к CSS-брейкпойнтам, используйте matchMedia; нужна «честная» ширина окна в пикселях, innerWidth.

Так что использовать: итоговый расклад

Для нового кода ответ однозначен, window.matchMedia(). Он живёт в одном контракте с вашим CSS, не требует ручной подгонки чисел под каждый брейкпойнт и даёт событийную модель «переключилось, узнал» вместо постоянного опроса ширины.

Типичный сценарий, где разница ощутима сразу: вы верстаете карточку товара, которая на десктопе показывает галерею из четырёх изображений, а на мобильном, свайпаемое одно. В CSS у вас @media (max-width: 768px), меняющее раскладку. В JS вместо if (window.innerWidth <= 768) вы пишете matchMedia("(max-width: 768px)"), и браузер гарантирует, что JS-условие сработает ровно тогда же, когда сменилась вёрстка. Никаких «почти попало», никаких багов на 767px из-за скроллбара.

jQuery-подход с $(window).width() оставьте для поддержки старых проектов, он работает, но заставляет дублировать брейкпойнты в коде и молча расходится с CSS на ширину скроллбара.

А если хотите увидеть всё вышеописанное вживую, вот 10-минутный туториал, где matchMedia разбирается от вызова до событийной модели:

Попробуйте заменить ближайший if (innerWidth < 768) на matchMedia("(min-width: 768px)"), и вы сразу почувствуете, насколько чище становится код.