Псевдокласс :not()

Определение

:not() – CSS-псевдокласс отрицания: выбирает элементы, не соответствующие селектору-аргументу. В Selectors Level 4 принимает список селекторов (:not(.a, .b)) – кроссбраузерно с января 2021 (Chrome 88, Firefox 78, Safari 9).

3 мин 108 Обновлено 15 октября

Исключения – постоянная часть работы со стилями: "все поля, кроме чекбоксов", "все элементы списка, кроме последнего", "все кнопки, кроме основной". Без псевдокласса отрицания такие задачи решаются вторым правилом, перекрывающим первое, или лишними классами в разметке. :not() переносит исключение прямо в селектор. Псевдокласс появился ещё в Selectors Level 3 в урезанном виде, а в Selectors Level 4 стал полноценным инструментом со списком селекторов в аргументе. Вместе с реляционным :has() он образует логику современных селекторов: :has() проверяет наличие, :not() – отсутствие.

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

Синтаксис – селектор:not(аргумент): правило применяется к элементам, которые не соответствуют ни одному селектору из аргумента. p:not(.fancy) – "параграфы без класса fancy", input:not([type="checkbox"]) – "все поля ввода, кроме чекбоксов". Возможности аргумента зависят от поколения спецификации.

:not() в Selectors Level 3 и Level 4 – в чём разница

Два поколения синтаксиса :not()
ПараметрLevel 3Level 4
Аргументодин простой селекторсписок через запятую, составные и сложные селекторы
Примеры:not(.foo):not(.foo, .bar), h2 :not(span.foo), a:not(table a)
Поддержкавсе браузеры, включая IE9Safari 9 (2015), Firefox 78 (2020), Chrome и Edge 88 (январь 2021)
Обход для старых браузеровне нуженцепочка :not(.foo):not(.bar)

Отсюда типовой вопрос: чем :not(a):not(b) отличается от :not(a, b)? По результату – ничем, оба означают "ни a, ни b". Цепочка – И-логика старого синтаксиса, работающая везде уже больше десяти лет; список – компактная запись Level 4. Ошибка появляется, когда список читают по аналогии с :is(): :not(.foo, .bar) – это "не (.foo ИЛИ .bar)", то есть исключаются оба класса сразу.

Второй важный нюанс: список аргументов :not() – unforgiving ("непрощающий"), один невалидный или неподдерживаемый браузером селектор инвалидирует правило целиком. Это прямой контраст с forgiving-списками :is(), :where() и :has(). Приём для устойчивости – обернуть аргументы в :is():

/* База: все параграфы, кроме .fancy */
p:not(.fancy) { color: green; }

/* Level 4: ни .foo, ни .bar */
div:not(.foo, .bar) { color: red; }

/* То же самое в Level 3-синтаксисе */
div:not(.foo):not(.bar) { color: red; }

/* Отступы между элементами списка, кроме последнего */
li:not(:last-child) { margin-block-end: 8px; }

/* Unforgiving-список, защищённый через :is() */
p:not(:is(.foo, :unsupported-thing)) { color: green; }

Специфичность :not() устроена так же, как у :has(): сам псевдокласс ничего не добавляет, считается самый специфичный селектор из аргумента. #foo:not(#bar) выбирает те же элементы, что и просто #foo, но весит как два ID – перебить такое правило почти невозможно. Подробнее о подсчёте – в статье о специфичности CSS.

Зачем это нужно

Главный выигрыш – меньше служебных классов и правил-перекрытий. В дизайн-системах :not() задаёт общие стили "всем, кроме" отдельных компонентов без цепочек переопределений; в шаблонах 1С-Битрикс, где типовые компоненты приходят со своей разметкой, он исключает служебные элементы из общих правил без копирования и правки шаблона. Но у псевдокласса есть ловушки, которые регулярно ловят даже опытных верстальщиков:

  • Матчит html и body. Одиночный :not(.foo) применится ко всем элементам без этого класса, включая html и body, – охват шире ожидаемого. Уточняйте селектор слева: p:not(.foo).
  • Не исключает ветку DOM. body :not(table) a всё равно покрасит ссылки внутри таблицы: между table и ссылкой стоят tbody, tr и td, а каждый из них сам матчит :not(table). В Level 4 задача решается сложным селектором в аргументе: a:not(table a).
  • :not(*) не выбирает ничего. Элемент, который "не элемент", невозможен.
  • Псевдоэлементы запрещены. Передать внутрь ::before или ::first-line нельзя – селектор станет невалидным, аргументом могут быть только селекторы реальных элементов.
Поддержка :not() браузерами
СинтаксисБраузерыСтатус
Один простой селектор (Level 3)все, включая IE9+Baseline Widely available с июля 2015
Список селекторов (Level 4)Safari 9+, Firefox 78+, Chrome и Edge 88+кроссбраузерно с января 2021

Пример

Типовая задача вёрстки – отступы между элементами списка: правило li:not(:last-child) из блока кода выше заменяет пару "всем задать отступ, последнему обнулить" одним селектором. Тот же подход работает с порядковыми псевдоклассами вроде :nth-child(): tr:not(:nth-child(-n + 2)) исключит из правила первые две строки таблицы.

Вторая показательная связка – "отрицание по потомку" вместе с :has():

/* Figure без подписи – убираем нижний отступ */
figure:not(:has(figcaption)) { padding-block-end: 0; }

/* Карточки без изображения получают рамку */
.card:not(:has(img)) { border: 1px solid #e0e0e0; }

До Selectors Level 4 выбрать "элементы, внутри которых чего-то нет" средствами CSS было невозможно – требовался JavaScript, который проверял содержимое и навешивал классы. Сейчас достаточно одного правила, а разметка остаётся чистой: ни классов-модификаторов, ни скриптов, ни рассинхронизации между DOM и стилями.

Частые вопросы

Можно ли передать в :not() несколько селекторов?

Да, начиная с Selectors Level 4 аргументом может быть список через запятую: button:not(.primary, .danger). Поддержка: Safari 9+, Firefox 78+, Chrome и Edge 88+ (январь 2021). Для максимальной совместимости тот же результат даёт цепочка button:not(.primary):not(.danger).

Чем :not(a):not(b) отличается от :not(a, b)?

По результату они эквивалентны: оба означают "ни a, ни b". Разница в поколении синтаксиса и совместимости: цепочка – Selectors Level 3, работает во всех браузерах, включая устаревшие; список – Level 4, кроссбраузерно с января 2021. Важно не путать список с :is(): :not(a, b) исключает оба варианта сразу.

Какая специфичность у :not()?

Сам псевдокласс специфичности не добавляет – считается самый специфичный селектор из аргумента. #foo:not(#bar) матчит те же элементы, что и #foo, но имеет специфичность двух ID. Поэтому ID внутрь :not() без необходимости лучше не передавать.

Почему правило с :not() перестаёт работать целиком?

Скорее всего, в аргументе есть невалидный или неподдерживаемый селектор: список :not() – unforgiving, одна ошибка инвалидирует всё правило. Лечится обёрткой в forgiving-список :is(): p:not(:is(.foo, :experimental)) – невалидная часть просто игнорируется.

Матчит ли :not(.foo) теги html и body?

Да. Без уточняющего селектора слева :not(.foo) применится ко всем элементам без класса foo, включая html и body, – охват получается шире ожидаемого. Указывайте элемент или контекст: p:not(.foo) вместо одиночного :not(.foo).

Почему body :not(table) a красит ссылки внутри таблицы?

Потому что между table и ссылкой стоят tbody, tr и td, а каждый из них сам матчит :not(table). :not() исключает только сам элемент, а не ветку DOM. В Selectors Level 4 задача решается сложным селектором в аргументе: a:not(table a).

Можно ли использовать псевдоэлементы внутри :not()?

Нет, ::before, ::after и другие псевдоэлементы внутри :not() запрещены спецификацией – селектор станет невалидным. :not() принимает только селекторы реальных элементов: теги, классы, атрибуты, псевдоклассы.

108

Связанные термины

Selectors Level 4 Selectors Level 4 – модуль спецификации CSS от W3C, расширивший селекторы: :is(), :where(), :has(), :not() со списком, :nth-child(An+B of S). Статус – Working Draft (редакция 22.01.2026), но ядро давно поддержано всеми браузерами. Псевдокласс :has() :has() – реляционный CSS-псевдокласс из Selectors Level 4: выбирает элемент по наличию потомка, соседа или состояния внутри. Долгожданный "родительский селектор" работает во всех браузерах с декабря 2023 (Firefox 121). Псевдокласс :nth-child() :nth-child() – CSS-псевдокласс, выбирающий элементы по позиции среди всех детей родителя: по номеру, ключевым словам odd/even или формуле An+B. Нумерация начинается с 1, а не с 0. Специфичность селектора Специфичность селектора – механизм каскада CSS, определяющий, какое из конфликтующих правил применит браузер. Считается триадой A-B-C (ID, классы, теги) и сравнивается попозиционно: один ID тяжелее любого числа классов.

Материалы по теме

Что почитать дальше по этой теме

Инструкция Уведомления о заявках с сайта в MAX: что нужно для подключения Что нужно, чтобы заявки с сайта приходили в MAX: верифицированная организация, модерация бота, токен из веб-версии и группа для заявок. Плюс разбор для разработчика. 7 мин 219 4 октября 2026 Статья Что боты находят на сайте, а вы – нет Боты ходят по изнанке сайта, куда живой человек не забредёт, и фиксируют в логах то, чего не видно ни в Метрике, ни на витрине: падающие с ошибкой 500 страницы каталога, битый файл с 747 обращениями, поток сканеров секретов. Разбираю на реальных логах, что искать и как чинить. 7 мин 436 22 июля 2026 Инструкция Как выгрузить и читать access-логи, если хостинг хранит их три дня Access-лог (журнал обращений к серверу) – единственное место, где виден весь трафик сайта, включая ИИ-ботов, которых не замечают счётчики. Разбираю на практике: где взять логи, как не потерять их из-за ротации, какие команды показывают ботов, ошибки и подозрительную активность. 7 мин 431 21 июля 2026 Флагманский гайд ИИ-анализ email-обращений: методика на стыке Яндекс.Метрики и Claude Code Классический email-трекинг отвечает на вопрос "откуда пришло обращение". ИИ-слой отвечает на вопросы, которые раньше требовали ручного аналитика: значим ли email на фоне звонков, форм и мессенджеров, почему один источник даёт качественные обращения, а другой – пустые, какие сегменты визитов предшествуют письму, что написать в ответ с учётом пути пользователя. Здесь – как я собираю это на уже имеющемся стеке: Logs API Метрики, PostgreSQL и Claude Code, без новых платных сервисов. 11 мин 458 26 мая 2026 Флагманский гайд Майкор: ИИ-аудит проекта по 4 точкам контакта Майкор – это перекрёстный ИИ-аудит проекта в 4 точках контакта: сайт, контекстная реклама, ИИ-поиск и Яндекс.Карты. Я анализирую каждую систему и смотрю связки между ними – где маркетинг рассказывает одно, реклама ведёт на другое, а ИИ-системы цитируют третий номер телефона. В одном из моих аудитов медицинской клиники в Москве у организации в индексе ИИ-сервисов оказалось 6 разных номеров и всего 7 упоминаний на 64 проверочных запроса – при том, что в обычной выдаче Яндекса клиника была в топ-1. Майкор закрывает такие разрывы за один аудит вместо четырёх раздельных. 16 мин 744 13 мая 2026