为什么我的jQuery :not()选择器在CSS中不起作用?

61

我有这个布局:

<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)类添加到那些没有alphabetagamma类中的扇区:

我使用 jQuery 在那些没有 alphabetagamma 类的扇区中添加了 unassigned 类。

$('#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 预览

考虑到 jQuery 中的 :not() 选择器 基于 CSS3 中的 :not() ,我想直接将其移入样式表中,这样就不必依赖于使用 jQuery 添加额外的类。此外,我也不是很关心支持旧版本的 IE,因为其他浏览器对 :not() 选择器的支持非常好。

因此,我尝试将上述部分的 .unassigned 更改为以下内容(知道我的布局中只有Α、Β和Γ三个部分):

#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的解决方法,还是我必须依靠脚本来实现?


1
相关:在CSS中组合:not()选择器 - BoltClock
还有相关的内容:Encyclopedia Stack Exchange - 这是我第一次尝试,除了那个不友好的踩票之外,我必须说它比我预期的要好!:) - BoltClock
1个回答

85
为什么在jQuery中:not()选择器可以工作,而在CSS中失败?既然jQuery声称是“CSS3兼容的”,那么它不应该在两个地方都工作得相同吗?还是我漏掉了什么?
也许它本应如此,但事实证明它并没有:jQuery扩展了:not()选择器,以便您可以向其传递任何选择器,无论它们有多复杂,并且我怀疑这样做的主要原因是与.not()方法保持一致,该方法也接受任意复杂的选择器并进行过滤。它确实保持了类似于CSS的语法,但是它扩展了标准定义的内容。
作为另一个示例,这也可以正常工作(我知道它与问题中给出的内容相比是一个非常荒谬的示例,但仅供说明目的)。
/* 
 * Select any section
 * that's 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()意味着过滤掉不匹配任何列出的选择器的元素。

现在,在选择器级别3中,:not()伪类本身非常有限。您只能将单个简单选择器作为参数传递给:not()。这意味着一次只能传递以下任何一个:

  • 通用选择器 (*),可选地带有命名空间
  • 类型选择器 (a, div, span, ul, li等),可选地带有命名空间
  • 属性选择器 ([att], [att=val]等),可选地带有命名空间
  • 类选择器 (.class)
  • ID选择器 (#id)
  • 伪类选择器 (:pseudo-class)

因此,这里是jQuery的:not()选择器当前标准的:not()选择器之间的区别:

  1. First and foremost, to answer the question directly: you can't pass a comma-separated selector list.1 For example, while the given selector works in jQuery as demonstrated in the fiddle, it isn't valid CSS:

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

    Is there a pure CSS workaround for this or will I have to rely on a script?

    Thankfully, in this case, there is. You simply have to chain multiple :not() selectors, one after another, in order to make it valid CSS:

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

    It doesn't make the selector that much longer, but the inconsistency and inconvenience remain evident.

    Updated interactive jsFiddle preview

  2. You can't combine simple selectors into compound selectors for use with :not(). This works in jQuery, but is invalid CSS:

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

    You'll need to split it up into multiple negations (not just chain them!) to make it valid CSS:

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

    As you can see, this is even more inconvenient than point 1.

  3. You can't use combinators. This works in jQuery, but not CSS:

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

    This is a particularly nasty case, primarily because it has no proper workaround. There are some loose workarounds (1 and 2), but they almost always depend on the HTML structure and are therefore very limited in utility.

  4. In a browser that implements querySelectorAll() and the :not() selector, using :not() in a selector string in a way that makes it a valid CSS selector will cause the method to return results directly, instead of falling back to Sizzle (jQuery's selector engine which implements the :not() extension). If you're a stickler for performance, this is a positively minuscule bonus you'll definitely salivate over.

好消息是选择器4增强了:not()选择器,允许使用逗号分隔的复杂选择器列表。复杂选择器可以是单个简单或复合选择器,也可以是由组合符分隔的整个复合选择器链。简而言之,就是您在上面看到的所有内容。
这意味着jQuery上面的示例将成为有效的4级选择器,在CSS实现开始在未来几年内支持它时,伪类将变得更加有用。
1虽然这篇文章说在Firefox 3中可以传递逗号分隔的选择器列表到:not(),但实际上不应该这样做。如果像那篇文章所声称的那样在Firefox 3中起作用,那是因为Firefox 3中存在一个bug,我找不到这个票据了,但在未来浏览器实现未来标准之前,它不应该起作用。考虑到目前有多少文章引用了这篇文章,我已经留下了注释来说明这一点,但同时考虑到这篇文章的年龄和网站更新频率的低下,我真的不指望作者会回来修复问题。

3
那篇文章真的很烦人。它似乎是让人们认为在CSS中 :not(#foo, #foo *) 起作用的唯一原因。好吧,或许jQuery也对此有些责任。 - thirtydot
1
目前只有Safari支持Level 4选择器,http://caniuse.com/#search=%3Anot - Carlos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接