Почему мой jQuery: not() не работает в CSS?

У меня есть этот макет:

<div id="sectors">
    <h1>Sectors</h1>
    <div id="s7-1103" class="alpha"></div>
    <div id="s8-1104" class="alpha"></div>
    <div id="s1-7605" class="beta"></div>
    <div id="s0-7479"></div>
    <div id="s2-6528" class="gamma"></div>
    <div id="s0-4444"></div>
</div>

С помощью этих правил CSS:

#sectors {
    width: 584px;
    background-color: #ffd;
    margin: 1.5em;
    border: 4px dashed #000;
    padding: 16px;
    overflow: auto;
}

#sectors > h1 {
    font-size: 2em;
    font-weight: bold;
    text-align: center;
}

#sectors > div {
    float: left;
    position: relative;
    width: 180px;
    height: 240px;
    margin: 16px 0 0 16px;
    border-style: solid;
    border-width: 2px;
}

#sectors > div::after {
    display: block;
    position: absolute;
    width: 100%;
    bottom: 0;
    font-weight: bold;
    text-align: center;
    text-transform: capitalize;
    background-color: rgba(255, 255, 255, 0.8);
    border-top: 2px solid;
    content: attr(id) ' - ' attr(class);
}

#sectors > div:nth-of-type(3n+1) {
    margin-left: 0;
}

#sectors > div.alpha { color: #b00; background-color: #ffe0d9; }
#sectors > div.beta  { color: #05b; background-color: #c0edff; }
#sectors > div.gamma { color: #362; background-color: #d4f6c3; }

Я использую jQuery для добавления класса unassigned в сектора, которые иначе не имеют один из классов alpha, beta или gamma:

$('#sectors > div:not(.alpha, .beta, .gamma)').addClass('unassigned');

Затем я применяю к этому классу несколько правил:

#sectors > div.unassigned {
    color: #808080;
    background-color: #e9e9e9;
    opacity: 0.5;
}

#sectors > div.unassigned::after {
    content: attr(id) ' - Unassigned';
}

#sectors > div.unassigned:hover {
    opacity: 1.0;
}

И все работает безупречно в современных браузерах.

Интерактивный просмотр jsFiddle

Но просмотр как :not() селектора в jQuery основан на :not() в CSS3, я думал, что могу перенести его прямо в мою таблицу стилей, поэтому мне не пришлось бы полагаться на добавление дополнительного класса с помощью jQuery. Кроме того, мне не очень интересно поддерживать более старые версии IE, а другие браузеры отлично поддерживают селектор :not().

Итак, я пытаюсь изменить часть .unassigned выше на это (зная, что в моем макете будут только сектора A, Β и Γ):

#sectors > div:not(.alpha, .beta, .gamma) {
    color: #808080;
    background-color: #e9e9e9;
    opacity: 0.5;
}

#sectors > div:not(.alpha, .beta, .gamma)::after {
    content: attr(id) ' - Unassigned';
}

#sectors > div:not(.alpha, .beta, .gamma):hover {
    opacity: 1.0;
}

Но как только я это сделаю, он перестает работать - во всех браузерах! Мои неназначенные сектора не выделены серым цветом, исчезли или помечены как "Не назначенные".

Обновленный, но не такой интерактивный просмотр jsFiddle

Почему селектор :not() работает в jQuery, но не работает в CSS? Не должно ли он работать одинаково в обоих местах, поскольку jQuery утверждает, что он является "совместимым с CSS3", или есть что-то, что мне не хватает?

Есть ли способ обхода CSS для этого или мне придется полагаться на script?

Ответ 1

Почему селектор :not() работает в jQuery, но не работает в CSS? Не должно ли он работать одинаково в обоих местах, поскольку jQuery утверждает, что он является "совместимым с CSS3", или есть что-то, что мне не хватает?

Возможно, это должно произойти, но оказалось, что это не так: jQuery расширяет селектор :not() таким образом, что вы можете передать ему любой селектор, каким бы сложным он ни был, и я подозреваю, что основной причиной этого является паритет с методом .not(), который также принимает любой произвольно сложный селектор и соответственно фильтрует. Он делает так, чтобы поддерживать синтаксис, подобный CSS, но он простирается от того, что определено в стандарте.

В качестве другого примера это работает просто отлично (я знаю, это невероятно смехотворный пример по сравнению с тем, что дается в вопросе, но это только для иллюстративных целей):

/* 
 * Select any section
 * that neither a child of body with a class
 * nor a child of body having a descendant with a class.
 */
$('section:not(body > [class], body > :has([class]))')

Предварительный просмотр jsFiddle

Помните, что передача списка селекторов, разделенных запятыми, на :not() означает элементы фильтрации, которые не соответствуют ни одному из перечисленных селекторов.

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

  • Универсальный селектор (*), необязательно с пространство имен
  • Селектор типов (a, div, span, ul, li и т.д.), необязательно с namespace
  • Селектор атрибутов ([att], [att=val] и т.д.), необязательно с namespace
  • Селектор классов (.class)
  • Селектор ID (#id)
  • Псевдокласс (:pseudo-class)

Итак, вот различия между jQuery :not() селектор и текущий стандартный селектор :not():

  • Прежде всего, чтобы ответить на вопрос напрямую: , вы не можете передать список разделенных запятыми. 1 Например, хотя данный селектор работает в jQuery, как показано на скрипке, это не верно CSS:

    /* If it not in the Α, Β or Γ sectors, it unassigned */
    #sectors > div:not(.alpha, .beta, .gamma)
    

    Есть ли способ обхода CSS для этого или мне придется полагаться на script?

    К счастью, в этом случае есть. Вам просто нужно связать несколько селекторов :not() один за другим, чтобы сделать его действительным CSS:

    #sectors > div:not(.alpha):not(.beta):not(.gamma)
    

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

    Обновлен интерактивный просмотр jsFiddle

  • Вы не можете комбинировать простые селектора в составных селекторах для использования с :not(). Это работает в jQuery, но недействительно CSS:

    /* Do not find divs that have all three classes together */
    #foo > div:not(.foo.bar.baz)
    

    Вам нужно разделить его на несколько отрицаний (а не просто на цепочку!), чтобы сделать его действительным. CSS:

    #foo > div:not(.foo), #foo > div:not(.bar), #foo > div:not(.baz)
    

    Как вы можете видеть, это еще более неудобно, чем точка 1.

  • Вы не можете использовать комбинаторы. Это работает в jQuery, но не в CSS:

    /* 
     * Grab everything that is neither #foo itself nor within #foo.
     * Notice the descendant combinator (the space) between #foo and *.
     */
    :not(#foo, #foo *)
    

    Это особенно неприятный случай, прежде всего потому, что у него нет надлежащего обходного пути. Есть некоторые способы обхода (1 и 2), но они почти всегда зависят от структуры HTML и поэтому очень ограничены в утилите.

  • В браузере, который реализует селектор querySelectorAll() и :not(), используя :not() в селекторной строке способом, который делает его допустимым селектором CSS, приведет к тому, что метод будет напрямую возвращать результаты, а не Возвращаясь к Sizzle (движок селектора jQuery, который реализует расширение :not()). Если вы приверженец производительности, это положительно незначительный бонус, который вы наверняка сэлируете.

Хорошей новостью является то, что Селекторы 4 улучшают селектор :not(), чтобы разрешить список сложных селекторов, разделенных запятыми. Комплексный селектор - это просто простой или составной селектор или целая цепь составных селекторов, разделенных комбинаторами. Короче говоря, все, что вы видите выше.

Это означает, что приведенные выше примеры jQuery станут действительными селекторами уровня 4, что сделает псевдокласс намного более полезным, когда реализация CSS начнет поддерживать его в ближайшие годы.


1 Хотя в этой статье говорится, что вы можете передать список селекторов, разделенных запятыми, на :not() в Firefox 3, вы не должны быть в состоянии. Если он работает в Firefox 3, как утверждает эта статья, то это потому, что ошибка в Firefox 3, для которой я больше не могу найти билет, но он не должен работать, пока будущие браузеры не будут внедрять будущие стандарты. Видя, как часто эта статья цитируется до настоящего времени, я оставил comment, но также посмотрел, сколько лет статья и как редко сайт обновляется, я действительно не рассчитываю, что автор вернется, чтобы исправить его.