Đức Trần
Đức Trần

Reputation: 27

How to wrapAll element with the same class but different position

I have a problem with Javascript, my HTML list has five items with .item class, I want it to wrapAll by ul element but, when I use the wrapAll method of jQuery, so it wraps all .item elements with one ul, I want it to be a different ul separated by other li elements like the code below:

<ul>
   <li>Item 1</li>
   <li class='item'>Item 2</li>
   <li class='item'>Item 3</li>
   <li>Item 4</li>
   <li>Item 5</li>
   <li class='item'>Item 6</li>
   <li class='item'>Item 7</li>
   <li class='item'>Item 8</li>
   <li>Item 9</li>
   <li>Item 10</li>
</ul>

And I want to wrapAll the .item class like this:

<ul>
   <li>Item 1</li>
   <ul class='wrap'>
      <li class='item'>Item 2</li>
      <li class='item'>Item 3</li>
   </ul>
   <li>Item 4</li>
   <li>Item 5</li>
   <ul class='wrap'>
      <li class='item'>Item 6</li>
      <li class='item'>Item 7</li>
      <li class='item'>Item 8</li>
   </ul>
   <li>Item 9</li>
   <li>Item 10</li>
</ul>

I've tried this:

$('.item').wrapAll($('<ul/>',{class:'wrap'}));

but it goes like this:

<ul>
   <li>Item 1</li>
   <ul class='wrap'>
      <li class='item'>Item 2</li>
      <li class='item'>Item 3</li>
      <li class='item'>Item 6</li>
      <li class='item'>Item 7</li>
      <li class='item'>Item 8</li>
   </ul>
   <li>Item 4</li>
   <li>Item 5</li>
   <li>Item 9</li>
   <li>Item 10</li>
</ul>

Thanks for reading.

Upvotes: 2

Views: 105

Answers (2)

Nanoo
Nanoo

Reputation: 901

Here, I made a pure JavaScript solution:

var item = document.querySelectorAll("ul")[0].children;
var arr = [];
for (var i=0; i<item.length; i++) {
    if (item[i].className == "item") {
        arr.push(item[i]);
        var nextEle = item[i].nextElementSibling;
        if (nextEle.className !== "item") {
            var newGroup = document.createElement("UL");
            newGroup.setAttribute("class", "wrap");
            for (var x=0; x<arr.length; x++) {
                console.log(arr[x]);
                newGroup.appendChild(arr[x]);
            }
            nextEle.parentNode.insertBefore(newGroup, nextEle);
            arr = [];
        }
    }
}
<ul>
   <li>Item 1</li>
   <li class='item'>Item 2</li>
   <li class='item'>Item 3</li>
   <li>Item 4</li>
   <li>Item 5</li>
   <li class='item'>Item 6</li>
   <li class='item'>Item 7</li>
   <li class='item'>Item 8</li>
   <li>Item 9</li>
   <li>Item 10</li>
</ul>

This took me a while, but does exactly what you need.

Upvotes: 1

Nick Parsons
Nick Parsons

Reputation: 50974

One idea could be to select all li elements which do not have an item class which have an adjacent li item with an .item class. This essentially will give you all the li elements at the start of a group of items. You can then use .nextUntil(':not(.item)') on each starting li to get each li element after your first .item which has the .item class. This gives you all elements but your original starting element, so you need to use .addBack() to add the initial starting element to the collection. You can then wrap this using wrapAll():

$('li:not(.item) + li.item').each(function() {
  $(this).nextUntil(':not(.item)').addBack().wrapAll($('<ul/>',{class:'wrap'}));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
   <li>Item 1</li>
   <li class='item'>Item 2</li>
   <li class='item'>Item 3</li>
   <li>Item 4</li>
   <li>Item 5</li>
   <li class='item'>Item 6</li>
   <li class='item'>Item 7</li>
   <li class='item'>Item 8</li>
   <li>Item 9</li>
   <li>Item 10</li>
</ul>

Upvotes: 1

Related Questions