От автора: итак, вот ваша задача, возьметесь ли вы за нее: создать таблицу элементов. Каждый элемент должен занимать треть ширины экрана, а четвертый должен начинаться с новой строки, как при обтекании. Но определенный, особый элемент должен всегда отображаться в конце первой строки таблицы.
Если в таблице всего две ячейки, то наш особенный элемент должен занимать вторую, однако если их больше трех, то он будет занимать последнюю ячейку в первой строке таблицы.
Вы можете сразу предположить, что JavaScript тут подойдет лучше всего – просто сделать цикл по всем элементам, и если их больше трех, то применить необходимые стили. А что если я скажу, что вы могли бы сделать то же самое на чистом CSS?
Счетчик на чистом CSS
В последнее время я полностью погрузился в flexbox, преподавая его вместе с обтеканием контента с помощью свойства float. Данные методы мы использовали в верстке нашего маленького проекта HackerYou. И я обнаружил, что студенты быстро схватывают эту тему. В спецификации flexbox есть свойства, позволяющие нам изменять разметку новыми способами. Один из этих способов это свойство order, оно позволяет изменять визуальный порядок контента, не трогая при этом его разметку.
В совокупности с медиа запросами order крайне полезная вещь, нам открывается возможность изменять порядок контента в зависимости от размера окна браузера. И это заставило меня задуматься: Почему мы не можем изменять порядок элементов в зависимости от количества контента?
Количественные запросы
Идея, предложенная Lea Verou, André Luis и Heydon Pickering, заключается в том, что эти запросы подсчитывают число смежных элементов и применяют стили к ним, если набирается определенное их количество. Что если мы скомбинируем количественные запросы со свойством order, чтобы изменить порядок контента в зависимости от его же количества?
Способ на основе Flexbox
Flexbox или «Flexible Box Layout Module» это CSS спецификация, позволяющая гибко манипулировать контентом в ячейках сетки и легко подгонять размер ячеек под размеры родителя. Впервые представленный в 2009 году за все это время flexbox подвергся массе изменений. Тем не менее, он поддерживается во всех последних версиях браузеров кроме IE9+.
Одним из самых значимых изменений в flexbox стал синтаксис присвоения имен ассоциативных свойств и значений. Поскольку спецификация развивается на протяжении всего времени, производители браузеров будут использовать тот синтаксис, который доступен на данный момент времени. Поэтому для поддержки устаревших браузеров необходимо использовать вендорные префиксы.
Один из рекомендуемых инструментов для обеспечения кроссбраузерного CSS кода это Autoprefixer, как правило, он уже включен в препроцессоры, Gulp и Grunt файлы.
Как понять суть порядка
Прежде чем погрузиться в количественные запросы и разбираться в их работе, необходимо понять, как работать со свойством order. Сначала необходимо обернуть контент родительским элементом и добавить к этому блоку-обертке стили display:flex.
HTML код:
1 2 3 4 |
<div class="container"> <p class="itemOne">Hello</p> <p class="itemTwo">World!</p> </div> |
И CSS код:
1 2 3 |
.container { display: flex; } |
По умолчанию элементы выстраиваются в порядке, заданном разметкой. Все дочерние элементы в родительском flexbox имеют значение order равное 1.
Данное значение не имеет единицы измерения и просто сопоставляется с порядковым номером элемента относительно остальных элементов вокруг него. Но мы можем изменить значение отдельного элемента с помощью свойства order.
1 2 3 |
p.itemOne { order: 2; } |
В примере выше мы изменили порядок для строки p.itemOne на значение 2, и эта строка теперь отображается после p.itemTwo. Тем самым мы изменяем визуальное представление элементов для пользователя. Обратите внимание на то, что разметка осталась прежней.
Счетчики на CSS
Медиа запросы, да? Настолько отличный инструмент в CSS в отдельных случаях. Такими случаями могут выступать тип устройства, размер экрана, цвет и т.д. – довольно-таки мощная штука. Но данные запросы применяются только к устройству пользователя; нет определенного способа CSS, который вычисляет количество контента в элементе.
Если отнестись к делу творчески и внимательнее рассмотреть существующие CSS псевдоклассы, мы можем создать инструментарий для подсчета количества дочерних элементов и применения соответствующих стилей к ним. Например, давайте воспользуемся обычным нумерованным списком:
1 2 3 4 5 6 7 8 |
<ul class="ellist"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li class="target">6</li> </ul> |
Вся магия по подсчету смежных элементов в коде ниже. Данные стили применяются к элементам, если их 4 или более.
1 2 3 4 |
ul.ellist li:nth-last-child(n+4) ~ li, ul.ellist li:nth-last-child(n+4):first-child { // тут прописаны стили } |
Стоп, не может быть!
Точно, это именно тот селектор. На русский язык это можно перевести как: «Когда дочерних элементов в списке 4 или более, получаем элементы другого списка вместе с первым дочерним элементом». Разберем подробнее. Для начала начнем со счетчика:
1 2 3 |
ul.ellist li:nth-last-child(n+4) { // Стили! } |
Переводится как «Дойди до последнего дочернего элемента и отсчитай назад 4 элемента». Стили применяются к четвертому элементу и всем, кто перед ним. (Отсчет ведется в обратном порядке!). Продолжим и поэкспериментируем с кодом, назначим селекторам другие значения.
Вот такие счетчики. Если смежных элементов меньше четырех, то стили не будут применены. Теперь мы изменим данный селектор, чтобы он выбирал все li из списка с помощью универсального селектора.
1 2 3 |
ul.ellist li:nth-last-child(n+4) ~ li { // Стили! } |
Вся проблема в том, что в таком случае не выбирается первый дочерний элемент. Мы можем добавить еще одни селектор:
1 2 3 4 |
ul.ellist li:nth-last-child(n+4) ~ li, ul.ellist li:nth-last-child(n+4):first-child { // Стили! } |
Конечно, мы можем сделать селектор более независимым, просто указав родительский элемент и позволив ему выбрать все дочерние элементы. Мы сделаем это с помощью селектора *.
1 2 3 4 |
element > *:nth-last-child(n+4) ~ *, element *:nth-last-child(n+4):first-child { // Стили! } |
Порядок на основе количества элементов
Теперь, когда мы разобрались, как подсчитывать элементы с помощью CSS и как использовать flexbox для изменения порядка контента, давайте объединим их и создадим инструментарий для упорядочивания элементов на основе их количества.
Еще раз, мы хотим сделать последний элемент третьим в списке (т.е. чтобы тот отображался последним в первой строке), если элементов больше трех.
Добавим пару стилей. К контейнеру-родителю добавим display:flex, это свойство позволит нам применить свойство order к его дочерним элементам. Кроме того, чтобы отличать элементы друг от друга мы добавим пару стилей по умолчанию к элементу .target.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
ul.ellist { margin: 20px 0; padding: 0; list-style: none; display: flex; flex-flow: row wrap; } ul.ellist > * { border: 10px solid #27ae60; text-align: center; flex: 1 0 calc(33.33% - 20px); padding: 20px; margin: 10px; } .target { color: white; background: #2980b9; border: 10px solid #3498db; } ul.ellist, ul.ellist > * { box-sizing: border-box; } ul.ellist { margin: 20px 0; padding: 0; list-style: none; display: flex; flex-flow: row wrap; } ul.ellist > * { border: 10px solid #27ae60; text-align: center; flex: 1 0 calc(33.33% - 20px); padding: 20px; margin: 10px; } ul.ellist .target { color: white; background: #2980b9; border: 10px solid #3498db; } |
Теперь, когда у нас есть базовые стили, мы можем создать логику для упорядочивания элементов определенным образом. Еще раз, по умолчанию все элементы имеют значение order равное 1 и отображаются в том порядке, в котором они прописаны в верстке. При помощи количественных запросов мы можем подсчитать количество элементов. Больше ли их чем три элемента.
1 2 3 |
ul.ellist > *:nth-last-child(n+3) { // Стили! } |
Затем, если условие выполнилось, мы можем выбрать необходимый нам элемент .target. Мы применили значение -1 для свойства order, теперь этот элемент отобразится в начале списка.
1 2 3 |
ul.ellist > *:nth-last-child(n+3) ~ .target { order: -1; } |
Вуаля! Мы только что стилизовали элемент, подсчитав количество смежных с ним элементов. С помощью этого кода мы можем ставить один элемент перед другим. А что если необходимо поставить элемент между двумя?
Немного логических размышлений
Перед нами задача с тремя неизвестными:
По умолчанию все элементы списка имеют значение order:1. Нам необходимо сделать так, чтобы это значение не изменилось у первых двух элементов.
Наш особенный элемент должен располагаться в конце первой строки. Значит, нам необходимо увеличить значение order и сделать его больше, чем у первых двух элементов, т.е. 2.
Все элементы, начиная с третьего, должны иметь значение order выше, чем у нашего целевого элемента. Значит, для остальных элементов значение order будет равно 3.
Как вам? Так как все элементы по умолчанию имеют значение 1, то нам не нужно его объявлять. Давайте установим нашему особенному элементу значение order:2 с помощью количественных запросов, эффективно поместив его после всех.
1 2 3 |
ul.ellist > *:nth-last-child(n+3) ~ .target { order: 2; } |
Затем, применив другой количественный запрос nth-child(), мы начнем считать элементы с начала списка, а не с конца. Так как раньше мы прописали запрос для .target, то этот элемент будет проигнорирован, однако все остальные третий и следующие за ним элементы поменяют свой порядок.
1 2 3 4 5 6 |
ul.ellist > *:nth-last-child(n+3) ~ .target { order: 2; } ul.ellist > *:nth-child(n+3) { order: 3; } |
Давайте разберем еще раз!
Мы ведем отсчет с конца списка по дочерним элементам на предмет совпадения с определенным количеством. Если количество элементов удовлетворяет условию, мы применяем стили к выбранным нами элементам. Затем мы ведем отсчет с начала списка и применяем стили ко всем элементам за определенным, особенным элементом. Самая главная особенность в том, что если мы удалим элементы, то целевой элемент останется на нужном нам месте.
1 2 3 4 5 6 |
<ul class="ellist"> <li>1</li> <li>2</li> <li>3</li> <li class="target">4</li> </ul> |
Конечная задача
Первой мыслью, когда я ставил перед собой эту задачу, было использовать языки программирования. Так как я использовал WordPress, я мог бы изменить цикл и, вычислив количество элементов, вставить в нужном месте целевой элемент.
Но так как я создавал сайт для школы по front-end разработке, я хотел сделать это на чистом CSS и раскрыть возможности свойства flexbox order. Ниже пример конечного результата, выполненного целиком на CSS.
Применение на практике
SASS
Концепция количественных запросов достаточно нова, и в связи с этим написание селекторов может быть слегка затруднительно. Тем не менее, сообщество разработчиков горячо приняло эту концепцию и уже пишет Sass mixin’ы, которые помогут нам делать запросы намного эффективнее. Такие библиотеки, как, например, библиотека Quantity Queries Mixin от Daniel Guillan позволяет нам писать медиа запросы, как простое подключение.
1 2 |
@include at-least($count) { ... } @include between($first, $last) { ... } |
Также данная концепция и мощь, скрытая в ней, объясняются во множестве статей. James Steinbach написал замечательную статью «Применение Sass в количественных запросах».
POSTCSS
PostCSS – новый инструмент для изменения CSS с помощью JavaScript. На данный момент флора и фауна PostCSS насчитывает множество разработанных сообществом плагинов, включая Quantity Query плагин. Данный плагин позволяет настраивать псевдоклассы на значения в определенном диапазоне.
1 2 |
p:at-least(4) { ... } p:between(4,6) { ... } |
Поддержка браузерами
На данный момент CSS псевдоклассы и flexbox превосходно работают в современных браузерах. Если ваш проект учитывает пользователей не ниже чем IE10, вы можете использовать количественные запросы вместе с flexbox.
Где использовать
При создании веб-сайтов мы часто прибегаем к языкам программирования, которые предоставляют нам возможность подсчитывать и модифицировать наш контент, если это необходимо. Однако CSS стал намного лучше, и мы ушли от языков программирования в сторону продвинутых свойств CSS.
Примером может послужить CSS анимация. Что раньше было доступно только на Flash или JavaScript теперь полностью доступно в CSS, в языке описания внешнего вида контента.
Количественное упорядочивание позволяет нам удалить куски кода с циклами, которые отвечали за подсчет и вставку элементов в правильном порядке, приводя наш контент к семантически чистому виду.
Великолепным примером пользы количественного упорядочивания будет сайт новостей с рекламой. В разметке все статьи идут по порядку, а блок рекламы расположен в самом конце. С точки зрения доступности такой подход предоставляет нам непрерывный поток контента. При использовании количественного упорядочивания реклама может быть размещена в любом месте и только на уровне представления.
С одной стороны, количественный порядок может быть использован для изменения визуального представления элементов, повышая доступность, с другой стороны, это может привести к негативному опыту у пользователей. Принимайте к сведению то, как контент будет отображаться на экране и на доступных устройствах.
Заключение
Количественные запросы и упорядочивание – продвинутые концепции и могут напугать новичков. Тем не менее, чем дальше мы отодвигаем стилизацию представления от языков программирования в сторону CSS, тем все больше мы будем использовать данный инструментарий. Все большее количество членов нашего сообщества открывают для себя контент запросы, и теперь мы можем использовать количественные запросы для изменения порядка контента просто с помощью счетчиков.
Автор: Drew Minns
Источник: //www.smashingmagazine.com/
Редакция: Команда webformyself.