Reputation: 4953
Is there a way to select every nth child that matches (or does not match) an arbitrary selector? For example, I want to select every odd table row, but within a subset of the rows:
table.myClass tr.row:nth-child(odd) {
...
}
<table class="myClass">
<tr>
<td>Row
<tr class="row"> <!-- I want this -->
<td>Row
<tr class="row">
<td>Row
<tr class="row"> <!-- And this -->
<td>Row
</table>
But :nth-child()
just seems to count all the tr
elements regardless of whether or not they're of the "row" class, so I end up with the one even "row" element instead of the two I'm looking for. The same thing happens with :nth-of-type()
.
Can someone explain why?
Upvotes: 155
Views: 49392
Reputation: 417
The main issue here is that you're trying to programmatically do something arbitrary, which by definition has no predictable logic.
yes, the accepted answer of table.myClass tr:nth-child(odd of .row)
will accomplish the task, and satisfies your immediate needs, but a practical solution can only be derived from why you're skipping that first table row.
I'm assuming you're skipping the first row because it's actually a table header, in which case, your table is formatted incorrectly
<table class="myClass">
<tr>
<td>Row
<tr class="row">
<td>Row
<tr class="row">
<td>Row
<tr class="row">
<td>Row
</table>
is more properly formatted as
<table class="myClass">
<thead>
<tr>
<th>Row
<tbody>
<tr class="row">
<td>Row
<tr class="row">
<td>Row
<tr class="row">
<td>Row
</table>
then the desired effect can be accomplished with the selector table.myClass tbody .row:nth-child(odd)
that's dumb... this is more work, why would I do this when I can just use the new syntax?
Upvotes: 0
Reputation: 723628
The support of the new syntax is pretty good now so you can use:
table.myClass tr:nth-child(odd of .row)
This is a very common problem that arises due to a misunderstanding of how :nth-child(An+B)
and :nth-of-type()
work.
In Selectors Level 3, the :nth-child()
pseudo-class counts elements among all of their siblings under the same parent. It does not count only the siblings that match the rest of the selector.
Similarly, the :nth-of-type()
pseudo-class counts siblings sharing the same element type, which refers to the tag name in HTML, and not the rest of the selector.
This also means that if all the children of the same parent are of the same element type, for example in the case of a table body whose only children are tr
elements or a list element whose only children are li
elements, then :nth-child()
and :nth-of-type()
will behave identically, i.e. for every value of An+B, :nth-child(An+B)
and :nth-of-type(An+B)
will match the same set of elements.
In fact, all simple selectors in a given compound selector, including pseudo-classes such as :nth-child()
and :not()
, work independently of one another, rather than looking at the subset of elements that are matched by the rest of the selector.
This also implies that there is no notion of order among simple selectors within each individual compound selector1, which means for example the following two selectors are equivalent:
table.myClass tr.row:nth-child(odd)
table.myClass tr:nth-child(odd).row
Translated to English, they both mean:
Select any
tr
element that matches all of the following independent conditions:
- it is an odd-numbered child of its parent;
- it has the class "row"; and
- it is a descendant of a
table
element that has the class "myClass".
(you'll notice my use of an unordered list here, just to drive the point home)
Selectors level 4 seeks to rectify this limitation by allowing :nth-child(An+B of S)
2 to accept an arbitrary selector argument S, again due to how selectors operate independently of one another in a compound selector as dictated by the existing selector syntax. So in your case, it would look like this:
table.myClass tr:nth-child(odd of .row)
You can also use a script to filter elements and apply styles or extra class names accordingly. For example, the following is a common workaround using jQuery (assuming there is only one row group populated with tr
elements within the table):
$('table.myClass').each(function() {
// Note that, confusingly, jQuery's filter pseudos are 0-indexed
// while CSS :nth-child() is 1-indexed
$('tr.row:even').addClass('odd');
});
With the corresponding CSS:
table.myClass tr.row.odd {
...
}
If you're using automated testing tools such as Selenium or scraping HTML with tools like BeautifulSoup, many of these tools allow XPath as an alternative:
//table[contains(concat(' ', @class, ' '), ' myClass ')]//tr[contains(concat(' ', @class, ' '), ' row ')][position() mod 2)=1]
Other solutions using different technologies are left as an exercise to the reader; this is just a brief, contrived example for illustration.
1 If you specify a type or universal selector, it must come first. This does not change how selectors fundamentally work, however; it's nothing more than a syntactic quirk.
2 This was originally proposed as :nth-match()
, however because it still counts an element relative only to its siblings, and not to every other element that matches the given selector, it has since as of 2014 been repurposed as an extension to the existing :nth-child()
instead.
Upvotes: 186
Reputation: 10500
table.myClass tr:nth-child(odd of .row) {}
Generalized version (spec):
:nth-child(<nth> [of <selector>]?) {}
...in which <nth>
is 2
, 3n + 1
, -n + 3
, odd
or other valid values and <selector>
is a list of selectors, which may be complex.
At the time of writing, this feature is only supported in Chrome 111+ (2023/03/07, also for Android), Firefox 113+ (2023/05/09, also for Android), Edge 111+ (2023/03/13) and Safari 9+ (2015/10/01, also for iOS), according to caniuse.com.
Try it:
/*** First example ***/
/* All odds (2n + 1) */
#example-1 > :nth-child(odd of .foo) {
background: red;
}
/* Odd evens (4n + 2) */
/* Note how .foo:not(...) is evaluated first */
#example-1 > :nth-child(odd of .foo:not(:nth-child(odd of .foo))) {
background: yellow;
}
/*** Second example ***/
/* This works */
#example-2 > :nth-of-type(3) {
background: red;
}
/* This too */
#example-2 > :nth-child(4 of div) {
background: yellow;
}
/* And so will this */
#example-2 > :nth-last-child(11 of div) {
background: green;
}
/* Use :nth-last-child() to select from last */
/* First 2 of last 3 of <div>s */
#example-2 > :nth-child(-n + 2 of :nth-last-child(-n + 3 of div)) {
background: cyan;
}
/* 1st, 4th, 7th... from last */
#example-2 > :nth-last-child(3n + 1 of div) {
text-decoration: underline;
}
/* ...of which odd elements will be selected */
#example-2 > :nth-last-child(odd of :nth-last-child(3n + 1)) {
outline: 3px solid black;
}
/*** Other styles ***/
.flex {
display: flex;
flex-flow: row wrap;
}
.grid {
display: grid;
grid-template: repeat(5, 1fr) / repeat(5, 1fr);
}
.flex > div,
.grid > div {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100px;
width: 100px;
text-align: center;
}
/* 2nd, 3rd, 4th... of both .flex and .grid */
body > :nth-child(n + 2 of .flex, .grid) {
gap: 10px;
}
/* First 1 of [2nd, 3rd, 4th...] <div>s */
body > :nth-child(-n + 1 of :nth-child(n + 2 of div)) > div {
background: #ddd;
}
/* 2nd of last 2 of [1st, 2nd, 3rd...] non-<script> from last */
/* ...which means 1st from last */
:nth-child(odd of :nth-last-child(-n + 3 of body > :not(script))) > .foo {
border-radius: 50%;
}
:nth-child(odd of :nth-last-child(-n + 3 of body > :not(script))) > .foo::after {
content: '.foo';
display: block;
}
<div class="flex" id="example-1">
<div>odd</div>
<div>even</div>
<div class="foo">1st odd</div>
<div class="foo">1st even</div>
<div class="foo">2nd odd</div>
<div class="foo">2nd even</div>
<div class="foo">3rd odd</div>
<div class="foo">3rd even</div>
</div>
<hr>
<div class="flex" id="example-2">
<div>1 (15)</div>
<div class="foo">2 (14)</div>
<div>3 (13)</div>
<div class="foo">4 (12)</div>
<div>5 (11)</div>
<div>6 (10)</div>
<div class="foo">7 (9)</div>
<div>8 (8)</div>
<div>9 (7)</div>
<div class="foo">10 (6)</div>
<div class="foo">11 (5)</div>
<div>12 (4)</div>
<div class="foo">13 (3)</div>
<div>14 (2)</div>
<div>15 (1)</div>
</div>
Upvotes: 4
Reputation: 1
Not an answer to "Can someone explain why?" since other answers has explained.
But as one possible solution to your situation, you may use custom tags for the rows and cells, say <tr-row>
, <td-row>
, then :nth-of-type()
should work. Don't forget to set style display: table-row;
and display: table-cell;
respectively to make them still work like table cells.
Upvotes: -1
Reputation: 11
IF you have same parent class for all selector, Then you use that class document.querySelector("main .box-value:nth-child(3) select.priorityOption");
Because in that case document.querySelector("main .box-value select.priorityOption:nth-child(3)");
Not working. Thank You
<div class="card table">
<div class="box">
<div class="box-value">
<select class="priorityOption">
<option value="">--</option>
<option value="">LOREM</option>
<option value="">LOREM</option>
</select>
</div>
<div class="box-value">
<select class="priorityOption">
<option value="">--</option>
<option value="">LOREM</option>
<option value="">LOREM</option>
</select>
</div>
<div class="box-value">
<select class="priorityOption">
<option value="">--</option>
<option value="">LOREM</option>
<option value="">LOREM</option>
</select>
</div>
</div>
</div>
Upvotes: 1
Reputation: 1103
All of the questions around using nth-child and skipping hidden tags appear to be redirecting as dupes of this one so I will leave this here. I came across this blog https://blog.blackbam.at/2015/04/09/css-nth-child-selector-ignore-hidden-element/ that uses a clever css approach to make nth-child ignore hidden elements, as follows:
The following CSS adds a margin right to every second visible element no matter which element has the cpw class.
.cpw {
display:none;
}
.video_prewrap {
margin-right:20px;
}
.video_prewrap:nth-child(2n) {
margin-right:0;
}
.cpw ~ .video_prewrap:nth-child(2n) {
margin-right:20px;
}
.cpw ~ .video_prewrap:nth-child(2n-1) {
margin-right:0;
}
Hope that helps someone who is following the dupe trail for the ignore hidden elements questions!
Upvotes: 1
Reputation: 415
Here is your answer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TEST</title>
<style>
.block {
background: #fc0;
margin-bottom: 10px;
padding: 10px;
}
/* .large > .large-item:nth-of-type(n+5) {
background: #f00;
} */
.large-item ~ .large-item ~ .large-item ~ .large-item ~ .large-item {
background: #f00;
}
</style>
</head>
<body>
<h1>Should be the 6th Hello Block that start red</h1>
<div class="small large">
<div class="block small-item">Hello block 1</div>
<div class="block small-item large-item">Hello block 2</div>
<div class="block small-item large-item">Hello block 3</div>
<div class="block small-item large-item">Hello block 4</div>
<div class="block small-item large-item">Hello block 5</div>
<div class="block small-item large-item">Hello block 6</div>
<div class="block small-item large-item">Hello block 7</div>
<div class="block small-item large-item">Hello block 8</div>
</div>
</body>
</html>
Upvotes: 1
Reputation: 43052
You may be able to do that with xpath. something like //tr[contains(@class, 'row') and position() mod 2 = 0]
might work. There are other SO questions expanding on the details how to match classes more precisely.
Upvotes: 1
Reputation: 10187
nth-of-type
works according to the index of same type of the element but nth-child
works only according to index no matter what type of siblings elements are.
For example
<div class="one">...</div>
<div class="two">...</div>
<div class="three">...</div>
<div class="four">...</div>
<div class="five">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
Suppose in above html we want to hide all the elements having rest class.
In this case nth-child
and nth-of-type
will work exactly same as all the element are of same type that is <div>
so css should be
.rest:nth-child(6), .rest:nth-child(7), .rest:nth-child(8), .rest:nth-child(9), .rest:nth-child(10){
display:none;
}
OR
.rest:nth-of-type(6), .rest:nth-of-type(7), .rest:nth-of-type(8), .rest:nth-of-type(9), .rest:nth-of-type(10){
display:none;
}
Now you must be wondering what is the difference between nth-child
and nth-of-type
so this is the difference
Suppose the html is
<div class="one">...</div>
<div class="two">...</div>
<div class="three">...</div>
<div class="four">...</div>
<div class="five">...</div>
<p class="rest">...</p>
<p class="rest">...</p>
<p class="rest">...</p>
<p class="rest">...</p>
<p class="rest">...</p>
In the above html the type of .rest
element is different from others .rest
are paragraphs and others are div so in this case if you use nth-child
you have to write like this
.rest:nth-child(6), .rest:nth-child(7), .rest:nth-child(8), .rest:nth-child(9), .rest:nth-child(10){
display:none;
}
but if you use nth-of-type css can be this
.rest:nth-of-type(1), .rest:nth-of-type(2), .rest:nth-of-type(3), .rest:nth-of-type(4), .rest:nth-of-type(5){
display:none;
}
As type of
.rest
element is<p>
so herenth-of-type
is detecting the type of.rest
and then he applied css on the 1st, 2nd, 3rd, 4th, 5th element of<p>
.
Upvotes: 2
Reputation: 195992
Not really..
The
:nth-child
pseudo-class matches an element that has an+b-1 siblings before it in the document tree, for a given positive or zero value for n, and has a parent element.
It is a selector of its own and does not combine with classes. In your rule it just has to satisfy both selector at the same time, so it will show the :nth-child(even)
table rows if they also happen to have the .row
class.
Upvotes: 8