最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - jQuery alternative to nth-child selector that supports classes - Stack Overflow

programmeradmin6浏览0评论

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 of a .parent div (counter resets for every parent node).

For example I want to select every third "div.item" element so I'm expecting to affect elements with content "Item 3", "Item 6", "Another item 3".

Rules:

  • Desired classes are always applied to a "div" element (maybe not important).
  • Parents have always "parent" class and are also always "div" elements.
  • Amongst divs there can be other divs (or any other type of element) with random class name (or without) and these must not interfere with the nth counter.
  • Elements also can be nested so every item class element can in addition contain another parent class element and that again another item class elements.

Unfortunatelly CSS selector:

div.parent div.item:nth-child(3n)

with nth-child() is not working properly. Although the effects are applied only to elements with given class, the counting itself is not correct because it counts also elements without given class.

As I doubt that there will be pure CSS solution and also because I'm in fact using this as a jQuery selector, I would like some simple jQuery alternative. Thank you guys for any help you can give me.

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 of a .parent div (counter resets for every parent node).

For example I want to select every third "div.item" element so I'm expecting to affect elements with content "Item 3", "Item 6", "Another item 3".

Rules:

  • Desired classes are always applied to a "div" element (maybe not important).
  • Parents have always "parent" class and are also always "div" elements.
  • Amongst divs there can be other divs (or any other type of element) with random class name (or without) and these must not interfere with the nth counter.
  • Elements also can be nested so every item class element can in addition contain another parent class element and that again another item class elements.

Unfortunatelly CSS selector:

div.parent div.item:nth-child(3n)

with nth-child() is not working properly. Although the effects are applied only to elements with given class, the counting itself is not correct because it counts also elements without given class.

As I doubt that there will be pure CSS solution and also because I'm in fact using this as a jQuery selector, I would like some simple jQuery alternative. Thank you guys for any help you can give me.

Share Improve this question asked Oct 22, 2016 at 1:00 Martin L.Martin L. 1249 bronze badges 4
  • I think you'll need to use $("div.parent").each() to process each parent separately, then use $(this).children(".item") to get all the items, and then loop over them by 3. – Barmar Commented Oct 22, 2016 at 1:10
  • I think you cannot do this in css alone need some javscript support – Geeky Commented Oct 22, 2016 at 1:12
  • @PHPglue: As you can see in this fiddle link it's not working unfortunately. Or am I missing something? – Martin L. Commented Oct 22, 2016 at 1:35
  • nth-child() is nth child of parentNode. – StackSlave Commented Oct 22, 2016 at 2:04
Add a ment  | 

5 Answers 5

Reset to default 5

You can filter the items based on the index they have in the parent, in relation to other items 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./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 want nth element of some jQuery collection you need to use the .eq() selector on that collection. As 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./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 keep your count throughout the collection (irrespective of parents). If you want each parent dealt with separately, use an .each() on $('.parent')s. As 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./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 that there will be pure CSS solution

For a pure css solution you can utilize General siblings binator selector ~.

The elements represented by the two sequences share the same parent in the document tree and the element represented by the first sequence precedes (not necessarily immediately) the element represented by the second one.

Use two selectors. At first selector, match required element. At second selector match elements following first selector, set default value for properties set at first matched 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 nth .item div class child of a .parent div (counter resets for every parent node).

For example I want to select every third "div.item" element so I'm expecting to affect elements with content "Item 3", "Item 6", "Another item 3".

Did not initially notice that requirement was to select every third element.

You can still utilize css General sibling binator selector with .querySelector(), which returns a single element, within a javascript function to return expected result.

The function currently accepts a parent element as selector string or DOM element, descendant element selector, number referencing the distance between selections of descendant selector, a callback to call for each matched element, returns matched elements within an array.

for loop within for..of loop iterates a maximum of childSelector.length / nth times. A selector string is constructed using the nth number parameter within the for loop; .querySelector() returns the single element, then increments the nth parameter which creates selector matching nth element from previous matched element for next iteration; eliminating the necessity for for loop to iterate all childSelector.length elements of 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./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>

nth-child selector is not class sensitive. It selects element by name not by class. See jquery nth-child

The :nth-child(n) pseudo-class is easily confused with :eq(n), even though the two can result in dramatically different matched elements. With :nth-child(n), all children are counted, regardless of what they are, and the specified element is selected only if it matches the selector attached to the pseudo-class. With :eq(n) only the selector attached to the pseudo-class is counted, not limited to children of any other element, and the (n+1)th one (n is 0-based) is selected.

Though eq(n) is class sensitive but it not supports equation as parameter.

So I think there's no direct way to use css only to implement it.

Try it also with some js codes.

This isn't possible with pure CSS selectors. However, this is something achievable if you have the flexibility to use a unique tag name instead of div.item. Here is something 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>

发布评论

评论列表(0)

  1. 暂无评论