Медиа-запросы — это не выход: Polyfill-скрипт для element-запросов

Дата публикации:Июнь 27, 2013

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

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

Что представляет собой отзывчивый веб-дизайн?

Отзывчивый веб-дизайн не ограничивается набором каких-то технологий; скорее, это принципиально иной подход к проектированию и созданию веб-сайтов. Я, как и многие другие, воспринял слова Ethan об отзывчивом веб-дизайне слишком буквально, упустив суть сказанного:

«Жидкие сетки, гибкие изображения и медиа-запросы – это три технических компонента отзывчивого дизайна, однако он также требует иного образа мыслей»

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

Модульный дизайн

Когда я изучал, как медиа-запросы могут использоваться в отзывчивом веб-дизайне, я был потрясен открывающими перспективами. Однако это было еще до того, как я узнал о существующих ограничениях. Медиа-запросы прекрасно подходят для адаптации разметки к различным размерам экрана, но они просто ужасны для создания модульного дизайна. Модульные CSS-стили уже сами по себе достаточно сложны, и медиа-запросы нисколько их не упрощают. Реальная модульная разметка должна отзываться не только на размер области просмотра, но и на размеры контейнеров. Однако медиа-запросы основываются на области просмотра, а не на контейнере элемента. Можно возлагать некоторые надежды на новый стандарт CSS, маячащий на горизонте, который является пока еще рабочим черновиком W3C, и который позволит нарушать каскадное наследование и сбрасывать элемент к его значениям по умолчанию. Однако как быть с медиа-запросами?

Хак @media

Веб-разработчики – настоящие гуру в том, чтобы взять что-либо, созданное для одной цели, и использовать это для выполнения совершенно других действий. История Web насыщена такими примерами, и медиа-запросы – не исключение. Стоит поблагодарить Ian Storm Taylor за описание похожих мыслей в статье: «Media Queries Are a Hack». Хаки играют важную роль в Сети, поскольку они позволяют реализовать необходимую функциональность при отсутствии должной поддержки, а также позволяют обеспечить работу кода в устаревших браузерах. Как утверждает W3C, «с помощью медиа-запросов можно подгонять представление к определенному диапазону устройств вывода, не изменяя сам контент». Ключевое слово здесь – «можно», однако если вы можете что-то сделать, это не означает, что вы должны это делать… Но разве у нас есть другой выбор?

Element-запрос

Давайте познакомимся с element-запросом. Что представляет собой этот вид запросов? Element-запрос напоминает медиа-запрос: если условие выполняется, то применяются некоторые CSS-стили. Условия для element-запроса (такие как min-width, max-width, min-height и max-height) зависят от элементов, а не от браузера. К сожалению, CSS пока еще не поддерживает element-запросы, однако это не должно останавливать нас от каких-либо действий.

Абстрактный пример

Рассмотрим следующий пример, в котором навигационное меню должно стать видимым, когда оно достигнет минимальной ширины в 500 пикселей (представляет один из многих возможных синтаксисов):

nav (min-width: 500px) {
	display: block;
}

Сравните это с медиа-запросом, в котором видимость навигационного меню зависит от ширины области просмотра, причем важно учитывать поля (padding) и другие объявления родительских элементов:

@media all and (min-width: 520px) {
	nav {
		display: block;
	}
}

Теперь давайте предположим, что нам требуется создать модульный компонент, который должен быть помещен в контейнеры различных размеров на отдельной странице. Один из существующих подходов заключается в добавлении различных тематических классов (как, к примеру, .module—large) для вызова CSS в медиа-запросах. Однако такой подход лишь все усложняет, требуя, чтобы модули знали, как их родитель будет реагировать на различную ширину области просмотра.

Проблемы: неверные и зацикленные условия

Существуют отдельные случаи, когда CSS-стили element-запроса приводят к появлению неверного запроса или вызывают рекурсию. Хотелось бы надеяться, что браузер будет в состоянии обнаружить такие условия и соответствующим образом ответить на них.

Рассмотрим следующие примеры.

Как только элемент достигнет ширины в 500 пикселей, он будет изменен до 200 пикселей, после чего правило больше не будет применяться:

.element (min-width: 500px) {
	width: 200px;
}

Как только ширина элемента достигнет 31.250 em, его размер шрифта будет уменьшен, что изменяет определение модуля em:

.element (min-width: 31.250em) {
	font-size: 0.75em;
}

Как только ширина контейнера достигнет 450 пикселей, размер его дочерних элементов меняется до 400 пикселей, что ужмет и сам контейнер:

.container { float: left; }
.child { width: 500px; }
.container (min-width: 450px) > .child {
	width: 400px;
}

Можно привести много других похожих примеров, однако вы видите: element-запросы не так просты, как мы думали.

Polyfill-код для element-запросов

Element-запросы выглядят крутыми, однако у них имеются и некоторые существенные проблемы. Чтобы решить их, я написал тестовый polyfill-код. Этот код позволил мне понять, как браузер будет реагировать на различные условия. Как я выяснил впоследствии, polyfill-код мог оказаться важным для всего веб-сообщества, а некоторые разработчики вполне могли использовать element-запросы уже сегодня.

ElementQuery polyfill-скрипт доступен на GitHub – вы можете использовать его, изменять и распространять.

Синтаксис селекторов

Синтаксис, используемый в предыдущих примерах, имеет некоторые ограничения, потому я решил обновить polyfill-код для поддержки синтаксиса селекторов атрибутов. Селектор атрибутов (~=) проверяет, содержится ли значение в разделенном пробелами списке (поддерживается современными браузерами старше Internet Explorer 6).

Следующие примеры демонстрируют CSS-правила с синтаксисом, необходимым для elementQuery polyfill-скрипта.

Правило для одного условия:

header[min-width~="500px"] {
	background-color: #eee;
}

Правило для нескольких условий:

header[min-width~="500px"][max-width~="800px"] {
	background-color: #eee;
}

Правило для родительского элемента:

header[min-width~="31.250em"] nav {
	clear: both;
}

Как это работает

К сожалению, elementQuery polyfill-скрипт требует подключения JavaScript и механизма селекторов Sizzle (который встроен в jQuery). Когда DOM готов, elementQuery сканирует набор document.styleSheets для каждого CSS-правила, которое использует elementQuery. Найдя совпадение, он извлекает следующую информацию:

  • Селектор (к примеру, header, ul > li.class)
  • Тип запроса (min-width, max-width, min-height, max-height)
  • Значение запроса (500px, 31.250em)

Затем elementQuery использует эту информацию для добавления или удаления атрибутов у тех элементов, которые соответствуют указанному селектору и условию запроса.

Расширенная поддержка

Большинство браузеров, за исключением Internet Explorer, не дают доступа к контенту междоменных стилевых таблиц, что приводит к проблемам, когда CSS-файлы берутся из CDN. В дополнение ко всему, парсинг стилевых таблиц занимает некоторое время (правда, не такое длительное). Таким образом, я создал две ветви для elementQuery: master и prod. Master-ветвь включает в себя код, позволяющий извлечь необходимую для elementQuery информацию (селектор, тип запроса, значение запроса) – также эта версия имеет функцию selectors() для экспорта информации. Prod-ветвь запрашивает информацию, которая будет объявлена в Javascript, что позволяет обойти проблему вызова междоменных файлов, а также сокращает время, требуемое на парсинг стилевых таблиц.

Ниже представлен пример того, как выполнить экспорт elementQuery-информации в master-ветви:

console.log(JSON.stringify(elementQuery.selectors()));

А также пример того, как выполнить экспорт elementQuery-информации в prod-ветви:

elementQuery({"header":{"min-width":["500px","31.250em"],"max-width":["800px"]}});

Рабочие примеры

Я разместил несколько рабочих примеров на CodePen (используя master-ветвь), с которыми вы можете экспериментировать и которые вы можете форкать. Мне хотелось бы увидеть результаты работы других людей (которые, возможно, будут круче моих). Обязательно тегируйте их #elementquery, чтобы другие могли найти их.

  • Grid: вложенные элементы
  • Menu: elementQuery, основанный на ширине
  • Blockquote: elementQuery, основанный на высоте

Будьте креативными

Я писал эту статью не для того, чтобы быстро перейти на element-запросы. Я хотел подтолкнуть людей к поиску решения проблем, которые ограничивают нашу среду. Давайте продолжим обсуждать этот путь и сделаем Сеть самым лучшим пространством! Идите и сделайте что-нибудь крутое!

Источник: coding.smashingmagazine.com/2013/06/25/media-queries-are-not-the-answer-element-query-polyfill/

Поделиться

2 комментария

  1. Дуо says:

    Круть. Хорошо видно в какую сторону народ гребет и что будет завтра. Спасибо за статью.

  2. Спасибо за перевод статьи!

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

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