Снимаем вершки с медиа-запросов
Оригинал статьи: Cutting the Mustard with CSS Media Queries
Оглавление:- Делаем лучше
- Как это работает
- “Но это же хак!”
- Где это не работает
- Как насчет загрузки скриптов?
- Итоговый код
- Благодарности
Снимать вершки (Cutting the Mustard) это термин Тома Маслена с BBC. Суть его метода состоит в использовании JavaScript для проверки возможностей браузера перед дальнейшей загрузкой CSS и JavaScript, чтобы улучшить ощущения пользователей и загрузить лишь базовые файлы, если возможности браузера невелики.
Этот метод вызвал огромный интерес, появились такие статьи как Migrating to Flexbox by Cutting the Mustard, Server Side Mustard Cut и Progressive Enhancement
Делаем лучше
Мой опыт подсказывает, что даже самый базовый функционал может быть проблемным для самых старых браузеров, поэтому я хотел бы не передавать в старые браузеры никаких стилей, а по возможности оставить им только чистый HTML.
Конечно, очевидное решение использовать JavaScript для условной загрузки стилей решает задачу, но при этом современные браузеры, в которых JavaScript отключен пользователями, будут получать страницы без стилей. Поэтому я искал возможность условной загрузки CSS без использования скриптов и в итоге нашел старый пост Крейга Баклера. После нескольких экспериментов я пришел к следующему коду:
<!-- in the <head> -->
<link rel="stylesheet" href="css/your-stylesheet.css"
media="only screen and (min-resolution: 0.1dpcm)">
<link rel="stylesheet" href="css/your-stylesheet.css"
media="only screen and (-webkit-min-device-pixel-ratio:0)
and (min-color-index:0)">
Давайте проверим, к чему это приведет.
Как это работает
Первый элемент <link>
с меди-запросом загружает стили только в следующих браузерах:
- IE 9+
- FF 8+
- Opera 12
- Chrome 29+
- Android 4.4+
А второй элемент <link>
с меди-запросом загружает стили для такого набора браузеров:
- Chrome 29+
- Opera 16+
- Safari 6.1+
- iOS 7+
- Android 4.4+
При совмещении этих запросов, данная техника позволяет не загружать стили, если браузер не относится к следующему списку:
- IE 9+
- FF 8+
- Opera 12, 16+
- Chrome 29+
- Safari 6.1+
- iOS 7+
- Android 4.4+
Примечание: стили загружаются только один раз, независимо от количества элементов <link>
.
Можно скомбинировать медиа-запросы в один элемент <link>
, разделив их с помощью запятой, вот так:
<link rel="stylesheet" href="css/your-stylesheet.css"
media="only screen and (min-resolution: 0.1dpcm),
only screen and (-webkit-min-device-pixel-ratio:0)
and (min-color-index:0)">
Однако, я предпочитаю разделять их, чтобы было проще понимать, что происходит.
Так что если вы структурируете вашу разметку для семантичности и доступности, старые браузеры смогут по прежнему воспроизводить ваш нестилизованный контент в чистом HTML.
Это, конечно, очень субъективно, но на мой взгляд, все, кто использует эти браузеры, заслуживают того, чтобы получить то, что им нужно. Вполне вероятно, что передавая в старые браузеры современный CSS, вы можете поломать некоторые вещи и страница будет совершенно непригодной. Наш метод, как минимум, ничего не испортит. И более важно то, что вам не надо ничего тестировать в этих браузерах. Вы разобрались с ними. По крайней мере, в теории.
Конечно, список поддерживаемых вами браузером может меняться и в этом еще одно преимущество этой техники — вы можете расширять список браузеров. Например, вам надо добавить поддержку ИЕ8 — вы просто добавляете условный комментарий, загружающий стили в браузер:
<!--[if IE 8]>
<link rel="stylesheet" href="css/your-stylesheet.css">
<![endif]-->
А если вам надо поддерживать старые устройства на основе WebKit с соотношением пикселей больше 1, вы просто добавите еще один элемент <link>
с целевым меди-запросом:
<link rel="stylesheet" href="css/your-stylesheet.css"
media="only screen and (-webkit-min-device-pixel-ratio:1.1)">
Сам по себе, этот код будет загружать стили только для Android 2.2+ (у меня не было возможности протестировать более ранние версии), но если он подключен в дополнение к другим элементам <link>
, он просто добавляет эту группу браузеров.
Также возможно, что вы сможете изменить медиа-запросы основного элемента <link>
, чтобы кастомизировать выборку поддерживаемых браузеров. Однако, тестирование этого будет достаточно сложным, так что будьте осторожны.
“Но это же хак!”
Да, наверное, хотя это зависит от вашего подхода. Медиа-запросы созданы для тестирования возможностей браузера перед применением CSS и в данном случае мы используем их именно с этой целью, хотя и косвенно.
Опять-таки, независимо от того, хак это или нет, я доволен этой техникой и она работает во всех проведенных мной тестах, в ближайшее время я планирую использовать ее на рабочем сайте.
Где это не работает
Я много тестировал этот метод и столкнулся только с одним случаем, когда он не работает так, как ожидалось. UC Browser в Android 4.4 не реагирует на медиа-запрос. Насколько я понимаю,это происходит в силу того, что этот браузер использует старую версию движка Webkit.
Если вам нужна поддержка этого браузера, вы можете заставить его загрузить стили с помощью JavaScript:
if (navigator.userAgent.indexOf('UCBrowser') > -1) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'css/your-stylesheet.css';
document.getElementsByTagName('head')[0].appendChild(link);
}
В качестве бонуса добавлю, что нет никакого способа отключить JavaScript в Android UC Browser, а значит этот код загрузит стили в любом случае, кроме, конечно, сбоев в сети и прочего форс-мажора.
Конечно, если вы найдете более специфичные браузеры, которые вам надо поддерживать, вы всегда может добавить подобный код, но я рекомендую делать это только при необходимости, ведь иначе мы утрачиваем смысл техники условной загрузки без JavaScript.
Я создал тестовую страницу, чтобы проверить базовый метод в различных браузерах. Если вы найдете браузеры, в которых метод работает не так, как ожидается или если вы найдете другие медиа-запросы, добавляющие поддержку новых браузеров или групп браузеров, пожалуйста оставьте там комментарий или создайте issue в GitHub репозитории.
Как насчет загрузки скриптов?
ОК, вы используете CSS для определения списка поддерживаемых браузеров, а, значит, скорее всего вам понадобиться загружать скрипты только для тех же самых браузеров. И как это сделать?
Этого можно достичь несколькими способами, но самый простой работает примерно так:
- Добавьте к своим стилям безвредное CSS свойство к элементу
body
со значением, отличным от дефолтного. - Используйте JavaScript метод
getComputedStyle
на элементеbody
, чтобы выяснить применено ли свойство или нет. - Если свойство задано, то вы можете запускать остальной JavaScript.
Например, свойство clear
по умолчанию задано как none
. Установка его в значение both
для body
не вызовет каких-либо видимых последствий (но если это внезапно произойдет, то вы сможете взять иное свойство). В CSS эта декларация будет выглядеть так:
body {
clear: both;
}
А вот и скрипт для проверки:
var is_supported = false
, val = '';
if (window.getComputedStyle) {
val = window.getComputedStyle(document.body, null).getPropertyValue('clear');
} else if (document.body.currentStyle) {
val = document.body.currentStyle.clear;
}
if (val == 'both') {
is_supported = true;
}
Итоговый код
Независимо от того, считаете ли вы это хаком или нет, я всего лишь показываю многоразовый способ выявления относительно современных браузеров и предотвращения загрузки в старые браузеры современных скриптов и стилей, заставляя их отображать лишь чистый HTML. Необходимый код невелик и позволяет вам сконцентрироваться на создании современных страниц для современных браузеров с использованием последних технологий.
Возможно, что вам не понадобиться все, о чем я написал, но вот на всякий случай полный код:
<head>
<link rel="stylesheet" href="mq-test.css"
media="only screen and (min-resolution: 0.1dpcm)">
<link rel="stylesheet" href="mq-test.css"
media="only screen and (-webkit-min-device-pixel-ratio:0)
and (min-color-index:0)">
<link rel="stylesheet" href="mq-test.css"
media="only screen and (-webkit-min-device-pixel-ratio:1.1)">
<!--[if IE 8]>
<link rel="stylesheet" href="mq-test.css">
<![endif]-->
<script>
if (navigator.userAgent.indexOf('UCBrowser') > -1) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'mq-test.css';
document.getElementsByTagName('head')[0].appendChild(link);
}
</script>
</head>
<body>
<!-- content here... -->
<script>
var is_supported = false
, val = '';
if (window.getComputedStyle) {
val = window.getComputedStyle(document.body, null).getPropertyValue('clear');
} else if (document.body.currentStyle) {
val = document.body.currentStyle.clear;
}
if (val == 'both') {
is_supported = true;
}
if (is_supported) {
// Load or run JavaScript for supported browsers here.
}
</script>
</body>
Я также сделал еще один тест со всеми расширениями.
Благодарности
Я бы не сделал это без использования BrowserStack, Can I Use, работы всей команды Browserhacks и Джеффа Клейтона, поэтому спасибо всем, кто участвовал и пожалуйста дайте мне знать, если у вас появятся какие-либо идеи или отзывы.