【问题标题】:jQuery alternative to nth-child selector that supports classesjQuery 替代支持类的 nth-child 选择器
【发布时间】:2017-03-04 09:28:09
【问题描述】:

我有这样的结构:

<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>

我只需要选择 .parent div 的第 n 个 .item div 类子节点(每个父节点的计数器都会重置)。

例如,我想选择每个 第三个“div.item” 元素,因此我希望影响内容为“Item 3”、“”的元素第 6 项”、“另一项 3”。

规则:

  • 所需的类始终应用于“div”元素(可能并不重要)。
  • 父母总是有“父”类,也总是“div”元素。
  • 在 div 中可以有其他 div(或任何其他类型的元素)具有随机类名(或没有),这些不得干扰第 n 个计数器。
  • 元素也可以嵌套,因此每个项目类元素还可以包含另一个父类元素,以及另一个项目类元素。

不幸的是 CSS 选择器

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

nth-child() 无法正常工作。虽然效果仅适用于具有给定类的元素,但计数本身是不正确的,因为它也计算没有给定类的元素。

由于我怀疑是否会有纯 CSS 解决方案,而且因为我实际上将其用作 jQuery 选择器,所以我想要一些简单的 jQuery 替代方案。谢谢你们能给我的任何帮助。

【问题讨论】:

  • 我认为您需要使用 $("div.parent").each() 分别处理每个父项,然后使用 $(this).children(".item") 获取所有项目,然后将它们循环 3。
  • 我认为你不能仅在 css 中做到这一点需要一些 javscript 支持
  • @PHPglue:正如您在这个小提琴link 中看到的那样,不幸的是它不起作用。还是我错过了什么?
  • nth-child() 是 parentNode 的第 n 个子节点。

标签: javascript jquery html css css-selectors


【解决方案1】:

您可以根据它们在父项中的索引过滤项目,相对于具有相同类的其他项目

$('.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>

【讨论】:

  • 这正是我想要的。简短而有效。我需要一点时间才能完全理解这个符号。 :)
【解决方案2】:

如果您想要某个 jQuery 集合的 nth 元素,您需要在该集合上使用 .eq() 选择器。如...

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>

以上内容将在整个收藏过程中保持您的计数(无论父母是谁)。如果您希望单独处理每个父母,请在$('.parent')s 上使用.each()。如...

$('.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>

【讨论】:

  • 这非常简单易懂。如果没有更短的答案并且没有 for 循环,我会将其完全标记为已接受的答案,所以无论如何非常感谢您!它功能完善,特别容易定制解决方案。
  • 我也喜欢@adeneo 的解决方案,它非常精致。谢谢你让我知道我的回答很有帮助。这就是 SO 的意义所在,恕我直言。干杯!
【解决方案3】:

我怀疑会有纯 CSS 解决方案

对于纯css 解决方案,您可以使用General siblings combinator 选择器~

两个序列表示的元素在 文档树和第一个序列表示的元素 在(不一定立即)由 第二个。

使用两个选择器。在第一个选择器中,匹配所需的元素。在第二个选择器匹配第一个选择器之后的元素时,为在第一个匹配元素处设置的属性设置默认值或使用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>

我只需要选择 .parent div 的第 n 个 .item div 类子项 (计数器为每个父节点重置)。

例如,我想选择每个 第三个“div.item” 元素,所以我 期望影响内容为 “Item 3”、“Item 6”的元素, “另一个项目 3”

最初并没有注意到要求每隔三个元素选择一次。

您仍然可以在 javascript 函数中将 css General sibling combinator 选择器与返回单个元素的 .querySelector() 一起使用以返回预期结果。

该函数当前接受父元素作为选择器字符串或DOM 元素,后代元素选择器,引用后代选择器选择之间距离的数字,调用每个匹配元素的回调,返回数组中的匹配元素。

forfor..of 循环内循环最多迭代childSelector.length / nth 次。选择器字符串是使用for 循环中的nth 数字参数构造的; .querySelector() 返回单个元素,然后递增 nth 参数,该参数创建选择器匹配 nth 元素从前一个匹配元素进行下一次迭代;消除了 for 循环迭代 parentSelector 的所有 childSelector.length 元素以匹配所需选择器的必要性。

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>

使用 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>

【讨论】:

  • 它没有选择第6项
  • @Mohammad Ah,错过了 OP 试图选择每三个元素
  • 非常感谢您提供这么长的可解释帖子。不把它标记为答案让我很伤心,尤其是当你付出这么多努力的时候。但这太复杂和复杂了。在我原来的帖子中,我要求一个简单的 jQuery 解决方案,否则我会自己想出更长的解决方案。但无论如何,谢谢你千百遍! :)
  • yield* Array.from([arg])?那只是yield arg
  • @Ryan 找到了“yield 关键字实际上返回一个 IteratorResult”developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
【解决方案4】:

nth-child 选择器对类不敏感。它按名称而不是按类选择元素。见jquery nth-child

:nth-child(n) 伪类很容易与 :eq(n) 混淆,尽管两者会导致匹配的元素截然不同。使用 :nth-child(n) 计算所有子元素,无论它们是什么,并且仅当指定的元素与附加到伪类的选择器匹配时才会被选中。使用 :eq(n) 只计算附加到伪类的选择器,不限于任何其他元素的子元素,并且选择第 (n+1) 个(n 从 0 开始)。

虽然 eq(n) 是类敏感的,但它不支持方程作为参数。

所以我认为没有直接的方法可以只使用 css 来实现它。

也可以用一些js代码试试。

【讨论】:

  • 好吧,我在我的原始帖子中解释了为什么nth-child 选择器在这种情况下不起作用。你是不对的,它按名称选择元素,不,它只选择子节点,它根本不关心任何名称。 nth-of-type 按名称选择元素。但无论如何谢谢:)
  • 你误会了。 nth-child 按子名称而不是类选择子节点。所以 div.item:nth-child(3n) 选择子 divs(name) 不是按类(item)。
  • 不正确,或者您可能只是不明白。我从来没有说过这个。您说 select 但这是在nth-child 过于笼统的情况下。简而言之,nth-child 做了两件事:1) 它计算子元素,并且在计算时它不关心名称,它只计算节点。再说一遍:在计数时,它并没有考虑到他们的名字/类型/等等。 2)一旦计数完成,它只对由前面的选择器(类/类型/等)标识的节点应用效果。如果您仍然不明白,请参阅文档。
  • 你是对的。我没有认识到 nth-child 仅适用于 child(node) 无论孩子是什么。哈哈。
【解决方案5】:

这对于纯 CSS 选择器是不可能的。但是,如果您可以灵活地使用唯一的标签名称而不是 div.item,这是可以实现的。这是您可以做的事情,如果您可以将 div.item 元素更改为 p 标签。

.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>

【讨论】:

  • 感谢您的回复。但不幸的是,我希望它是 div 元素。否则我也会使用nth-of-type :) 或者只是简单的 CSS 选择器。但无论如何感谢您的尝试!
猜你喜欢
  • 2011-09-03
  • 1970-01-01
  • 2012-07-29
  • 2013-04-04
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多