Работа с медиа-запросами JavaScript

От автора: что в первую очередь приходит в голову, когда вы думаете о медиа-запросах? Может быть, что-то в файле CSS.

Что-то в файле CSS, что выглядит так:

body { background-color: plum;
}


@media (min-width: 768px) { body { background-color: tomato; }
}

Медиа-запросы CSS — ключевой компонент любого адаптивного дизайна. Это отличный способ применить разные стили к разным контекстам, будь то на основе размера области просмотра, предпочтений движения, предпочтительной цветовой схемы, конкретных взаимодействий и, черт возьми, даже определенных устройств, таких как принтеры, телевизоры и проекторы, и многое другое.

Но знаете ли вы, что у нас есть медиа-запросы и для JavaScript? Это так! Возможно, мы не так часто видим их в JavaScript, но определенно есть варианты их использования, которые я на протяжении многих лет считаю полезными для создания адаптивных плагинов, таких как слайдеры. Например, при определенном разрешении вам может потребоваться перерисовать и пересчитать элементы слайдера.

Работа с медиа-запросами в JavaScript сильно отличается от работы с ними в CSS, хотя концепции схожи: сопоставьте некоторые условия и примените некоторые вещи.

Использование matchMedia()

Чтобы определить, соответствует ли документ строке медиа-запроса в JavaScript, мы используем метод matchMedia(). Несмотря на то, что он официально является частью спецификации модуля представления объектной модели CSS, находящейся в статусе рабочего проекта, его поддержка браузером великолепна даже в Internet Explorer 10 с глобальным охватом 98,6%.

Эти данные о поддержке браузером взяты с Caniuse, где есть более подробная информация. Число указывает, что браузер поддерживает эту функцию в этой версии и выше.

Использование почти идентично медиа-запросам CSS. Мы передаем строку медиа-запроса в matchMedia(), а затем проверяем свойство .matches.

// Define the query
const mediaQuery = window.matchMedia('(min-width: 768px)')

Определенный медиа-запрос вернет объект MediaQueryList. Это объект, в котором хранится информация о медиа-запросе и ключевом свойстве, которое нам нужно для .matches. Это логическое свойство только для чтения, которое возвращается true, если документ соответствует медиа-запросу.

// Create a media condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia('(min-width: 768px)')


// Check if the media query is true
if (mediaQuery.matches) { // Then trigger an alert alert('Media Query Matched!')
}

Это базовое использование для сопоставления условий медиа в JavaScript. Мы создаем условие соответствия (matchMedia()), которое возвращает объект (MediaQueryList), проверяем его (.matches), а затем делаем что-нибудь, если условие истинно. Почти как в CSS!

Но это еще не все. Например, если бы мы изменили размер окна ниже целевого размера, ничего не изменится так, как это будет с CSS, прямо из коробки. Это потому, что .matches идеально подходит для одноразовых мгновенных проверок, но не может постоянно проверять наличие изменений. Значит, нам нужно…

Прослушивать изменения

MediaQueryList имеет метод addListener() (и последующие removeListener()), который принимает функцию обратного вызова (представленную событием .onchange), которая вызывается при изменении статуса медиа-запроса. Другими словами, мы можем запускать дополнительные функции при изменении условий, что позволяет нам «реагировать» на обновленные условия.

// Create a condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia('(min-width: 768px)')


function handleTabletChange(e) { // Check if the media query is true if (e.matches) { // Then log the following message to the console console.log('Media Query Matched!') }
}


// Register event listener
mediaQuery.addListener(handleTabletChange) // Initial check
handleTabletChange(mediaQuery)

И matchMedia() и MediaQueryList дают нам такую же возможность не только проверять условия медиа, которые предоставляет CSS, но и активно реагировать на обновленные условия.

Когда вы регистрируете прослушиватель событий addListener(), он изначально не срабатывает. Нам нужно вызвать функцию обработчика событий вручную и передать медиа-запрос в качестве аргумента.

Старый способ сделать это

Ради контекста — и немного ностальгии — я хотел бы осветить старый, но все еще популярный способ выполнения «медиа-запросов» в JavaScript (и, да, эти цитаты здесь важны). Самый распространенный подход — привязать прослушиватель событий resize, который проверяет window.innerWidth или window.innerHeight.

Вы все еще увидите что-то подобное в реальной среде:

function checkMediaQuery() { // If the inner width of the window is greater then 768px if (window.innerWidth > 768) { // Then log this message to the console console.log('Media Query Matched!') }
}


// Add a listener for when the window resizes
window.addEventListener('resize', checkMediaQuery);

Поскольку событие изменения размера вызывается при каждом изменении размера браузера, это затратная операция! Глядя на влияние пустой страницы на производительность, мы можем увидеть разницу.

Работа с медиа-запросами JavaScript

Это на 157% больше скриптов!

Еще более простой способ увидеть разницу — с помощью журнала консоли.

Работа с медиа-запросами JavaScript

Это 208 событий изменения размера по сравнению с шестью совпадающими событиями медиа.

Даже если не обращать внимания на проблемы с производительностью, изменение размера является ограничительным в том смысле, что не позволяет нам писать расширенные медиа-запросы для таких вещей, как печать и ориентация. Таким образом, хотя оно и имитирует поведение «медиа-запроса», позволяя нам проверять ширину области просмотра, он неспособно сопоставить большую часть чего-либо еще — и мы знаем, что настоящие медиа-запросы способны на гораздо большее.

Заключение

Мы рассмотрели медиа-запросы в JavaScript! Мы исследовали, как matchMedia() позволяет нам определять условия медиа, и исследовали объект MediaQueryList, который позволяет нам выполнять одноразовые (.matches) и постоянные ( addListener()) проверки на соответствие этим условиям, чтобы мы могли реагировать на изменения (.onchange), вызывая функции.

Мы также рассмотрели «старый» способ делать что-то, прослушивая событие resize в окне. Хотя он по-прежнему широко используется и является абсолютно законным способом реагирования на изменения размера window.innerWidth, он не может выполнять проверки расширенных состояний медиа.

В завершение статьи приведем полезный пример, который невозможно реализовать по-старому. С помощью медиа-запроса проверю, находится ли устройство пользователя в альбомной ориентации. Этот подход распространен при разработке игр HTML5, и его лучше всего просматривать на мобильном устройстве.

Автор: Marko Ilic

Источник: https://css-tricks.com

Редакция: Команда webformyself.