首个子元素 + 非选择器?

7

我有一个动态列表,其中包含可见和隐藏的项目:

<ul>
    <li class="hidden">
    <li class="hidden">
    <li>
</ul>

我希望能够对列表中第一个未隐藏元素应用样式。

关于第一个元素的代码:

ul li:first-child{
  font-size:10px;
}

我获取非隐藏元素的代码

ul li:not(.hidden){
  font-size:10px;
}

我该如何将这两个合并为跨浏览器解决方案?理想情况下,它应该是这样的:
ul li:not(.hidden):first-child

(这个不起作用)


"li:not(.hidden):first-child" 是有效的,但它实际上意味着,选择第一个子元素,如果它也没有 .hidden 类(第一个子元素确实有 .hidden 类,因此它不会选择任何内容)。 - Monfa.red
2个回答

3

仅使用CSS的方法

“第一个子元素”并不是这样使用的。你可以在BoltClock的回答这里找到更多信息。它选择父级中的first-child(也要匹配任何其他条件),而不能选择满足所提供条件的孩子中的第一个元素。

相反,你可以先在所有的li:not(.hidden)元素上应用所需属性,然后再为 li:not(.hidden)〜li:not(.hidden)覆盖默认设置。第二个选择器的意思是除第一个之外,任何作为另一个兄弟的.hidden元素(这意味着它不是第一个)都会得到默认设置,而第一个则会得到修改后的设置(代码段中的红色颜色)。

应该使用通用兄弟选择器,而不是相邻兄弟选择器(+),因为正如你在下面的代码片段中看到的那样,只要之间没有其他的.hidden,它就只选择所有其他元素。

ul > li:not(.hidden) {
  color: red;
}
ul#one > li:not(.hidden) ~ li:not(.hidden) {
  color: black;
}
ul#two > li:not(.hidden) + li:not(.hidden) { /* wont help if any other hidden elements in between */
  color: black;
}
<ul id='one'>
  <li class="hidden">First hidden</li>
  <li class="hidden">Second hidden</li>
  <li>First not hidden</li>
  <li>Second not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Third not hidden</li>
</ul>

<ul id='two'>
  <li class="hidden">First hidden</li>
  <li class="hidden">Second hidden</li>
  <li>First not hidden</li>
  <li>Second not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Third not hidden</li>
</ul>

使用JavaScript/jQuery

我知道您要求纯CSS选择器,但从现有答案中可以看出,没有办法在不覆盖相邻兄弟的情况下实现这一点。如果您想要一个简单直接的方法来处理问题,则可以考虑使用JS或jQuery。以下是几个示例片段:

jQuery:

$(document).ready(function() {
  $('ul').each(function() {
    $(this).children('li:not(.hidden):first').css('color', 'red');
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id='one'>
  <li class="hidden">First hidden</li>
  <li class="hidden">Second hidden</li>
  <li>First not hidden</li>
  <li>Second not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Third not hidden</li>
</ul>

<ul id='two'>
  <li>First not hidden</li>
  <li class="hidden">First hidden</li>
  <li class="hidden">Second hidden</li>
  <li>Second not hidden</li>
  <li>Third not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Fourth not hidden</li>
</ul>

<ul id='two'>
  <li class="hidden">First hidden</li>
  <li>First not hidden</li>
  <li class="hidden">Second hidden</li>
  <li>Second not hidden</li>
  <li>Third not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Fourth not hidden</li>
</ul>

JavaScript:

$(document).ready(function() {
  var ulEls = document.querySelectorAll('ul');
  for (var i = 0; i < ulEls.length; i++) {
    ulEls[i].querySelector('li:not(.hidden)').setAttribute('style', 'color: red');
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id='one'>
  <li class="hidden">First hidden</li>
  <li class="hidden">Second hidden</li>
  <li>First not hidden</li>
  <li>Second not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Third not hidden</li>
</ul>

<ul id='two'>
  <li>First not hidden</li>
  <li class="hidden">First hidden</li>
  <li class="hidden">Second hidden</li>
  <li>Second not hidden</li>
  <li>Third not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Fourth not hidden</li>
</ul>

<ul id='two'>
  <li class="hidden">First hidden</li>
  <li>First not hidden</li>
  <li class="hidden">Second hidden</li>
  <li>Second not hidden</li>
  <li>Third not hidden</li>
  <li class="hidden">Third hidden</li>
  <li>Fourth not hidden</li>
</ul>

注意:我在上面的代码片段中使用了.each()函数或循环,因为我想说明它如何涵盖多种情况,但根据您的用途,您可能不需要它们。


虽然这样做可以起作用,但在覆盖具有相邻兄弟元素的样式时,它似乎太过于hackish。 - sigmaxf
如果你能找到更好的方法,请使用它 :) 据我所知,没有其他纯CSS的方法。 - Harry
@raphadko:正如Kit Sunde的回答所示,目前除了覆盖样式之外,没有纯CSS的方法。然而,根据您之前的评论,我理解您对使用它并不太感兴趣。您是否愿意使用JS / jQuery作为替代方案? - Harry
Jquery看起来很酷,我对列表进行了一些更改,使得隐藏的项目将移动到一个单独的列表中。 - sigmaxf
哦,太好了@raphadko。无论如何,这是一个不错的问题,回顾一下我在这里的第一条评论,似乎有点严厉。希望你没有因为语气而感到冒犯 :) - Harry
1
没关系,我喜欢知道什么是无法以最优方式完成的,这样我们就可以开始寻找不同的解决问题的方法。 - sigmaxf

1

:first-child针对父元素进行匹配,而不是第一个匹配的元素。你可以使用兄弟选择器来实现相同的效果,但是必须指定基准元素,因为没有办法跨整个列表仅匹配一次:

ul > li:not(.hidden):first-child,
ul > li.hidden + li:not(.hidden) {
  font-size: 10px;
}
ul > li:not(.hidden) ~ li.hidden + li:not(.hidden) {
  font-size: 1em;
}
<ul>
    <li>1
    <li class="hidden">2
    <li>3
    <li>4
</ul>
<ul>
    <li class="hidden">1
    <li class="hidden">2
    <li>3
    <li>4
</ul>

如果没有那个 :not,就会有一个有用的 CSS4 未来特性 nth-match,我们可以这样做:
li:nth-match(1 of :not(.hidden)) {
  font-size: 10px;
}

但出于性能考虑,它可能不允许嵌套:not


虽然这对于问题中显示的确切标记将起作用,但如果第一个元素本身未隐藏,则会失败。 - Harry
请注意,这仍然无法正常工作。请参考第一个代码片段以及第一和第三个如何被设置样式。 - Harry
我不理解。这可能是有意为之的,但似乎并不适用于所有情况。如果我过于挑剔,请见谅,我并不是说我的答案是正确的,而是想弄清楚是否有什么地方我没有理解。 - Harry
1
Harry是正确的。我想获取第一个未隐藏的元素,所以如果1被隐藏了,它将应用样式到第二个元素。如果1和2都被隐藏了,将应用于第三个元素,依此类推。 - sigmaxf
@Harry 我并不是在说你错了。我意识到这个问题,但我还没有完成编辑工作。 - Kit Sunde
显示剩余5条评论

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