MediaQueryList — медіазапити з JavaScript і matchMedia
Об'єкт MediaQueryList у JavaScript
Об'єкт MediaQueryList у JavaScript є потужним інструментом, який дозволяє вам взаємодіяти з медіа-запитами безпосередньо зі скрипту. У сучасному веб-розробництві адаптивний дизайн є ключовою вимогою. Це означає, що ваш сайт або веб-додаток повинен коректно відображатися на різних пристроях з різними розмірами екрану та орієнтацією.
Створення та використання MediaQueryList
MediaQueryList створюється за допомогою метода window.matchMedia(). Цей метод приймає медіа-запит як аргумент і повертає об'єкт MediaQueryList.
Приклад:
let mql = window.matchMedia('(max-width: 600px)');
console.log(mql.matches); // повертає true, якщо ширина екрану менша або дорівнює 600px, інакше - false
У цьому прикладі ми створюємо MediaQueryList для визначення, чи є ширина екрану користувача меншою або дорівнює 600 пікселям.
Події MediaQueryList та їх обробники
Однією з найцікавіших особливостей MediaQueryList є можливість відслідковувати зміни у результаті медіа-запиту. Це робиться за допомогою події change.
Приклад:
let mql = window.matchMedia('(max-width: 600px)');
function handleWidthChange(e) {
if (e.matches) {
console.log('Екран має ширину до 600 пікселів');
} else {
console.log('Екран більший за 600 пікселів');
}
}
mql.addEventListener('change', handleWidthChange);
Використовуючи обробник подій, ми можемо динамічно реагувати на зміни у розмірах екрану, наприклад, змінювати стиль або структуру веб-сторінки відповідно до умов медіа-запиту.
автор: Bond
Властивості MediaQueryList
Об'єкт MediaQueryList включає в себе декілька ключових властивостей, які допомагають розробникам отримати доступ до інформації про медіа-запит та дізнатися, чи задовольняє поточний стан пристрою вказаним умовам.
matches:
- Опис: Ця властивість вказує, чи задовольняє поточний стан пристрою умовам медіа-запиту.
- Тип даних: Boolean (
trueабоfalse). - Приклад:
let mql = window.matchMedia('(max-width: 600px)');
if (mql.matches) {
console.log('Екран менше 600 пікселів у ширину');
} else {
console.log('Екран 600 пікселів або більше у ширину');
}
В цьому прикладі, якщо ширина екрану користувача менше 600 пікселів, властивість matches поверне true, і в консолі буде виведено повідомлення про це. У протилежному випадку виведеться інше повідомлення.
media:
- Опис: Властивість
mediaдозволяє отримати рядкове представлення медіа-запиту, асоційованого з конкретним об'єктомMediaQueryList. - Тип даних: String.
- Приклад:
let mql = window.matchMedia('(orientation: portrait)');
console.log(mql.media); // Виведе: "(orientation: portrait)"
Тут ми створили об'єкт MediaQueryList для визначення орієнтації екрану (портретна). Властивість media дозволить нам перевірити, який саме медіа-запит було використано.
Ці властивості роблять MediaQueryList дуже корисним, коли вам потрібно робити умовні перевірки або відслідковувати зміни у відповідності до медіа-запитів, не використовуючи CSS.
Загальний опис
автор: NagarD
- Порада
-
Старайтеся формулювати медіа-запити якнайбільш конкретно, щоб уникнути небажаних перетинів. Наприклад, замість того, щоб використовувати
автор: Bond(min-width: 600px)' та '(min-width: 900px), використовуйте(min-width: 600px) and (max-width: 899px). - Порада
-
Коли ви використовуєте об'єкт
автор: BondMediaQueryList, не забудьте використовувати подіюchange, щоб відслідковувати зміни стосовно відповідності медіа-запиту. Це особливо корисно, коли вам потрібно динамічно адаптуватися до змін умов пристрою користувача. - Порада
-
Коли ви використовуєте
автор: BondMediaQueryListдля внесення динамічних змін у ваш інтерфейс, може бути корисно використовувати функції-обгортки або допоміжні бібліотеки для спрощення коду та зменшення його дублювання. - Порада
-
Не всі браузери можуть підтримувати
MediaQueryListабо окремі види медіа-запитів. Перед використанням переконайтеся, що ви додали відповідні перевірки підтримки:
автор: Bondif (window.matchMedia) { let mql = window.matchMedia('(max-width: 600px)'); // ... ваш код }
Синтаксис
let mediaQueryList = window.matchMedia(<string>);
Переглядачі
| Переглядач | ||||||
|---|---|---|---|---|---|---|
| 9 | 6 | 5.1 | 12.1 | 12 | 10 |
| Переглядач | ||||
|---|---|---|---|---|
| 4.4 | 18 | 6 | 5 |
Приклади
+ запропонувати свій приклад у пісочниці
При невеликій ширині екрану навігаційна панель може стати "гамбургер"-меню, що зберігає місце.
HTML
<div class="navbar">перший елемент navbar</div>
<div class="navbar">другий елемент navbar</div>
JS
let mql = window.matchMedia('(max-width: 768px)');
function adaptNavbar(e) {
const navbar = document.querySelector('.navbar');
if (e.matches) {
navbar.classList.add('hamburger-menu');
} else {
navbar.classList.remove('hamburger-menu');
}
}
mql.addEventListener('change', adaptNavbar);
adaptNavbar(mql); // Для первісної установки стилю навігаційної панелі
Прогін по всіх межах об’єкта: звідки він береться, як браузер нормалізує рядок запиту, чому хибний запит нічого не кидає, чому два виклики дають різні об’єкти — і як через це ламається зняття слухача. Результати йдуть у консоль і в таблицю на сторінці; поміняйте ширину вікна, щоб побачити подію change.
// ── таблиця для виводу: у пісочниці вікон і сповіщень немає, тож пишемо в DOM ──
document.head.append(Object.assign(document.createElement('style'), {
textContent: 'table{border-collapse:collapse;font:14px/1.4 system-ui,sans-serif}'
+ 'th,td{padding:5px 10px;border:1px solid #b9c6d8;text-align:left;vertical-align:top}'
+ 'th{background:#eef4ff}td:first-child{color:#5a6472}'
}));
const table = document.createElement('table');
table.innerHTML = '<thead><tr><th>що питаємо</th><th>що вийшло</th></tr></thead><tbody></tbody>';
document.body.append(table);
const tbody = table.querySelector('tbody');
const show = (pytannia, vidpovid) => {
const row = tbody.insertRow();
row.insertCell().textContent = pytannia;
row.insertCell().textContent = String(vidpovid);
console.log(pytannia + ' → ' + vidpovid);
};
// ── звідки береться об'єкт ──
const mq = window.matchMedia('(max-width: 600px)');
show('typeof matchMedia(...)', mq.constructor.name); // → MediaQueryList
show('це EventTarget?', mq instanceof EventTarget); // → true
try {
new MediaQueryList();
} catch (e) {
show('new MediaQueryList()', e.name + ': ' + e.message); // → TypeError: Illegal constructor.
}
// ── два поля, заради яких усе й затівалося ──
show('mq.media', mq.media); // → (max-width: 600px)
show('mq.matches (ширина рамки зараз)', mq.matches); // → true або false, залежно від рамки
// ⚠️ matches тільки для читання: присвоєння мовчки нічого не робить
const bulo = mq.matches;
mq.matches = !bulo;
show('після mq.matches = !mq.matches', mq.matches === bulo ? 'не змінилося' : 'змінилося'); // → не змінилося
// ── браузер нормалізує рядок запиту ──
show('" (MAX-WIDTH: 600PX) "', window.matchMedia(' (MAX-WIDTH: 600PX) ').media); // → (max-width: 600px)
show('незакрита дужка "(width: 1px"', window.matchMedia('(width: 1px').media); // → (width: 1px)
// ⚠️ хибний запит НЕ кидає помилки — він стає "not all" і не збігається ніколи
const bad = window.matchMedia('це не медіа-запит');
show('хибний запит → media', bad.media); // → not all
show('хибний запит → matches', bad.matches); // → false
// ⚠️ порожній рядок — це "будь-який носій", тобто ЗАВЖДИ істина
const empty = window.matchMedia('');
show('порожній запит → media', JSON.stringify(empty.media)); // → ""
show('порожній запит → matches', empty.matches); // → true
// ⚠️ кожен виклик віддає НОВИЙ об'єкт — і саме на цьому ламається зняття слухача
const a = window.matchMedia('(min-width: 1px)');
const b = window.matchMedia('(min-width: 1px)');
show('два виклики з тим самим рядком: a === b', a === b); // → false
show('але значення в них однакове', a.matches === b.matches); // → true
// ── корисні запити, які взагалі не про ширину ──
for (const zapyt of ['(prefers-color-scheme: dark)', '(prefers-reduced-motion: reduce)',
'(pointer: coarse)', '(hover: hover)', '(orientation: portrait)', 'print']) {
show(zapyt, window.matchMedia(zapyt).matches);
}
// ── подія change: тримаємо ОДИН об'єкт у змінній, інакше слухача не зняти ──
const vuzka = window.matchMedia('(max-width: 500px)');
vuzka.addEventListener('change', (e) => {
// у події свої поля matches і media — зовнішню змінну чіпати не треба
show('подія change, ' + e.media, e.matches ? 'умова виконалася' : 'умова перестала виконуватися');
console.log('тип події:', e.constructor.name); // → MediaQueryListEvent
});
show('слухач "(max-width: 500px)" почепився', 'поміняйте ширину вікна — рядок з’явиться нижче');
Дозволяє автоматично перемикати тему дизайну сторінки між світлою та темною в залежності від системних установок користувача.
let mql = window.matchMedia('(prefers-color-scheme: dark)');
function toggleTheme(e) {
if (e.matches) {
document.body.classList.add('dark-theme');
} else {
document.body.classList.remove('dark-theme');
}
}
mql.addEventListener('change', toggleTheme);
toggleTheme(mql); // Для первісної установки теми
Якщо користувач використовує режим високого контрасту для покращення читабельності, виведіть спеціальне повідомлення, щоб відзначити це.
let mql = window.matchMedia('(forced-colors: active)');
function notifyHighContrast(e) {
if (e.matches) {
alert('Ви використовуєте режим високого контрасту. Дизайн сайту може відрізнятися.');
}
}
mql.addEventListener('change', notifyHighContrast);
notifyHighContrast(mql); // Для первісного виявлення установок
Коментарі
Коментарів ще немає — будьте першим!