Let's say I have a collection of 'items' like so:
<p class="item">Item 1</p>
<p class="item">Item 2</p>
<p class="item group">Item 3</p>
<p class="item group">Item 4</p>
<p class="item">Item 5</p>
I want to loop through the items and wrap a containing div around any that have the 'group' class to result in something like this (grouped items will always be right next to one another):
This is the script I've got:
var group = [];
$('.item').each(function(i, item) {
if ($(item).hasClass('group')) {
group.push(item);
}
});
$(group).wrap('<div class="wrapper" />');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="item">Item 1</p>
<p class="item">Item 2</p>
<div class="wrapper">
<p class="item group">Item 3</p>
<p class="item group">Item 4</p>
</div>
<p class="item">Item 5</p>
What happens is that the wrapping div is wrapped around each element separately in the array (which makes sense) but I need it to wrap all elements together. Is there any way I can do this? Here's a jsFiddle.
There is a more complex variation of this problem that is possible, this would be a situation where there are several 'sets' of these groups, each to be wrapped in their own 'group' div
. Initial state:
<p class="item">Item 1</p>
<p class="item">Item 2</p>
<p class="item group">Item 3</p>
<p class="item group">Item 4</p>
<p class="item">Item 5</p>
<p class="item group">Item 6</p>
<p class="item group">Item 7</p>
<p class="item group">Item 8</p>
<p class="item">Item 9</p>
Desired state:
<p class="item">Item 1</p>
<p class="item">Item 2</p>
<div class="wrapper">
<p class="item group">Item 3</p>
<p class="item group">Item 4</p>
</div>
<p class="item">Item 5</p>
<div class="wrapper">
<p class="item group">Item 6</p>
<p class="item group">Item 7</p>
<p class="item group">Item 8</p>
</div>
<p class="item">Item 9</p>
You can use a combination of
.filter
and.map
to achieve the desired result:See it in action.
Example on JS Bin to get around the current JSFiddle problems.
Rationale
The method
.wrap
embeds each item inside the current jQuery object inside the markup of your choice. It follows that if you want to wrap multiple elements in the same wrapper you have to match those N elements with a jQuery object and then create another jQuery object that contains one element: the first jQuery object. It is this latter object that you should pass to.wrap
.So what we need to do here is create one jQuery object for each group and then put all of those into another "master" jQuery object. Begin by selecting all
.group
elements that are not themselves preceded by a.group
sibling:From each such element, create a jQuery object that includes the element and all following siblings with
.group
:The resulting jQuery objects are automatically placed inside the "master" object because they take the place of the bare elements we selected with
.filter
inside the jQuery object we created with$(".item.group")
. A final call to.wrap
... wraps things up. :)Try
wrapAll
method instead:DEMO: http://jsfiddle.net/LanMt/3/
For wrapping the separate groups of
.group
elements you can use the following:DEMO: http://jsfiddle.net/LanMt/5/
The code above was assembled with the help of @Jon's answer.
Use wrapAll instead of wrap.
Documentation of wrapAll can be found at - http://api.jquery.com/wrapAll/
Other wrapping methods available can be found at - http://api.jquery.com/category/manipulation/dom-insertion-around/
EDIT:
For the complex case where there can be more than one groups, we can achieve it using wrapAll with a $.each as follows -