CSS-селектор :not. Полезные примеры

В спецификации и блогах про селектор :not обычно приводят какие-то искусственные примеры, которые хоть и объясняют синтаксис и принцип действия, но не несут никакой идеи о том, как получить пользу от нового селектора.

Например:

p:not(.classy) { color: red; }

Ну окей, думаю я, в моей практике не встречались такие ситуации. Обходились мы ведь как-то раньше без :not. Приходилось немного переписать структуру селекторов или обнулить пару значений.

Пример 1. Элемент без класса

Селектор :not может быть крайне полезен, когда нам нужно застилить контент сгенерированный пользователем (нет возможности расставить в нем классы), или когда у нас контента очень много и расставлять в нем классы слишком трудоёмко.

Например, мы хотим на сайте сделать красивые буллиты для ненумерованных списков ul li. Мы пишем код:

ul li { /* наши красивые стили */ }

В результате, наши красивые буллиты появляются не только в контенте, но и, например, в навигации, где тоже используются ul li.

Мы ограничиваем область действия селектора:

.content ul li { /* красота */ }

Навигацию мы спасли, но ненужные буллиты всё еще вылазят на слайдерах, списках новостей и других конструкциях внутри .content, где тоже используются ul li.

Далее у нас варианты:

1) обнулить мешающие стили в слайдерах и других местах. Но это противоречит «DRY» и является одним из признаков «вонючего» кода. К тому же не решает проблему раз и навсегда: добавите, например, аккордеон и списки в нем снова придется обнулять.

2) пойти от обратного и ставить класс всем спискам, которые нужно стилизовать:

.textlist li { /* красота */ }

Это добавляет лишней работы по расстановке классов в контенте. Иногда имеет смысл, но лишнюю работу никто не любит.

3) стилизовать только те ul li, у которых нет никаких классов вообще:

ul:not([class]) li { /* красота */ }

Победа! Нам не нужно делать дополнительную работу по расстановке классов в контенте. А на слайдерах, аккордеонах и прочих конструкциях, которые не должны выглядеть как списки, но используют их в своей разметке, в 99% случаев уже будут свои классы, и наши стили их не затронут.

Этот прием — «выбирать только элементы без класса» — очень полезен для оформления пользовательского контента и его можно применять не только к спискам, но и для других случаев.

Пример 2. Изменение внешнего вида всех элементов, кроме наведенного

Пример

Такой эффект можно реализовать без :not путем перезаписи значений. И это будет работать в бо́льшем количестве браузеров.

/* с перезаписью свойств */
ul:hover li {
  opacity:0.5;
}
ul:hover li:hover {
  opacity:1;
}

Но если придется обнулять слишком много свойств, то есть смысл использовать :not.

/* используя :not() */
ul:hover li:not(:hover) {
  opacity:0.5;
}

Пример 3. Меню с разделителями между элементами

Пример

Как и в предыдущем примере, желаемого можно добиться несколькими способами.

Через перезапись свойств. Но тут два правила вместо одного, что не есть «DRY».

.menu-item:after {
  content: ' | ';
}
.menu-item:last-child:after {
  content: none;
}

Через :nth-last-child(). Одно правило, но тяжело читается.

.menu-item:nth-last-child(n+2):after {
  content: ' | ';
}

Через :not() — самая короткая и понятная запись.

.menu-item:not(:last-child):after {
  content: ' | ';
}

Пример 4. Debug css

Удобно для отладки и самоконтроля искать/подсвечивать картинки без alt, label без for и другие ошибки.

/* подсвечиваем теги без необходимых атрибутов */
img:not([alt]),
label:not([for]),
input[type=submit]:not([value]) {
  outline:2px solid red;
}

/* тревога, если первый child внутри списка не li и прочие похожие примеры */
ul > *:not(li),
ol > *:not(li),
dl > *:not(dt):not(dd) {
  outline:2px solid red;
}

Пример 5. Поля форм

Раньше текстовых полей форм было не много. Достаточно было написать:

select,
textarea,
[type="text"],
[type="password"] {
    /* стили для текстовых полей ввода */
}

С появлением новых типов полей в HTML5 этот список увеличился:

select,
textarea,
[type="text"],
[type="password"],
[type="color"],
[type="date"],
[type="datetime"],
[type="datetime-local"],
[type="email"],
[type="number"],
[type="search"],
[type="tel"],
[type="time"],
[type="url"],
[type="month"],
[type="week"] {
  /* стили для текстовых полей ввода */
}

Вместо перечисления 14 типов инпутов можно исключить 8 из них:

select,
textarea,
[type]:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]):not([type="reset"]):not([type="range"]):not([type="file"]):not([type="image"]) {
  /* стили для текстовых полей ввода */
}

Ладно, этот пример не очень красив, и я рекомендую всё же первый вариант с перечислением, он работает с IE8+, а второй вариант с IE9+.

Поддержка

Следует заметить, что согласно спецификации в скобках селектора :not() может стоять только простой селектор и в скобках нельзя использовать сам селектор :not(). Если нужно исключить несколько элементов, :not() можно повторить несолько раз, как в примере 5.

CSS3 selectors

W3C Recommendation

Поддерживается начиная с версий:

Desktop

  • 4
  • 3.5
  • 9
  • 9.5
  • 3.2

Mobile / Tablet

  • 3.2
  • 2.1
  • 10
  • 57
  • 52
  • Поддержка:
  • Да
  • Нет
  • Частично
  • Полифил

Статистика с caniuse.com

Если очень нужны CSS3-селекторы в браузерах, которые их не поддерживают, можно использовать полифил selectivizr.

Ссылки и благодарности

Полная копия статьи: http://paulradzkov.com/2014/negation_css_selector/