具有类的第一个元素的 CSS 选择器

Posted

技术标签:

【中文标题】具有类的第一个元素的 CSS 选择器【英文标题】:CSS selector for first element with class 【发布时间】:2011-02-12 15:12:36 【问题描述】:

我有一堆类名为 red 的元素,但我似乎无法使用以下 CSS 规则选择带有 class="red" 的第一个元素:

.home .red:first-child 
    border: 1px solid red;
<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 类的孩子?

【问题讨论】:

【参考方案1】:

这是作者误解:first-child 工作原理的最著名例子之一。 Introduced in CSS2,:first-child 伪类代表其父级的第一个子级。而已。有一个非常普遍的误解,即它会选择第一个匹配复合选择器其余部分指定的条件的子元素。由于选择器的工作方式(请参阅here 以获得解释),这根本不是真的。

Selectors level 3 introduces a :first-of-type pseudo-class,表示其元素类型的兄弟元素中的第一个元素。 This answer 用插图解释了:first-child:first-of-type 之间的区别。但是,与:first-child 一样,它不查看任何其他条件或属性。在 html 中,元素类型由标签名称表示。在问题中,该类型是p

不幸的是,没有类似的:first-of-class 伪类来匹配给定类的第一个子元素。在此答案首次发布时,the newly published FPWD of Selectors level 4 introduced an :nth-match() pseudo-class,围绕现有的选择器机制设计,正如我在第一段中提到的那样,通过添加一个选择器列表参数,您可以提供复合选择器的其余部分以获得所需的过滤行为.近年来,此功能为subsumed into :nth-child() itself,选择器列表作为可选的第二个参数出现,以简化事情并避免:nth-match() 在整个文档中匹配的错误印象(请参阅下面的最后注释)。

虽然我们在等待 cross-browser support(说真的,已经快 10 年了,而在过去的 5 年中只有一个实现),但 Lea Verou 和我独立开发的一个解决方法(她先做了) !) 是首先将您想要的样式应用于该类的所有元素:

/* 
 * Select all .red children of .home, including the first one,
 * and give them a border.
 */
.home > .red 
    border: 1px solid red;

...然后“撤消”具有类的元素的样式在第一个之后,在覆盖规则中使用the general sibling combinator ~

/* 
 * 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 和更新版本中得到了很好的支持,不像 :first-of-type:nth-of-type() 仅在 IE9 及更高版本中支持。如果您需要良好的浏览器支持,那么您很幸运。

事实上,兄弟组合器是该技术中唯一重要的组件,并且它具有如此惊人的浏览器支持,这使得该技术非常通用——您可以通过以下方式将其用于过滤元素除了类选择器之外的其他东西:

您可以使用它来解决 IE7 和 IE8 中的 :first-of-type 问题,只需提供类型选择器而不是类选择器(同样,稍后部分的问题中会详细介绍其错误用法):

 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 */
 

您可以通过attribute selectors 或任何其他简单的选择器而不是类进行过滤。

您也可以将此覆盖技术与pseudo-elements 结合使用,即使伪元素在技术上并不是简单的选择器。

请注意,为了使其正常工作,您需要提前知道其他同级元素的默认样式是什么,以便您可以覆盖第一条规则。此外,由于这涉及覆盖 CSS 中的规则,因此您无法使用与 Selectors API 或 Selenium 的 CSS 定位器一起使用的单个选择器来实现相同的目的。

最后一点,请记住,此答案假定问题是在寻找具有给定类的 任意数量 个第一个子元素。对于复杂选择器的第 n 次匹配整个文档,既没有伪类,也没有通用 CSS 解决方案——解决方案是否存在很大程度上取决于文档结构。 jQuery 为此提供了:eq():first:last 等,但请再次注意they function very differently from :nth-child() et al。使用 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)),因为每个元素都是其类型中的第一个(也是唯一一个)(分别为pdiv),所以两者都会被选择器匹配。

当某个类的第一个元素也是其类型的第一个时,伪类会起作用,但这只是巧合。这种行为在菲利普的回答中得到了证明。当您在该元素之前插入相同类型的元素时,选择器将失败。从问题中提取标记:

<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 元素。

【讨论】:

那么,有什么方法可以模拟:last-of-class?选择类的最后一个元素。 我想知道为什么article &gt; p ~ article &gt; p 不起作用/产生与article &gt; p ~ p 相同的效果。 @Gnuey:那是因为组合子是线性的。在评论中解释起来有点困难,但基本上我会说第一个 &gt; p 暗示第二个 p 是同一个 article 通过兄弟组合器的孩子,所以替换第二个 @987654394 @ 和 article 的工作方式类似,下面的 &gt; p 从该点向下遍历一个额外的级别。我还有其他几个答案可以更详细地解释它:***.com/a/3851754***.com/a/8135729 与兄弟姐妹的技巧很好。值得注意的是,这也适用于多个兄弟组合器,例如p~p~p 将选择第三项及以后:jsfiddle.net/zpnnvedm/1 @ElRoBe:这需要假设第一个 p.red 将始终直接跟随跨度。有时,您可能会发现自己无法对标记做出假设,因为它可能会以某些方式发生变化。例如,该 span 可能并不总是存在,第一个 p.red 可能不会直接跟随它,或者同一父级中可能有多个 span + p.red 对,而您只希望第一个受到影响。但是,如果您能够对标记做出这些假设甚至保证,那么这些更简单的解决方案将可供您使用。【参考方案2】:

:first-child 选择器,顾名思义,用于选择父标签的第一个子标签。所以这个例子可以工作(刚刚试过here):

<body>
    <p class="red">first</p>
    <div class="red">second</div>
</body>

但是,如果您将标签嵌套在不同的父标签下,或者您的 red 类标签不是父标签下的第一个标签,这将不起作用。

还要注意,这不仅适用于整个文档中的第一个这样的标签,而且每次都有一个新的父级环绕它时,比如:

<div>
    <p class="red">first</p>
    <div class="red">second</div>
</div>
<div>
    <p class="red">third</p>
    <div class="red">fourth</div>
</div>

firstthird 将变为红色。

对于您的情况,您可以使用:nth-of-type 选择器:

.red:nth-of-type(1)

    border:5px solid red;
 
<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>

感谢Martyn,他删除了包含这种方法的答案。更多信息,例如 here。请注意,这是一个 CSS 3 选择器,因此并非所有浏览器都能识别它(例如 IE8 或更早版本)。

【讨论】:

读到这里我有点困惑。严格来说,.red:nth-of-type(1) 将选择 (a) 是其元素类型的第一个子元素并且 (b) 具有“red”类的任何元素。因此,在示例中,如果第一个

没有类“red”,则不会选择它。或者,如果 和第一个

都具有“red”类,则它们都会被选中。 jsfiddle.net/fvAxn

几个月前@David 所说的是正确的; :nth-of-type() 不是一个好的解决方案。我提供了一个更可靠的替代答案,并且作为奖励,它可以在 IE7+ 中运行,这与 :nth-of-type() 不同。 @Dan Mundy::first-of-type 等价于:nth-of-type(1),所以当然也可以。由于我的回答中所述的相同原因,它也可能以相同的方式失败。 @David 我确定:nth-of-type 表示“类型”时表示“元素/标签”。所以它只考虑元素,而不考虑类之类的东西。 @gcampbell:是的,这正是它的意思,这就是为什么这个答案有缺陷。选择“类型”一词的原因是为了不将选择器与 HTML/XML 结合起来,因为并非所有语言都有定义元素的“标签”概念。【参考方案3】:

正确答案是:

.red:first-child, :not(.red) + .red  border:5px solid red 

第一部分:如果元素是其父元素的第一个并且具有“红色”类,它将获得边框。 第二部分:如果“.red”元素不是第一个到它的父元素,而是紧跟在一个没有类“.red”的元素之后,它也应该得到这个边框的荣誉。

Fiddle or it didn't happen.

Philip Daubmeier 的回答虽然被接受,但不正确 - 请参阅随附的小提琴。 BoltClock 的答案会起作用,但会不必要地定义和覆盖样式 (特别是它会继承不同边框的问题 - 你不想将 other 声明为边框:none)

编辑: 如果您在非红色之后多次出现“红色”,则每个“第一个”红色都将获得边框。为了防止这种情况,需要使用 BoltClock 的答案。见fiddle

【讨论】:

这个答案没有错,选择器 is 对于给定的标记是正确的,但我应该重申,编辑中提到的情况是我声明覆盖的原因至少当你不能保证标记结构时是必要的——仅仅因为.red 跟随:not(.red) 并不总是使它成为其兄弟姐妹中的第一个.red。而如果需要继承边框,只需在覆盖规则中声明border: inherit而不是border: none即可。【参考方案4】:

您可以使用first-of-typenth-of-type(1)

.red 
  color: green;  


/* .red:nth-of-type(1) */
.red:first-of-type 
  color: red;  
<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>

【讨论】:

如果你在span 和你的第一个p 元素与red 类之间有一个&lt;p&gt;&lt;/p&gt;,它将不起作用。看看这个JSFiddle。【参考方案5】:

上面的答案太复杂了。

.class:first-of-type  

这将选择第一个类。 MDN Source

注意:在 2021 年 6 月使用 Chrome 91 和 Firefox 89 进行测试。

【讨论】:

first-type has been renamed to first-of-type 它不适用于类。这就是它应该的工作方式,因为选择类的第 n 个比选择第 n 个更有用的一个标签。但是 ':first-of-type' 只对 tagName 起作用。如果碰巧“first-of-class”和“first-of-tag”指的是同一个元素,他们经常这样做,那么很容易让自己误以为它确实以这种方式工作;然后想知道当你遇到他们没有的情况时发生了什么。 @Bernesto,当您有多个具有相同“类型”的元素的情况时,可以说&lt;span&gt;,并且有一些具有highlight 类。 .highlight:first-of-type 选择器将选择具有所选类的“类型”的第一个实例,在本例中是第一个&lt;span&gt;,而不是类highlight 的第一个实例。只有当 span 的第一个实例也具有类突出显示时,才会实现该样式。 (codepen.io/andrewRmillar/pen/poJKJdJ) 我的答案如此复杂的原因是因为它的最后一部分,以及我对其他几个答案的 cmets,准确解释了为什么这是有缺陷的。当您没有尝试将 MDN 材料与它相关联时,两年后将 MDN 链接添加到您的答案中并不能完全支持它 - 大概是因为您不能。【参考方案6】:

由于其他答案涵盖了它的错误,我将尝试另一半,如何解决它。不幸的是,我不知道您在这里有 CSS only 解决方案,至少 我想不到。不过还有一些其他的选择......

    在生成元素时为其分配一个first 类,如下所示:

    <p class="red first"></p>
    <div class="red"></div>
    

    CSS:

    .first.red 
      border:5px solid red;
    
    

    此 CSS 仅匹配具有 both firstred 类的元素。

    或者,在 javascript 中执行相同的操作,例如,您将使用 jQuery 来执行此操作,使用与上面相同的 CSS:

    $(".red:first").addClass("first");
    

【讨论】:

我想出了一个纯 CSS 的解决方案 a while ago;我在这里复制了它作为一个规范的答案。 只要没有像:first-of-class 这样的东西,我还建议在第一个元素中添加一个额外的类。目前看来是最简单的解决方案。【参考方案7】:

要匹配您的选择器,元素的类名必须为 red,并且必须是其父元素的第一个子元素。

<div>
    <span class="red"></span> <!-- MATCH -->
</div>

<div>
    <span>Blah</span>
    <p class="red"></p> <!-- NO MATCH -->
</div>

<div>
    <span>Blah</span>
    <div><p class="red"></p></div> <!-- MATCH -->
</div>

【讨论】:

【参考方案8】:

我的项目中有这个。

div > .b ~ .b:not(:first-child) 
	background: none;

div > .b 
    background: red;
<div>
      <p class="a">The first paragraph.</p>
      <p class="a">The second paragraph.</p>
      <p class="b">The third paragraph.</p>
      <p class="b">The fourth paragraph.</p>
  </div>

【讨论】:

在我的回答中应用该技术时,您不需要 :not(:first-child) 。 ~ 组合器意味着 :not(:first-child) 将始终匹配,因此是多余的 - 它对您的选择器真正产生的唯一影响是额外的特异性。而且您不需要这种额外的特异性,因此您可以(并且可能应该)将其省略。【参考方案9】:

我很惊讶没有人提到最干净的解决方案:

.red:not(.red ~ .red) 
    border: 1px solid red;
<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 不是“.red 前面有一个.red”。本例中只有第一个。但如果在两者之间插入另一个类,它将不起作用。注意:.red:not(.red ~ *) 也应该可以工作(“red 不是“anything 前面有 .red”) @hl037_ 为什么它不起作用?你确定你没有弄错'+'和'~'选择器、直接兄弟和任意距离兄弟吗?【参考方案10】:

根据你更新的问题

<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>

怎么样

.home span + .red
      border:1px solid red;
    

这将选择 class home,然后选择元素 span,最后选择紧随 span 元素之后的所有 .red 元素

参考:http://www.w3schools.com/cs-s-ref/css_selectors.asp

【讨论】:

【参考方案11】:

我正在使用下面的 CSS 来为列表 ul li 提供背景图片

#footer .module:nth-of-type(1)>.menu>li:nth-of-type(1)
  background-position: center;
  background-image: url(http://monagentvoyagessuperprix.j3.voyagesendirect.com/images/stories/images_monagentvoyagessuperprix/layout/icon-home.png);
  background-repeat: no-repeat;
<footer id="footer">
  <div class="module">
    <ul class="menu ">
      <li class="level1 item308 active current"></li>
      <li> </li>
    </ul> 
  </div>
  <div class="module">
    <ul class="menu "><li></li>
      <li></li> 
    </ul>
  </div>
  <div class="module">
    <ul class="menu ">
      <li></li>
      <li></li>
    </ul>
  </div>
</footer>

【讨论】:

【参考方案12】:

由于某种原因,上述答案似乎都没有解决父母的真正的第一个唯一的第一个孩子的情况。

#element_id > .class_name:first-child

如果您只想将样式应用于此代码中的第一个类子项,则上述所有答案都将失败。

<aside id="element_id">
  Content
  <div class="class_name">First content that need to be styled</div>
  <div class="class_name">
    Second content that don't need to be styled
    <div>
      <div>
        <div class="class_name">deep content - no style</div>
        <div class="class_name">deep content - no style</div>
        <div>
          <div class="class_name">deep content - no style</div>
        </div>
      </div>
    </div>
  </div>
</aside>

【讨论】:

我们的答案没有解决这个问题的原因是因为这不是问题开始的原因。这个答案作为单独问题的答案会更好。【参考方案13】:

您可以将您的代码更改为类似的内容以使其正常工作

<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>

这为你做的工作

.home span + .red
      border:3px solid green;
    

这是来自 SnoopCode 的 CSS 参考资料。

【讨论】:

【参考方案14】:

您可以使用 nth-of-type(1) 但请确保该站点不需要支持 IE7 等,如果是这种情况,请使用 jQuery 添加主体类,然后通过 IE7 主体类查找元素,然后是元素名称,然后在其中添加第 n 个子样式。

【讨论】:

【参考方案15】:

总而言之,在阅读了所有页面和其他页面以及大量文档之后。总结如下:

对于first/last 孩子:现在可以安全使用(所有现代浏览器都支持) :nth-child() 现在也可以安全使用(所有现代浏览器都支持)。 但要小心它甚至计算兄弟姐妹!因此,以下内容将无法正常工作:

/* This should select the first 2 element with class display_class
* but it will NOT WORK Because the nth-child count even siblings 
* including the first div skip_class
*/
.display_class:nth-child(-n+2) 
    background-color:green; 
<ul>
   <li class="skip_class">test 1</li>
   <li class="display_class">test 2 should be in green</li>
   <li class="display_class">test 3 should be in green</li>
   <li class="display_class">test 4</li>
 </ul>

目前,有一个selector :nth-child(-n+2 of .foo) 支持按类选择,但现代浏览器不支持,所以没用。

那么,剩下的就是 Javascript 解决方案了(我们将修复上面的示例):

// Here we'll go through the elements with the targeted class
// and add our classmodifer to only the first 2 elements!


[...document.querySelectorAll('.display_class')].forEach((element,index) => 
  if (index < 2) element.classList.add('display_class--green');
);
.display_class--green 
    background-color:green;
<ul>
   <li class="skip_class">test 1</li>
   <li class="display_class">test 2 should be in green</li>
   <li class="display_class">test 3 should be in green</li>
   <li class="display_class">test 4</li>
 </ul>

【讨论】:

【参考方案16】:

试试这个简单有效的方法

 .home > span + .red
      border:1px solid red;
    

【讨论】:

【参考方案17】:

我相信使用相对选择器+ 来选择紧随其后的元素,在这里效果最好(正如之前很少有人建议的那样)。

这种情况下也可以使用这个选择器

.home p:first-of-type

但这是元素选择器而不是类之一。

这里有不错的 CSS 选择器列表:https://kolosek.com/css-selectors/

【讨论】:

【参考方案18】:

你可以试试这样的:

.red:first-of-type 
    border: 5px solid red;

你也可以将它用于最后一个元素(如果你需要的话):

.red:last-of-type 
    border: 5px solid red;

【讨论】:

【参考方案19】:

试试这个解决方案:

 .home p:first-of-type 
  border:5px solid red;
  width:100%;
  display:block;
<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>

CodePen link

【讨论】:

问题是“CSS selector for first element with class”,而不是“CSS selector for first element with tag姓名”。【参考方案20】:

我想很多人已经解释过了。您的代码只选择第一个实例的第一个孩子。如果要选择所有红色类的第一个孩子,则需要使用

.home > .red:first-child 
    /* put your styling here */

【讨论】:

以上是关于具有类的第一个元素的 CSS 选择器的主要内容,如果未能解决你的问题,请参考以下文章

具有类的孩子的第一个实例的CSS选择器? [复制]

css选择器的1.5 子选择器

是否可以仅选择包含具有 CSS 类的元素的 li 元素

逗号分隔列表的特殊性规则

网页的构成 第2节 css技术

网页的构成 第2节 css技术