Неизвестно полезный CSS. Часть 7

в 9:01, , рубрики: css, ruvds_статьи, sm909_unknown_css, web-разработка, верстка, лайфхаки
Неизвестно полезный CSS. Часть 7 - 1

Привет. Я продолжаю рассказывать про неизвестные широкому кругу разработчиков CSS фишки. Я отбираю их так, чтобы они были полезны в разного рода проектах. Неважно, верстаете ли вы сайт для малого бизнеса или создаёте супермодное React приложение. Они поддерживаются большинством браузеров. Отдельно отмечу, что я не считаю IE11 современным браузером. По этой причине я не учитывал его.

Сегодня мы рассмотрим:

  • что вы можете не знать про псевдо-класс :not();
  • примеры работы псевдо-класса :has(), работающие по логике операторов ИЛИ и И;
  • как неожиданно может повыситься специфичность правила при использовании псевдо-класса :has();
  • чем полезно свойство user-select, кроме отмены выделения текста.

Больше не буду затягивать. Давайте посмотрим, что я вам подготовил.

▍ Что интересного скрывает псевдо-класс :not()

Общаясь с разработчиками, я понял, что псевдо-класc :not() является одной из любимейших фишек CSS. Только не все знают одну особенность. Сейчас я вам её покажу.

Скорее всего, вы привыкли использовать этот псевдо-класс примерно следующим образом:

.link:not(:last-child) {
  margin-left: 1rem;
}

Правило из этого кода применится ко всем элементам с классом .link, кроме тех, которые являются последними элементами. Всё просто. Но давайте посмотрим на код с другой стороны.

По сути мы задали одно условие отбора, а именно «все, кроме последнего». Но псевдо-класс :not() позволяется использовать множество условий. Для примера я покажу фрагмент кода из моего проекта.

img:not([class], :first-child) {
  margin-block-start: var(--ds-typography-img-margin-block-start, var(--_ds-typography-main-margin))
}

Браузеры применят стили ко всем элементам img кроме тех, у которых установлен атрибут class, или они являются первым элементом. Другими словами, у меня существует два условия. Первое — «все, кроме тех, у которых есть атрибут class». Второе — «все, кроме первого».

Такое поведение возможно, благодаря тому, что псевдо-класс :not() принимает список селекторов. Как сказано в стандарте Selectors Level 4, это список из простых и сложных селекторов, разделённых запятыми. Вы также могли с ним столкнуться в псевдо-классах :is(), :has() и других.

Как я говорил, мы не ограничены количеством селекторов. Даже большинство селекторов отлично сработают. В качестве последнего «извращения» покажу пример с использованием псевдо-классов :not() и :has(). Я добавлю значение lightblue для свойства background-color только для тех элементов <p>, у которых следующий соседний элемент не <h2>.

<body>
  <h1>Париж</h1>
  <p>Столица и крупнейший город Франции. Находится на севере государства, в центральной части Парижского бассейна, на реке Сена</p>
  <h2>История</h2>
  <p>Париж вырос на месте поселения Лютеция, основанного кельтским племенем паризиев в III веке до н. э.</p>
  <p>Поселение располагалось на безопасном острове Сите, окружённом водами реки Сены.</p>
</body>

p:not(:has(+ h2)) {
  background-color: lightblue;
}

Отображается фрагмент статьи. Есть Заголовок первого уровня, за ним следует параграф с текстом. Далее идет заголовок второго уровня. За ними следует 2 параграфа с текстом, выделенные светло-голубым фоном

Честно говоря, этот код является давней моей профессиональной мечтой. Написать стили, указав только для части документа, это же круто. Считайте у нас появился прокаченный на стероидах селектор :not(:first-child) или :not(:last-child).

▍ У псевдо-класса :has() есть встроенная логика операторов ИЛИ и И

Псевдо-класс :has() самое долгожданное новшество в CSS последних лет. Как я пониманию, не для меня одного. Много статей об этом псевдо-классе создаются чуть ли не каждую неделю. Конечно, большинство из них похожи друг на друга. И в них опускается несколько интересных нюансов. Что ж, надо вам про них рассказать.

Сразу начнём с кода, который я вам подготовил.

.awesome-block:has(h3) {
  background-color: lightblue;
}

Я уверен, что видели что-то похожее у многих авторов. Здесь нет супер сложной магии. Браузеры применят стили, если в элементе с классом .awesome-block, находится элемент <h3>.

Только такой код является одним частным случаем из всех возможных. С псевдо-классом has() можно сделать больше! Чтобы это показать, давайте углублённо рассмотрим принцип работы псевдо-класса.

В своём примере я использую селектор h3 в качестве аргумента. Но сам псевдо-класс принимает список селекторов, разделённых запятыми. По этой причине мы спокойно можем указывать множество селекторов. Например, я изменю код так, чтобы правило ещё срабатывало, если в элементе с классом .awesome-block есть заголовок четвёртого уровня.

.awesome-block:has(h3, h4) {
  background-color: lightblue;
}

Сейчас наше правило применится, когда есть хотя бы один из двух элементов. Другими словами, в этом случае браузеры работают по алгоритму логического оператора ИЛИ. Если есть элемент <h3> ИЛИ <h4>, то они применяют правило.

А это не вся логика, которая скрывается под псевдо-классом. Ещё есть возможность использовать логический оператор И. Для этого нужно использовать несколько псевдо-классов :has(), указав их друг за другом.

.awesome-block:has(h3):has(h4) {
  background-color: lightblue;
}

В этом примере браузеры применят правило только в том случае, если внутри элемента с классом .awesome-block одновременно есть заголовки третьего и четвёртого уровня.

▍ Псевдо-класс :has() неожиданно повышает специфичность всего правила

Скрытая логика является не единственным малоизвестным нюансом псевдо-класса :has(). Ещё мы можем использовать его, как хак для повышения специфичности всего правила. Давайте сразу перейдём к примеру, чтобы мне было проще всё объяснить.

Сначала напишем разметку, в которой будет элемент <div> с классом .awesome-block, содержащий элемент <h3>.

<body>
  <div class="awesome-block">
    <h3>Заголовок h3</h3>
  </div>
</body>

Далее мы создадим одно правило, в котором будем использовать псевдо-класс :has().

.awesome-block:has(h3) {
  background-color: lightblue;
}

Как браузеры рассчитают специфичность этого правила? Сначала рассчитывается специфичность псевдо-класса :has(). Как написано в стандарте, она рассчитывается по самому приоритетному селектору среди всех переданных. В нашем примере передаётся только один селектор. Это селектор по типу h3. Его специфичность — 0001.

Далее это значение прибавляется к специфичности оставшийся части селектора. Мы используем селектор по классу. Его специфичность равняется 0010. В итоге специфичность всего правила будет 0011.

Теперь давайте попробуем переопределить это значение. Например, у нас появится ещё одно правило с такой же специфичностью.

.awesome-block:has(h3) {
  background-color: lightblue;
}

div.awesome-block {
  background-color: tomato; /* это правило выиграло! */
}

Поскольку специфичность правил одинаковая, применится то, которое является последним по порядку. В итоге в нашем примере браузеры применят значение tomato для свойства background-color.

К чему я всё это время вёл? Мы можем снова вернуть значение lightblue, используя псевдо-класс :has(). Достаточно повысить его специфичность, указав более «тяжёлый» селектор. Например, можно задать селектор по идентификатору.

.awesome-block:has(#unknown-selector, h3) {
  background-color: lightblue; /* это правило выиграло! */
}

div.awesome-block {
  background-color: tomato;
}

Стоп. У нас же нет в разметке элемента с атрибутом id и значением #unknown-selector. Да, да. В этом вся фишка.

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

Конкретно в нашем примере браузеры сначала выберут из селекторов #unknown-selector и h3 наиболее приоритетный. Это селектор #unknown-selector. По этой причине «вес» всего псевдо-класса :has() будет 0100. К этому значению прибавится специфичность оставшийся части, а именно селектора по классу 0010. В итоге будет рассчитана специфичность всего правила 0110.

Конечно, я настоятельно не рекомендую использовать осознанно эту технику. Это хак. Но всегда есть вероятность того, что вы встретите такое решение в своей практике. А теперь вы осведомлены, и легко поймёте, почему у такого правила наивысший приоритет.

▍ Свойство user-select делает мою работу проще

Свойство user-select стало известно многим разработчикам после того, как они начали использовать его для отмены выделения текста у кнопок с помощью значения none. Только эта ситуация не единственная, когда свойство полезно.

В моём примере будем использовать Хабр. Попробуем выделить элемент <input> в тексте.

Для этого дважды кликнем по нему мышкой или нажмём и удержим палец на нём, если у вас сенсорный экран.

Отображается фрагмент статьи. В текст есть теги. Один из них выделен.

Мы видим, что выделилось только название. Но мне хочется, чтобы выделился весь элемент вместе со скобками. Хорошо, что такая задача решается одной строкой в CSS. Просто надо использовать свойство user-select со значением all.

В нашем примере я сделаю это прямо в инструментах разработчика. Для этого нужно найти элемент, который содержит название и скобки, и к нему добавить свойство. У нас это элемент <code>.

Открыты инструменты разработчика. В них добавлены планируемое свойство

Снова попробуем выделить элемент <input>.

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

Великолепно! Текст выделился так, как я хотел изначально. И не надо дополнительно мучиться, выделяя скобки. Супер удобно!

▍ Заключение

Давайте подведём итог. В этой статье мы рассмотрели:

  • возможность использовать несколько селекторов для псевдо-класса :not();
  • как использовать псевдо-класс :has(), чтобы он работал по принципу логического оператора ИЛИ или И;
  • хак для повышения специфичности псевдо-класса :has() с помощью селектора по несуществующему элементу;
  • какую пользу несёт значение all для свойства user-select.

Также, пожалуйста, напишите в комментариях, какие CSS фишки вы используете, о которых другие могут не знать. Буду ждать их. Спасибо за чтение!

P.S. Помогаю больше узнать про CSS в своём ТГ-канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.

P.S.S. Другие статьи из серии можно найти по тегу «sm909_unknown_css».

© 2025 ООО «МТ ФИНАНС»

Автор: melnik909

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js