选择一个元素及其所有后代元素

9

选择一个元素及其所有后代元素:

.media, .media * {color: #f00;}

有没有一个选择器可以代替用逗号分隔的两个选择器?我正在寻找一种更高效的方法来输入这个。


今天,2020年是否有更好的解决方案? - Andrey Mishchenko
1
基本上,CSS 不是用来这样工作的。在每个后代上应用相同的属性是一种不好的做法。.media {color: #f00} 就足够了,因为每个后代都会“继承”这个属性,而不是“设置”它,除非有其他规则适用(比如链接的颜色)。这是所谓的“层叠”样式表的一部分。 - Charles-Édouard Coste
@Charles-ÉdouardCoste 是的,这对于继承的属性是有效的。然而,并非所有的CSS属性都是继承的,例如 display - chharvey
1
当然,但是随意将此属性应用于每个后代而不是按类分组会很奇怪... 凭经验,当CSS中似乎缺少某些内容时,我意识到我以后不应该使用它来做我想做的事情。:D - Charles-Édouard Coste
2023年,CSS标准的最新发展提供了有趣的替代方案。请查看下面我的回答。 - mesr
3个回答

6

使用XPath,您可以使用descendant-or-self

但是在CSS中没有此类选择器。


3

在2023年,CSS语法和可用的伪类的最新添加带来了一些有趣的替代方法来解决这个问题(尽管仍然不如真正本地的自身或后代组合器理想)。

让我们考虑一个假设的.prose类,它标识站点的作者内容,可以通过嵌套结构的<article><section>块,或者直接包含文本的<p><blockquote>元素来传递。

只有严格的后代组合器和传统的CSS语法,像<p><blockquote>这样的元素需要添加一个(可能不太语义化的)包装器,例如<div>元素。

或者,CSS规则可以编写成匹配自身和后代的方式,但这涉及到重复(容易出错的)样板:

h1.prose, .prose h1 {...}
h2.prose, .prose h2 {...}
h3.prose, .prose h3 {...}
h4.prose, .prose h4 {...}
h5.prose, .prose h5 {...}
h6.prose, .prose h6 {...}

p.prose, .prose p {...}

blockquote.prose, .prose blockquote {...}

/* And so forth... */

利用最近的CSS发展,以上内容可以按以下方式重新编写:

.prose, .prose * {
    
    &:is(h1) {...}
    &:is(h2) {...}
    &:is(h3) {...}
    &:is(h4) {...}
    &:is(h5) {...}
    &:is(h6) {...}
    
    &:is(p) {...}

    &:is(blockquote) {...}

    /* Alternatively, the :where() pseudo-class can also be used,
       which differs in how specificity is calculated. */
}

显然,&:is(...) 部分仍然是样板代码,但重复性已经大大减少,这可以被认为是一种改进。

注意:截至2023年5月,浏览器对于原生CSS嵌套的支持仍在进行中(请参见https://caniuse.com/css-nesting),利用它的CSS代码应该转换为非嵌套语法(例如使用postcss-nesting插件)。:is() 伪类享有更广泛的支持(请参见https://caniuse.com/?search=is)。


0
这里有另一种有趣的方法来解决这个问题,它利用了CSS变量(也称为“自定义属性”),适用于在存在所谓的“上下文类”时切换单个属性,无论这些类是在元素本身上定义还是在其祖先元素上定义。
:root {
    --ctx-media: ;
    --ctx-not-media: initial;
}

.media {
    --ctx-media: initial;
    --ctx-not-media: ;
}

* {
    color: var(--ctx-media, #f00) 
           var(--ctx-not-media, inherit);
}

--ctx-media变量被定义为根元素上的一个空格,而--ctx-not-media未定义(:root规则中的--ctx-not-media: initial属性实际上并不需要,因为默认情况下所有变量都是未定义的,但为了视觉对称性,我还是提供了它)。请注意,CSS变量默认情况下是继承的,除非在@property规则中另有定义。无论何时应用media类,--ctx-media将变为未定义,--ctx-not-media将被定义为元素本身及其所有后代的一个空格。

接下来是color属性,无论哪个CSS变量被定义,都会扩展为一个空格,而未定义的则会扩展为其回退值。由于属性值周围的空格在语义上是惰性的,所以结果值仍然有效。对于非媒体内容,inherit值可以替换为任何所需的值。请注意,这两种情况必须包含在属性值的定义中。省略其中一种情况将导致无效的CSS:

* {
    /* WRONG!!! Will resolve to `color: ;` when --ctx-media is defined */
    color: var(--ctx-media, #f00);
}

现在,inherit值或其他“非媒体”值可能会在属性不被继承的情况下(例如marginpadding)引起问题,或者在根据上下文可以有多个其他值的情况下,你只想保持它不变。在这种情况下,一种解决方案是利用CSS的另一个最近添加的功能,即图层。这样就不需要为每种可能的情况创建--ctx-*变量。
:root {
    --ctx-media: ;
    --ctx-not-media: initial;
}

.media {
    --ctx-media: initial;
    --ctx-not-media: ;
}

@layer media {
    * {
        color: var(--ctx-media, #f00) 
               var(--ctx-not-media, revert-layer);
    }
}

请注意,完全省略分层并使用默认值revert而不是revert-layer将不起作用,因为该值将被还原为在整个样式表应用之前的值,而不是在同一样式表中被覆盖的定义。
当只涉及一个规则时,这当然比简单的.media, .media * {...}选择器更冗长。然而,假设*是一个用于样式化处于“媒体”上下文中的元素的假设规则集的占位符,这就是这种方法的全部好处所在。
CSS分层和自定义属性在各种浏览器中得到广泛支持。

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