JQuery alternative to nth-child selector that supports classes
I have a structure like this:
<div class="parent"> <div class="randomclass">...</div> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <div class="randomclassdifferentname">...</div> <div class="item">Item 4</div> <div class="item">Item 5</div> <div class="item">Item 6</div> <div class="item">Item 7</div> ... </div> <div class="parent"> <div class="anotherclass">...</div> <div class="item">Another item 1</div> <div class="item">Another item 2</div> <div>...</div> <div class="item">Another item 3</div> ... </div> I need to select only nth .item div class child .parent div (counter is reset for each parent node).
For example, I want to select every third div.item element , so I expect impact on the elements with the content of " Paragraph 3 ", " Paragraph 6 ", " Another element 3 ".
Rules:
- Desired classes are always applied to the div element (maybe not important).
- Parents always have a "parent" class and are also always div elements.
- Among divs there may be other divs (or any other type of element) with a random class name (or without), and they should not interfere with the nth counter.
- Elements can also be nested, so each element of an element class can additionally contain another element of the parent class and again elements of another element of the class.
Unfortunatelly CSS Selector :
div.parent div.item:nth-child(3n) with nth-child () does not work correctly. Although effects apply only to elements with a given class, the calculation itself is not correct, since it also considers elements without a given class.
As I doubt that there will be a pure CSS solution, and also because I actually use this as a jQuery selector, I would like a simple jQuery alternative. Thanks guys for any help you can give me.
You can filter elements based on the index that they have in the parent object, in relation to other elements with the same class
$('.item').filter(function(_,item) { return ($(item).siblings('.item').addBack().index(item)+1) % 3 === 0; }).css('color','red'); <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="parent"> <div class="randomclass">...</div> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <div class="randomclassdifferentname">...</div> <div class="item">Item 4</div> <div class="item">Item 5</div> <div class="item">Item 6</div> <div class="item">Item 7</div> ... </div> <div class="parent"> <div class="anotherclass">...</div> <div class="item">Another item 1</div> <div class="item">Another item 2</div> <div>...</div> <div class="item">Another item 3</div> ... </div> If you need the nth element of some jQuery collection, you need to use the .eq() selector in this collection. How in...
var allItems = $('.parent').find('.item'); for (i = 1; i <= allItems.length/3; i++) { allItems.eq((i*3)-1).css({'border':'1px solid red'}) } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="parent"> <div class="randomclass">...</div> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <div class="randomclassdifferentname">...</div> <div class="item">Item 4</div> <div class="item">Item 5</div> <div class="item">Item 6</div> <div class="item">Item 7</div> ... </div> <div class="parent"> <div class="anotherclass">...</div> <div class="item">Another item 1</div> <div class="item">Another item 2</div> <div>...</div> <div class="item">Another item 3</div> ... </div> The above will contain your account in the entire collection (regardless of parents). If you want each parent to deal separately, use .each() on $('.parent') s. How in...
$('.parent').each( function(){ var theseItems = $(this).find('.item'); for (i = 1; i <= theseItems.length/3; i++) { theseItems.eq((i*3)-1).css({border:'1px solid red'}) } }) <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="parent"> <div class="randomclass">...</div> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <div class="randomclassdifferentname">...</div> <div class="item">Item 4</div> <div class="item">Item 5</div> <div class="item">Item 6</div> <div class="item">Item 7</div> ... </div> <div class="parent"> <div class="anotherclass">...</div> <div class="item">Another item 1</div> <div class="item">Another item 2</div> <div>...</div> <div class="item">Another item 3</div> ... </div> As I doubt there will be a clean CSS solution
For a clean css solution, you can use the General siblings combinator ~ selector.
Elements represented by two sequences have the same parent element in the document tree and the element represented by the first sequence is preceded (not necessarily immediately) by the element represented by the second.
Use two selectors. First select the selector that matches the item you want. On the second selector elements following the first selector, set the default value for the properties specified in the first matching element, or use unset .
/* match third `div.item` */ .parent div.item ~ div.item ~ div.item { color: sienna; font-size: 2em; } /* match fourth through last `div.item` */ .parent div.item ~ div.item ~ div.item ~ div.item { color: unset; font-size: unset; } <div class="parent"> <div class="randomclass">...</div> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <div class="randomclassdifferentname">...</div> <div class="item">Item 4</div> <div class="item">Item 5</div> <div class="item">Item 6</div> <div class="item">Item 7</div> ... </div> <div class="parent"> <div class="anotherclass">...</div> <div class="item">Another item 1</div> <div class="item">Another item 2</div> <div>...</div> <div class="item">Another item 3</div> <div class="item">Another item 4</div> <div class="item">Another item 5</div> ... </div> I need to select only the nth child element of the .item div from the .parent div (the counter is reset for each parent node).
For example, I want to select every third div.item element , so that I would be expecting impact on the elements with the contents of "Paragraph 3", "Paragraph 6", "Another element 3" .
I did not initially notice that it was required to select every third element.
You can still use the css General sibling combinator selector with .querySelector() , which returns a single element in the javascript function to return the expected result.
Currently, the function accepts the parent element as a selector string or DOM element, a descendant element selector, a number that refers to the distance between the descendant selector choices, a callback to call each matched element, returns matched elements in the array.
for inside the for..of loop for..of at most childSelector.length / nth times. The selector string is constructed using the nth number parameter in a for loop; .querySelector() returns one element and then increments the nth parameter, which creates a selector matching the nth element from the previous matched element for the next iteration; eliminating the need for for loop to iterate childSelector.length all the elements of childSelector.length parentSelector to match the required selectors.
const gen = function* (arg) { yield* arg[Symbol.iterator] ? arg : [arg] }; window.onload = function() { // `parentSelector`: `".parent"`, `document.querySelector(".parent")`, // `document.querySelectorAll(".parent"), // `document.getElementsByClassName(".parent")` // `childSelector`: `".item"`; `nth`: `3`; `callback`: function function nthElement(parentSelector, childSelector, nth, callback) { let [nthparents, selector, n, items] = [ typeof parentSelector === "string" ? document.querySelectorAll(parentSelector) : [...gen(parentSelector)] , childSelector , nth , [] ]; for (let nthp of nthparents) { for (let i = n; i <= nthp.querySelectorAll(selector).length; i += n) { let item = nthp.querySelector(Array(i).fill(selector).join("~")); items.push(item); callback.call(item, i, item, nthp) } }; return items } // select every `nth` third `.item` element // that is a child of `.parent` element let items = nthElement(document.querySelectorAll(".parent"), ".item", 3 , function (i, nth, nthParent) { console.log(i, nth, nthParent); this.style.color = "sienna"; this.style.fontSize = "2em"; }); console.log(items); } <div class="parent"> <div class="randomclass">...</div> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <div class="randomclassdifferentname">...</div> <div class="item">Item 4</div> <div class="item">Item 5</div> <div class="item">Item 6</div> <div class="item">Item 7</div> ... <div class="randomclass">...</div> <div class="randomclass">...</div> <div class="randomclass">...</div> <div class="item">Item 8</div> </div> <div class="parent"> <div class="anotherclass">...</div> <div class="item">Another item 1</div> <div class="item">Another item 2</div> <div>...</div> <div class="item">Another item 3</div> <div class="anotherclass">...</div> <div class="item">Another item 4</div> <div class="item">Another item 5</div> ... <div class="anotherclass">...</div> <div class="anotherclass">...</div> <div class="anotherclass">...</div> <div class="item">Another item 6</div> </div> Using jQuery
$(function() { function nthElement(childSelector, nth, callback) { let [nthparents, selector, n, items] = [ Array.from(this) , childSelector , nth , [] ]; for (let nthp of nthparents) { for (let i = n; i <= nthp.querySelectorAll(selector).length; i += n) { let item = nthp.querySelector(Array(i).fill(selector).join("~")); items.push(item); callback.call(item, i, item, nthp) } }; return jQuery(items) } // set `nthElement` as a jQuery method $.fn.extend({nthElement: nthElement}); // select every third `.item` element that is a child of `.parent` element var items = $(".parent").nthElement(".item", 3, function(i, nth, nthParent) { console.log(i, nth, nthParent); $(this).css({color: "sienna",fontSize: "2em"}); }); console.log(items); }); <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"> </script> <div class="parent"> <div class="randomclass">...</div> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <div class="randomclassdifferentname">...</div> <div class="item">Item 4</div> <div class="item">Item 5</div> <div class="item">Item 6</div> <div class="item">Item 7</div> ... <div class="randomclass">...</div> <div class="randomclass">...</div> <div class="randomclass">...</div> <div class="item">Item 8</div> </div> <div class="parent"> <div class="anotherclass">...</div> <div class="item">Another item 1</div> <div class="item">Another item 2</div> <div>...</div> <div class="item">Another item 3</div> <div class="anotherclass">...</div> <div class="item">Another item 4</div> <div class="item">Another item 5</div> ... <div class="anotherclass">...</div> <div class="anotherclass">...</div> <div class="anotherclass">...</div> <div class="item">Another item 6</div> </div> The nth-child selector is not class sensitive. He selects an element by name not by class. See jquery nth-child
The nth-child (n) pseudo-class is easily confused with: eq (n), although the two can lead to completely different matched elements. Using nth-child (n), all child elements are taken into account, regardless of what they are, and the specified element is selected only if it matches the selector attached to the pseudo-class. Using: eq (n), only the selector attached to the pseudo-class, not limited to the children of any other element, is counted, and the (n + 1) -th one (n is the 0-base) is selected.
Although eq (n) is class sensitive, it does not support the equation as a parameter.
So, I think there is no direct way to use css only to implement it.
Try also with some js codes.
This is not possible with pure CSS selectors. However, this is possible if you have the flexibility to use a unique tag name instead of div.item. Here is what you could do if you could change the div.item elements to a p tag.
.parent p:nth-of-type(3n) { color: red; } .parent { padding: 10px; border: 1px solid black; } .item { border: 1px dotted black; } <div class="parent"> <div class="randomclass">Random</div> <p class="item">Item 1</p> <p class="item">Item 2</p> <p class="item">Item 3</p> <div class="randomclassdifferentname">Random different Name</div> <p class="item">Item 4</p> <p class="item">Item 5</p> <p class="item">Item 6</p> <p class="item">Item 7</p> </div> <div class="parent"> <div class="anotherclass">Another Random</div> <p class="item">Another item 1</p> <p class="item">Another item 2</p> <div>Random Text</div> <p class="item">Another item 3</p> </div>