使用jQuery将连续的列表项分别包装成不同的组

4
我有一个由CMS导出的无序列表,需要识别具有类.sub的
  • 元素,并将它们包装在
      中。
      我尝试过wrapAll()方法,但是它会找到所有的
    • 元素,并将它们都包装在一个
        中。我需要它保持分组。
        导出的代码如下:
        <ul>
          <li></li>
          <li></li>
          <li></li>
          <li class="sub"></li>
          <li class="sub"></li>
          <li class="sub"></li>
          <li></li>
          <li></li>
          <li class="sub"></li>
          <li class="sub"></li>
          <li class="sub"></li>
          <li></li>
         </ul>
        

        我需要它变成:

        <ul>
          <li></li>
          <li></li>
          <li></li>
          <ul>
             <li class="sub"></li>
             <li class="sub"></li>
             <li class="sub"></li>
          </ul>
          <li></li>
          <li></li>
          <li></li>
          <ul>
             <li class="sub"></li>
             <li class="sub"></li>
             <li class="sub"></li>
           </ul>
           <li></li>
           <li></li>
        </ul>
        

        任何帮助都将不胜感激。

  • 3
    你的最终结果是无效的HTML。嵌套的<ul>元素应该在<li>元素内部。 - j08691
    1
    @j08691关于生成的HTML无效是正确的。我建议重新考虑你正在做什么。 - Reinstate Monica Cellio
    3个回答

    7
    1. 使用.each遍历所有.sub元素。
    2. 使用hasClass()忽略父元素的类为wrapped的元素。
    3. 使用nextUntil(:not(.sub))选择所有连续的.sub元素(包括自身,使用.andSelf())。
      给定的第一个参数的意思是:当元素不匹配.sub时停止向前查找。
    4. wrapAll

    演示:http://jsfiddle.net/8MVKu/

    为了完整起见,我已经将<li>元素集合包装在<li><ul>...</ul></li>中,而不是普通的<ul>

    代码:

    $('.sub').each(function() {
       if ($(this.parentNode).hasClass('wrapped')) return;
       $(this).nextUntil(':not(.sub)').andSelf().wrapAll('<li><ul class="wrapped">');
    });
    $('ul.wrapped').removeClass('wrapped'); // Remove temporary dummy
    

    太棒了,我希望我能给+10。 - mrtsherman
    我也在写同样的东西,但是将它们附加到前一个项目而不是新项目。干得好! - kwelch

    5
    我希望能够扩展Rob W已经提供的出色解决方案,为消除临时包装类提供一种方法:
    $(document).ready(function() {
        $('li.sub').filter(':not(li.sub + li.sub)').each(function() {
            $(this).nextUntil(':not(li.sub)').andSelf().wrapAll('<li><ul>');
        });
    });
    

    http://jsfiddle.net/m8yW3/

    编辑:该过滤器甚至不需要:

    $('li.sub:not(li.sub + li.sub)').each(function() {
        $(this).nextUntil(':not(li.sub)').andSelf().wrapAll('<li><ul>');
    });
    

    看起来不错。我认为使用过滤器的双重循环(过滤器遍历所有元素,并针对选择器进行测试)并不比使用临时类更有效率。 - Rob W
    很好的观点,Rob。我意识到过滤器甚至不是必要的,并在编辑中将我的代码改为单个选择器。 - Dan A.
    @DanA. 你能解释一下:not(li.sub + li.sub)这部分的含义吗?你的解决方案很干净,也很好用,但我想了解一下那部分实际上是什么意思。我可以看到它防止了UL元素的继续嵌套,但是我无法弄清楚它是如何做到的。谢谢。 - user1447679
    @user1447679 :not(li.sub + li.sub) 的意思是不选择紧接在另一个 li.sub 元素后面的 li.sub 元素。因此,基本上它从每个分组中仅选择第一个 li.sub - Dan A.

    0

    我相信你需要的是这个jQuery:

    $('li.sub').wrap('<li><ul>');

    此方法可以将你的<li>元素正确包装在一个新的<ul>标签中,同时也将它们包裹在<li>标签内。你的示例输出如下:

    <ul>
      <li></li>
      <li></li>
      <li></li>
      <li><ul><li class="sub"></li></ul></li>
      <li><ul><li class="sub"></li></ul></li>
      <li><ul><li class="sub"></li></ul></li>
      <li></li>
      <li></li>
      <li><ul><li class="sub"></li></ul></li>
      <li><ul><li class="sub"></li></ul></li>
      <li><ul><li class="sub"></li></ul></li>
      <li></li>
     </ul>
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接