这是作者误解:first-child
工作方式的最著名例子之一。在CSS2介绍,该:first-child
伪类表示其父的第一个孩子。而已。有一个非常普遍的误解,认为它选择第一个与其余复合选择器指定的条件相匹配的子元素。由于选择器的工作方式(请参阅此处以获取解释),这根本不是事实。
选择器级别3引入了一个:first-of-type
伪类,该伪类表示其元素类型的同级元素中的第一个元素。该答案通过插图说明了:first-child
和之间的区别:first-of-type
。但是,与一样:first-child
,它不会查看任何其他条件或属性。在HTML中,元素类型由标记名称表示。在问题中,该类型为p
。
不幸的是,没有类似的:first-of-class
伪类可以匹配给定类的第一个子元素。我和Lea Verou为此(尽管是完全独立的)提出的一种解决方法是,首先将所需的样式应用于该类的所有元素:
/*
* Select all .red children of .home, including the first one,
* and give them a border.
*/
.home > .red {
border: 1px solid red;
}
...然后使用通用的同级组合器在覆盖规则中“撤消” 第一个元素之后的类的元素的样式:~
/*
* Select all but the first .red child of .home,
* and remove the border from the previous rule.
*/
.home > .red ~ .red {
border: none;
}
现在只有第一个元素具有class="red"
边框。
这是如何应用规则的说明:
<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>
没有规则适用;没有边框。
该元素没有class red
,因此被跳过。
仅应用第一个规则;呈现红色边框。
该元素具有class red
,但是它的red
父元素中没有带有该类的任何元素。因此,不应用第二条规则,仅应用第一条规则,并且元素保留其边界。
这两个规则都适用;没有边框。
该元素具有类red
。在类的前面还至少有一个其他元素red
。因此,这两个规则都适用,第二个border
声明覆盖第一个,因此可以“撤消”它。
作为奖励,但它是在选择器3介绍,一般的兄弟组合子是实际上是相当的IE7和很好的支持新的,不同于:first-of-type
和:nth-of-type()
其仅仅通过IE9起支持。如果您需要良好的浏览器支持,那么您很幸运。
实际上,同级组合器是该技术中唯一重要的组成部分,并且它具有令人赞叹的浏览器支持,这一事实使该技术非常通用-除了类选择器之外,您还可以通过其他方式使它适应过滤元素:
您可以使用此方法: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中的重写规则,因此,使用一个与Selectors API或Selenium的CSS定位器一起使用的选择器无法实现同一目的。
值得一提的是,选择器4 为:nth-child()
表示法引入了扩展(最初是一个全新的伪类,称为:nth-match()
),它使您可以使用类似的东西:nth-child(1 of .red)
来代替假设.red:first-of-class
。作为一个相对较新的建议,尚无足够的可互操作的实现方式使其可以在生产现场使用。希望这会很快改变。同时,我建议的解决方法应适用于大多数情况。
请记住,此答案假设问题正在寻找具有给定类的每个第一个子元素。对于整个文档中复杂选择器的第n个匹配,既没有伪类,也没有通用CSS解决方案-解决方案的存在在很大程度上取决于文档的结构。jQuery提供:eq()
,:first
,:last
多用于这一目的,但要注意再次证明它们的功能从非常不同:nth-child()
等。使用Selectors API,您可以使用document.querySelector()
获取第一个匹配项:
var first = document.querySelector('.home > .red');
或document.querySelectorAll()
与索引器配合使用以选择任何特定的匹配项:
var redElements = document.querySelectorAll('.home > .red');
var first = redElements[0];
var second = redElements[1];
// etc
尽管Philip Daubmeier.red:nth-of-type(1)
最初接受的答案中的解决方案有效(该解决方案最初是由Martyn撰写,但此后删除了),但它的行为方式并不符合您的期望。
例如,如果您只想p
在原始标记中选择:
<p class="red"></p>
<div class="red"></div>
...那么您将无法使用.red:first-of-type
(等效于.red:nth-of-type(1)
),因为每个元素都是其类型(p
和div
)中的第一个(也是唯一),因此选择器将同时匹配这两个元素。
当某个类的第一个元素也是其类型的第一个元素时,伪类将起作用,但这仅是巧合。Philip的回答证明了这种行为。当您在该元素之前插入相同类型的元素时,选择器将失败。进行更新的标记:
<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
元素现在是第二个 p
元素。