Технически, CSS не имеет возможности считать родных элементов. Тем не менее, Хейдон недавно показал нам то, что он называет количественными запросами : умная комбинация элементов :nth-last-child()
:first-child
и ~
для элементов стиля, основанных на количестве элементов в одном родителе. Взгляните на эту статью A List Apart и мою интерактивную демонстрацию по CodePen, чтобы понять, что происходит.
Как работают количественные запросы
Вот основная суть количественных запросов: используя вместе :nth-last-child($n)
и :first-child
, вы можете идентифицировать элемент, который является определенным числом от конца его братьев и сестер и первого потомка его родителя. , Число с конца говорит нам количество братьев и сестер. Например, селектор :nth-last-child(6):first-child
выберет только первый элемент в группе из 6 элементов. Если вы выберете этот элемент и все последующие элементы, используя ~
, вы можете написать собственный CSS для группы элементов в зависимости от их количества.
:nth-last-child(6):first-child, :nth-last-child(6):first-child ~ * { // unique CSS for elements when there are 6 of these }
Этот код позволяет вам ориентироваться на конкретное количество, но, скажем, вы хотите изменить стили, когда есть «по крайней мере» или «меньше» определенного числа. Вы можете сделать это, изменив значение :nth-last-child()
. Чтобы нацелить группы по крайней мере из 6 элементов, используйте :nth-last-child(n + 6):first-child
и для групп менее 6, используйте :nth-last-child(-n + 6):first-child
,
Написание количественных запросов с помощью Sass
Приведенный выше код великолепен, но давайте используем Sass, чтобы сделать его более сухим: нам не нужно переписывать весь синтаксис везде, где мы хотим использовать эту технику. Мы начнем с одного миксина, который принимает два параметра: число и сравнение:
@mixin quantity-query( $number, $comparison: 'equal' ) { // generate code in here }
Здесь $number
будет целым числом: то, что вы хотите, чтобы CSS считал. $comparison
будет одним из трех значений: greater
, less
или equal
. Мы определим equal
как значение $comparison
по умолчанию, если ничего не указано.
Первое, что мы сделаем внутри нашего миксина, это установим значение :nth-last-child()
на основе $number
и $comparison
:
@if index( ('greater' 'more' '>'), $comparison ) { $nth: 'n + #{$number}'; } @else if index( ('less' 'fewer' '<'), $comparison ) { $nth: '-n + #{$number}'; } @else if index( ('equal' 'same' '='), $comparison ) { $nth: $number; } @else { @warn "Sorry, that's an invalid $comparison value." }
У нас есть небольшая свобода: вы можете указать « greater
, « more
или '>'
для $comparison
превышающего $comparison
. Вы можете указать less
, fewer
или '< '
для $comparison
меньше чем $comparison
. Вы можете указать equal
, same
или '='
для $comparison
с равным $comparison
. Примечание: если вы используете символы, обязательно заключите их в кавычки.
На основе значения $comparison
значение :nth-last-child()
устанавливается в виде строки. Мы используем интерполяцию Sass внутри этой строки ( #{$variable}
), потому что мы хотим, чтобы число было частью строки, а не частью уравнения.
После того, как мы получим правильное значение для :nth-last-child()
, мы выведем наши селекторы:
&:nth-last-child(#{$nth}):first-child { &, & ~ * { @content; } }
Символ &
в передней части означает, что мы прикрепляем псевдоселекторы к селектору, в котором вы используете этот миксин. Если вы поместите его в блок .item {}
, вы получите .item:nth-last-child…
. Внутри селектора для первого элемента есть еще один &
за которым следует & ~ *
: это применяет стили к первому элементу группы и всем ее родным элементам. Примечание: я знаю, что не всем нравятся *
селекторы, но для защиты вложенности Sass это самый безопасный вариант для нас здесь.
@content;
Директива просто повторяет любой CSS, который вы пишете в миксине, когда вы его используете.
Бонус Mixins для скорости!
Мы можем использовать этот миксин просто так, как он есть:
.menu-item { @include quantity-query(5, greater) { color: blue; } }
… и мы получим:
.menu-item:nth-last-child(n+5):first-child, .menu-item:nth-last-child(n+5):first-child ~ * { color: blue; }
Но мы можем сделать это лучше с помощью псевдонимов:
@mixin qq-equal( $number ) { @include quantity-query( $number, equal ) { @content; } } @mixin qq-greater( $number ) { @include quantity-query( $number, greater ) { @content; } } @mixin qq-less( $number ) { @include quantity-query( $number, less ) { @content; } }
Теперь вы можете использовать уникальный миксин для каждого значения сравнения, если это улучшит ваш рабочий процесс.
Помимо меню
Пока что большинство демонстраций этой функции — меню. Но эта техника может сделать намного больше! Вот пример реального использования, которое мне удалось решить с помощью количественных запросов.
Недавно дизайнер дал мне комп для страницы, которая отображала четыре категории контента: каждая категория имела заголовок и список ссылок на отдельные части контента. Наиболее разумная разметка для этого контента:
<div class="content-category"> <h3 class="content-category-title">Title</h3> <ul class="content-category-list"> <li class="content-category-list-item">Item</li> </ul> </div> [*4]
На рабочих столах эти категории размещены в столбцах. Это достаточно просто. Тем не менее, дизайнер увидел другой макет для небольших экранов: все 4 заголовка должны быть вверху страницы и действовать как переключатели, чтобы показывать / скрывать элементы в своих списках под всеми заголовками. На левой стороне следующего изображения вы можете увидеть макет «рабочего стола»; на правой стороне разделителя вы можете увидеть «мобильную» раскладку:
Скорее всего, здесь можно было бы создать второй контейнер с копиями заголовков и использовать эти элементы для переключения триггеров на маленьких экранах, а затем скрыть его на больших экранах. Но с другой стороны, мы могли бы сохранить хорошую разметку, которую я описал выше, и использовать количественные запросы, чтобы исправить наш макет. Вот как я это сделал:
- На маленьких экранах размещайте заголовки абсолютно; позиционируйте их статически после большей точки останова.
- Используйте количественные запросы, чтобы определить, как далеко от вершины должен располагаться список на маленьких экранах.
Хотя соревнования начинались с 4 категорий, я всегда подозреваю, что в будущем клиент может захотеть 3, 5 или 7 категорий. Чтобы подготовиться к этим случаям, я дважды использовал цикл @for
в этом коде. Во-первых, я перебрал от 1 до $max
( $max
в настоящее время установлено на 7, но может быть изменено для учета более высоких пределов в будущем). Цикл внутри .content-category
(строка 55) вызывает mixin qq-equal()
и создает необходимые отступы в верхней части каждого списка на основе количества категорий. Цикл внутри .content-category-title
(строка 93) не использует количественный запрос; он просто устанавливает top
позицию каждого заголовка в зависимости от того, к какой категории он относится ( :nth-child()
).
Вот демоверсия из CodePen:
Вывод
С парой циклов @for
и нашими @for
количественных запросов мы смогли сохранить разумную семантическую разметку и создать расширенный адаптивный макет для этого дизайна. Не стесняйтесь брать миксины из этой истории Сассмайстера и делиться своим творческим запросом количества в комментариях!