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)"), і ви одразу відчуєте, наскільки чистішим стає код.