这是作者误解如何做到这一点的最著名的例子之一:first-child
works. CSS2中引入, the :first-child
伪类代表其父母的第一个孩子。就是这样。有一个非常常见的误解,即它会选择第一个与复合选择器其余部分指定的条件相匹配的子元素。由于选择器的工作方式(参见here以获得解释),这根本不是真的。
选择器级别 3 引入了:first-of-type伪类,它表示其元素类型的兄弟元素中的第一个元素。这个答案用插图解释了之间的区别:first-child
and :first-of-type
。然而,正如:first-child
,它不考虑任何其他条件或属性。在 HTML 中,元素类型由标签名称表示。在问题中,该类型是p
.
可惜没有类似的:first-of-class
用于匹配给定类的第一个子元素的伪类。在这个答案首次发布时,新发布的 FPWD of Selectors level 4 引入了:nth-match()伪类,围绕现有选择器机制设计,正如我在第一段中提到的,通过添加选择器列表参数,您可以通过该参数提供复合选择器的其余部分以获得所需的过滤行为。近年来,该功能被归入:nth-child() itself,选择器列表作为可选的第二个参数出现,以简化事情并避免错误的印象:nth-match()
匹配整个文档(请参阅下面的最后注释)。
当我们等待时跨浏览器支持(说真的,已经过去近 10 年了,而且最近 5 年只有一次实现),一种解决方法莉亚·维鲁而我独立开发的(她先做的!)就是先把你想要的样式应用到all该类的元素:
/*
* Select all .red children of .home, including the first one,
* and give them a border.
*/
.home > .red {
border: 1px solid red;
}
...然后“撤消”具有该类的元素的样式在第一个之后来, using 通用兄弟组合器~在压倒一切的规则中:
/*
* Select all but the first .red child of .home,
* and remove the border from the previous rule.
*/
.home > .red ~ .red {
border: none;
}
现在只有第一个元素class="red"
会有一个边界。
以下是如何应用规则的说明:
.home > .red {
border: 1px solid red;
}
.home > .red ~ .red {
border: none;
}
<div class="home">
<span>blah</span> <!-- [1] -->
<p class="red">first</p> <!-- [2] -->
<p class="red">second</p> <!-- [3] -->
<p class="red">third</p> <!-- [3] -->
<p class="red">fourth</p> <!-- [3] -->
</div>
-
不适用任何规则;不渲染边框。
该元素没有类red
,因此被跳过。
-
仅应用第一条规则;呈现红色边框。
该元素具有类red
,但其前面没有任何具有该类的元素red
在其父级中。因此,不应用第二条规则,仅应用第一条规则,并且元素保留其边框。
-
两条规则均适用;不渲染边框。
该元素具有类red
。它前面还至少有一个具有该类的其他元素red
。因此这两个规则都适用,并且第二个规则border
可以说,声明覆盖了第一个声明,从而“撤销”了它。
作为奖励,虽然它是在 Selectors 3 中引入的,但与 IE7 和更高版本不同的是,通用同级组合器实际上得到了 IE7 及更高版本的良好支持:first-of-type
and :nth-of-type()
仅 IE9 及以上版本支持。如果您需要良好的浏览器支持,那么您很幸运。
事实上,同级组合器是该技术中唯一重要的组成部分,and它具有如此惊人的浏览器支持,使得这项技术非常通用——除了类选择器之外,您还可以使用它来通过其他东西过滤元素:
-
您可以使用它来解决:first-of-type
在 IE7 和 IE8 中,只需提供类型选择器而不是类选择器(同样,在后面的部分的问题中详细介绍其错误用法):
article > p {
/* Apply styles to article > p:first-of-type, which may or may not be :first-child */
}
article > p ~ p {
/* Undo the above styles for every subsequent article > p */
}
-
您可以按以下条件过滤属性选择器或任何其他简单的选择器而不是类。
-
您还可以将这种压倒一切的技术与伪元素尽管伪元素从技术上讲并不是简单的选择器。
请注意,为了使其起作用,您需要提前知道其他同级元素的默认样式是什么,以便您可以覆盖第一个规则。此外,由于这涉及到覆盖 CSS 中的规则,因此您无法使用与选择器API, or Selenium的 CSS 定位器。
最后一点,请记住,这个答案假设问题正在寻找任意数量的具有给定类的第一个子元素。对于复杂选择器的第 n 次匹配,既没有伪类,也没有通用的 CSS 解决方案贯穿整个文档— 解决方案是否存在很大程度上取决于文档结构。 jQuery 提供:eq()
, :first
, :last
以及更多用于此目的的内容,但请再次注意它们的功能与:nth-child() et al。使用选择器 API,您可以使用document.querySelector()
获得第一个匹配:
var first = document.querySelector('.home > .red');
Or use document.querySelectorAll()
使用索引器来选择任何特定的匹配:
var redElements = document.querySelectorAll('.home > .red');
var first = redElements[0];
var second = redElements[1];
// etc
虽然.red:nth-of-type(1)
原始接受答案中的解决方案菲利普·道布迈尔作品(最初由Martyn但后来被删除),它的行为与您期望的方式不同。
例如,如果您只想选择p
here:
<p class="red"></p>
<div class="red"></div>
...那么你就不能使用.red:first-of-type
(相当于.red:nth-of-type(1)
),因为每个元素都是其类型的第一个(也是唯一的)一个(p
and div
分别),所以both将由选择器匹配。
当某个类的第一个元素也是同类中的第一个,伪类可以工作,但是这纯属巧合。菲利普的回答证明了这种行为。当您在该元素之前插入相同类型的元素时,选择器将失败。从问题中获取标记:
<div class="home">
<span>blah</span>
<p class="red">first</p>
<p class="red">second</p>
<p class="red">third</p>
<p class="red">fourth</p>
</div>
应用规则.red:first-of-type
会起作用,但是一旦你添加另一个p
没有班级:
<div class="home">
<span>blah</span>
<p>dummy</p>
<p class="red">first</p>
<p class="red">second</p>
<p class="red">third</p>
<p class="red">fourth</p>
</div>
...选择器将立即失败,因为第一个.red
元素现在是second p
元素。