Медиа-запросы в CSS – ключевой компонент адаптивного дизайна. Это отличный способ для применения разных стилей в зависимости от контекста, например, на основе размера области просмотра, предпочтительной цветовой схемы, конкретных взаимодействий и определенных устройств: принтеры, телевизоры, проекторы и т.д.
Знаете ли вы, что медиа-запросы есть для JavaScript? Наверное, не часто в JavaScript приходится встречаться с ними, однако есть очень полезные варианты использования их для реализации адаптивных плагинов: карусели, слайдеры. Например, когда при определенной ширине html-страницы требуется перерисовать и пересчитать элементы слайдера.
Работа с медиа-запросами в JavaScript отличается от работы с ними в CSS, хотя концепции схожи: сопоставить некоторые условия и применить какие-то вещи.
Использование matchMedia()
Чтобы определить, соответствует ли документ строке медиа-запроса в JavaScript, следует использовать метод matchMedia()
. Несмотря на то, что он официально является частью спецификации модуля представления объектной модели CSS (CSS Object Model View Module specification), пока ещё находящейся в статусе черновика, его поддержка браузерами достигает 98,6%.
Использование почти идентично медиа-запросам в CSS. Надо передать строку медиа-запроса в метод matchMedia()
, а затем проверить свойство .matches
.
const mediaQuery = window.matchMedia('(min-width: 768px)');
Определенный в аргументе метода медиа-запрос вернет объект MediaQueryList. Этот объект содержит информацию о медиа-запросе, в том числе ключевое свойство .matches
. Это свойство только для чтения, оно возвращает true
, если состояние документа соответствует запросу.
// Условие для viewport шириной не менее 768 пикселей
const mediaQuery = window.matchMedia('(min-width: 768px)')
// Проверить, что media query будет true
if (mediaQuery.matches) {
// Вызвать метод alert
alert('Media Query Matched!')
}
Это базовое использование медиа-запросов в JavaScript. Добавляем условие (метод matchMedia()
), которое возвращает объект (MediaQueryList
), проверяем его (свойство .matches
), а затем делаем что-то, если это условие возвращает true
. Не сильно отличается от CSS!
Но это ещё не всё. Например, если изменить размер окна браузера, ничего не изменится, как это происходит в CSS. Это потому, что .matches
реализует разовую проверку и не может отслеживать наличие изменений.
Отслеживание изменений
У MediaQueryList
есть метод addListener()
(а заодно и removeListener()
), который принимает функцию-callback (для события .onchange
). Она вызывается при изменении состояния медиа-запроса. Таким образом можно реагировать на изменения и многократно проверять условия медиа-запроса.
// Условие для viewport шириной не менее 768 пикселей
const mediaQuery = window.matchMedia('(min-width: 768px)');
function handleTabletChange(e) {
// Проверить, что media query будет true
if (e.matches) {
// Вывести сообщение в консоль
console.log('Media Query = ', e.matches);
}
}
// Слушать события
mediaQuery.addListener(handleTabletChange);
// Начальная проверка
handleTabletChange(mediaQuery);
Устаревший способ сделать то же самое
Наиболее распространенный подход – наблюдать за событием resize
, которое возникает при изменениях размеров окна и проверять window.innerWidth
и/или window.innerHeight
.
function checkMediaQuery() {
// Если ширина окна > 768px
if (window.innerWidth > 768) {
// Вывести сообщение в консоль
console.log('Media Query = true');
}
}
// Наблюдаем за изменениями размеров окна
window.addEventListener('resize', checkMediaQuery);
Событие resize
очень много раз вызывается при изменении размеров окна браузера, это – дорогостоящая операция.
Сравнение производительности matchMedia()
и resize
для пустой страницы.
В консоли это будет нагляднее, можно посмотреть и сравнить количество вызовов callback-функций для событий из matchMedia()
и resize
Даже если не обращать внимания на проблемы с производительностью, отслеживание только изменений размеров окна (resize
) сильно ограничено и не позволяет определять тип устройства (принтер, телевизор), ориентацию экрана. Таким образом, использование matchMedia()
даёт больше возможностей в определении состояния html-страницы, чем информация только о его размерах при использовании resize
.
Итоги
Вот так работают медиа-запросы в JavaScript. Мы изучили, как matchMedia()
определяет условия окружения и рассмотрели объект MediaQueryList
, который позволяет выполнять одноразовые (.matches
) и регулярные (addListener()
) проверки этих условий, чтобы реализовать возможность реагировать на их изменения (.onchange
).
А также вспомнили «старый» способ отслеживать события изменения размеров html-документа в окне браузера. Хотя он по-прежнему широко используется и является вполне допустимым способом реакции на изменения размера window.innerWidth
, он не может реализовать возможности для проверки расширенных условий аналогично медиа-запросам.