I need to target a descendant class (let’s call it inner
) of an outer
class, but only the first element with the inner
class (which is not outer’s first child). An arbitrary number of elements and levels of nesting may appear prior to the first occurrence of inner
inside outer
, and after that inner
may (re)appear multiple times. A simplified HTML structure follows (with my failed attempt to use :nth-child()
:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style>
.outer :nth-child(1 of .inner) {
color:red;
}
</style>
</head>
<body>
<div class="outer">
<!-- Arbitrary number of elements may happen here -->
<div>
<!-- Arbitrary number of elements may happen here -->
<div class="inner">
<span>Should BE affected!</span>
</div>
</div>
<!-- Arbitrary number of elements may happen here -->
<div>
<!-- Arbitrary number of elements may happen here -->
<div>
<!-- Arbitrary number of elements may happen here -->
<div class="inner">
<span>Should NOT be affected!</span>
</div>
</div>
</div>
</div>
</body>
</html>
In this example inner
appears only twice, but it can appear multiple times.
I can’t control the HTML generated, all I can do is add the .outer
class to the outer div (the inner
class actually is added by the control rendering library I’m using, which I don’t control – no pun intended).
So I need to target this "first" inner
element, and ignore all other instances of it. Am I approaching this the wrong (CSS) way? How can I achieve that?
3
Answers
Basically, you cant.
nth-child
will always select from its own parent container not other parents or ancestors. You need JavaScript.jQuery has a process (
.first
) for your requirementIn CSS, there is currently no parent selector or a previous sibling selector that allows one to select an element based on a descendant. However, we can achieve what you’re looking for by using a combination of the :first-child and :not pseudo-classes.
Here’s how we can do it:
This selector targets all elements with the class .inner that are direct children of an element with the class .outer, but only if they are not the first child of their parent. This effectively selects only the second occurrence of .inner and any subsequent occurrences, while excluding the first one.
The second occurrence of .inner (inside the nested div) will be affected, and the first occurrence (inside the immediate child div) will be ignored.
The :not pseudo-class is used to negate the :first-child condition, allowing to select only the elements we want.
So, you’re selecting the .inner element that:
I.e.
.outer .inner:not(.inner ~ *):not(:has(.inner) ~ *):not(.inner ~ * *):not(:has(.inner) ~ * *)