Skip to content

Wszystko o WordPressie, tworzeniu stron — i nie tylko

📱 JavaScript: jak określić szerokość ekranu — analog @media zapytań w kodzie

📱 JavaScript: jak określić szerokość ekranu — analog @media zapytań w kodzie

Zdarza się, że układ jest już gotowy, zapytania media w CSS rozpisane, a zachowanie w konkretnym punkcie granicznym trzeba przechwycić właśnie w JavaScript. Pokazać pop-up tylko na urządzeniach mobilnych, przebudować siatkę podczas zmiany rozmiaru, uruchomić animację, gdy ekran jest „wąski", wszystko to wymaga od skryptu znajomości bieżącej szerokości okna.

Problem w tym, że deweloperzy często idą trudną drogą: parsują window.innerWidth, wieszają throttle na resize, porównują z magicznymi liczbami i otrzymują kruchy kod, który żyje niezależnie od punktów granicznych CSS. Tymczasem w przeglądarce od dawna istnieje metoda, która działa z tymi samymi wyrażeniami medialnymi co CSS.

Poniżej trzy praktyczne podejścia: od nowoczesnego matchMedia (działa jak @media w CSS) po wariant jQuery dla projektów legacy. Z działającymi przykładami, które można skopiować i uruchomić od ręki.

💡 Szybki przegląd:

  • matchMedia, natywna metoda, która przyjmuje wyrażenie medialne CSS i informuje, czy jest ono właśnie spełnione; idealna do synchronizacji logiki JS z punktami granicznymi CSS
  • resize + matchMedia, połączenie reagujące na zmianę rozmiaru okna przeglądarki; skrypt dowiaduje się o przekroczeniu punktu granicznego natychmiast, bez cyklicznego odpytywania innerWidth
  • wariant jQuery, dla projektów, gdzie jQuery jest już na stronie; ten sam resize, ale bez natywnego matchMedia, porównanie odbywa się przez $(window).width()

MatchMedia, jedyne źródło prawdy dla szerokości ekranu

Główna wada window.innerWidth, nie ma on pojęcia o Pana punktach granicznych CSS. Ustawia Pan w zapytaniach medialnych 768px, a w JS pisze if (window.innerWidth < 768) i któregoś dnia zaokrąglenie albo pasek przewijania psują synchronizację.

window.matchMedia() rozwiązuje ten problem radykalnie: przyjmuje ten sam ciąg wyrażenia medialnego co reguła CSS @media. Wynik to obiekt MediaQueryList z właściwością .matches (true / false). Żadnych magicznych liczb, żadnej rozbieżności z układem.

Podstawowa składnia:

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

To samo wywołanie matchMedia("(min-width: 768px)") przeglądarka wykonuje według tych samych zasad co @media (min-width: 768px) w CSS. Jeśli w CSS dla tego punktu granicznego ukryty jest sidebar, JS „widzi" to samo i może na przykład ukryć menu mobilne.

Oprócz .matches obiekt MediaQueryList udostępnia właściwość .media (ciąg pierwotnego zapytania) oraz metodę addEventListener do subskrybowania zmian. Oznacza to, że deklarując raz punkt graniczny w konfiguracji, może Pan używać go jednocześnie w CSS i JS bez duplikowania magicznych liczb, wystarczy wynieść 768 do stałej i podstawić w obu miejscach.

Reakcja na zmianę rozmiaru bez throttle i protez

Sprawdzenie „teraz" to połowa sukcesu. Prawdziwa magia zaczyna się, gdy skrypt dowiaduje się o przekroczeniu punktu granicznego w momencie zmiany okna.

MediaQueryList ma zdarzenie change, które uruchamia się dokładnie wtedy, gdy wartość .matches się przełącza. Nie przy każdym pikselu zmiany rozmiaru, a tylko podczas przejścia przez granicę:

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});

Dla wstecznej kompatybilności ze starszymi przeglądarkami to samo można zrobić przez ogólny handler resize na 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});
Konsola przeglądarki z komunikatami o szerokości ekranu przy zmianie rozmiaru

Różnica jest prosta: change na MediaQueryList to podejście czysto zdarzeniowe (brak zbędnych wywołań podczas zmiany rozmiaru wewnątrz zakresu), a resize na window to fallback zrozumiały dla każdego dewelopera.

Zakres szerokości: między dwoma punktami granicznymi

Częste zadanie: „od 769px do 1024px". matchMedia i tutaj działa jak CSS: łączy Pan min-width i max-width w jednym wyrażeniu:

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});

Albo jednym wyrażeniem (przeglądarki rozumieją złożone zapytania medialne tak samo jak w CSS):

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

Który wariant wybrać? change na MediaQueryList, gdy trzeba złapać sam moment przekroczenia granicy (np. przebudować drzewo DOM). resize + matchMedia, gdy logika jest prostsza i wystarczy „sprawdzić teraz" bez subskrypcji przyszłych przejść.

Wariant jQuery: gdy matchMedia jest niedostępne

Jeśli projekt opiera się na jQuery, a polyfille nie wchodzą w grę, ten sam rezultat osiąga się przez porównanie $(window).width() z wartością progową:

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

Ten kod wykonuje się jednorazowo podczas ładowania strony. Aby śledzić zmianę rozmiaru, należy opakować sprawdzenie w handler:

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});

Proszę jednak pamiętać: $(window).width() i matchMedia mogą różnić się o kilka pikseli z powodu paska przewijania. matchMedia operuje na obszarze widoku (viewport), tak samo jak reguły CSS. Dlatego dla całego nowego kodu rekomenduję właśnie metodę natywną.

MatchMedia kontra innerWidth, w skrócie

Kryterium

matchMedia

innerWidth

Synchronizacja z CSS

Pełna (te same wyrażenia)

Ręczne dopasowywanie liczb

Zdarzenie przy przekroczeniu punktu granicznego

change na MediaQueryList

Tylko resize na window

Uwzględnienie paska przewijania

Jak w CSS (viewport)

Zależy od przeglądarki

Wykrywanie ciemnego motywu / prefers-reduced-motion

Tak (dowolne wyrażenie medialne)

Nie

⁉️🤔 Często zadawane pytania

Co jest lepsze: matchMedia czy window.innerWidth?

matchMedia jest lepsze zawsze, gdy logika jest powiązana z punktami granicznymi CSS. Używa tych samych zasad co @media, wykluczona jest rozbieżność o piksel czy dwa z powodu paska przewijania lub skalowania. innerWidth jest na miejscu tylko wtedy, gdy potrzebna jest dokładna wartość liczbowa (np. do obliczenia liczby mieszczących się elementów), a nie fakt „ekran jest szerszy niż N pikseli".

Czy matchMedia działa w starszych przeglądarkach?

Tak, wsparcie jest szerokie: wszystkie nowoczesne przeglądarki, w tym mobilne, oraz Internet Explorer 10+. IE9 i starsze pozostały z tyłu, dla nich trzeba będzie użyć window.innerWidth lub wariantu jQuery z artykułu. W praktyce udział IE9 w 2026 roku dąży do zera.

Czy przez matchMedia można sprawdzać nie tylko szerokość?

Tak, metoda przyjmuje dowolne poprawne wyrażenie medialne CSS. Na przykład: (orientation: portrait), określenie orientacji urządzenia; (prefers-color-scheme: dark), ciemny motyw w systemie operacyjnym; (prefers-reduced-motion: reduce), żądanie wyłączenia animacji. Działa tak samo jak w CSS: window.matchMedia("(prefers-color-scheme: dark)").matches zwróci true, jeśli użytkownik ma włączony ciemny motyw.

Czy trzeba usuwać handler change przy opuszczaniu strony?

W nowoczesnym kodzie, nie. Przeglądarka sama czyści pamięć przy wyładowaniu strony. W SPA (React, Vue), gdzie komponent jest montowany i odmontowywany bez przeładowania strony, koniecznie należy zachować referencję do handlera i usunąć ją przez removeEventListener w componentWillUnmount / onUnmounted, w przeciwnym razie dojdzie do wycieku pamięci i ponownych wywołań na „martwych" komponentach.

Dlaczego $(window).width() i matchMedia czasem pokazują różną szerokość?

Ponieważ mierzą różne rzeczy. matchMedia operuje na szerokości viewport (obszar widoku CSS), tej samej, której używają zapytania medialne. $(window).width() / window.innerWidth uwzględniają szerokość pionowego paska przewijania, jeśli jest widoczny. Różnica to zwykle 15-17 px, czyli właśnie szerokość scrollbara. Stąd zasada: wiąże Pan logikę z punktami granicznymi CSS, proszę używać matchMedia; potrzebna jest „uczciwa" szerokość okna w pikselach, innerWidth.

Co zatem stosować: podsumowujący harmonogram

Dla nowego kodu odpowiedź jest jednoznaczna: window.matchMedia(). Żyje ono w jednym kontrakcie z Pana CSS, nie wymaga ręcznego dopasowywania liczb do każdego punktu granicznego i daje model zdarzeniowy „przełączyło się, dowiedziałem się" zamiast ciągłego odpytywania szerokości.

Typowy scenariusz, w którym różnica jest odczuwalna od razu: tworzy Pan kartę produktu, która na desktopie pokazuje galerię z czterech zdjęć, a na urządzeniu mobilnym, jedno, które można przewijać. W CSS ma Pan @media (max-width: 768px), które zmienia układ. W JS zamiast if (window.innerWidth <= 768) pisze Pan matchMedia("(max-width: 768px)") i przeglądarka gwarantuje, że warunek JS zadziała dokładnie wtedy, gdy zmienił się układ. Żadnego „prawie trafiło", żadnych bugów na 767px przez scrollbar.

Podejście jQuery z $(window).width() proszę zostawić do wsparcia starych projektów, działa, ale zmusza do duplikowania punktów granicznych w kodzie i po cichu rozmija się z CSS o szerokość scrollbara.

A jeśli chce Pan zobaczyć wszystko opisane powyżej na żywo, oto 10-minutowy tutorial, w którym matchMedia jest rozkładane od wywołania po model zdarzeniowy:

Proszę spróbować zastąpić najbliższy if (innerWidth < 768) przez matchMedia("(min-width: 768px)"), a od razu odczuje Pan, o ile czystszy staje się kod.