Wrapping consecutive list items in separate groups

2019-06-25 14:55发布

问题:

I have an unordered list exported by a CMS and need to identify <li> elements that have the class .sub and wrap them in a <ul>.

I have tried the wrapAll() method but that finds all <li class="sub"> elements and wraps them in one <ul>. I need it to maintain seperate groupings.

The exported code is as follows:

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

I need it to be:

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

Any help would be greatly appreciated.

回答1:

  1. Use .each to walk through all .sub elements.
  2. Ignore elements whose parent has class wrapped, using hasClass()
  3. Use nextUntil(:not(.sub)) to select all consecutive sub elements (include itself using .andSelf()).
    The given first parameter means: Stop looking forward when the element does not match .sub.
  4. wrapAll

Demo: http://jsfiddle.net/8MVKu/

For completeness, I have wrapped the set of <li> elements in <li><ul>...</ul></li> instead of a plain <ul>.

Code:

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


回答2:

I would like to expand on Rob W's already awesome solution, providing a way to eliminate the temporary wrap class:

$(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/

Edit: The filter isn't even needed:

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


回答3:

I believe what you should have is this jQuery:

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

This will properly wrap your <li> elements in a new <ul> tag while wrapped them within <li> tags. The output of your example would then be:

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