Can I $.wrap() around a collection of elements in

2020-06-17 01:50发布

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>

3条回答
不美不萌又怎样
2楼-- · 2020-06-17 02:04

You can use a combination of .filter and .map to achieve the desired result:

$(".item.group")
.filter(function() {
    return !$(this).prev().is(".group");
})
.map(function() {
    return $(this).nextUntil(":not(.group)").andSelf();
})
.wrap('<div class="wrap" />');

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:

$(".item.group")
.filter(function() {
    return !$(this).prev().is(".group");
})

From each such element, create a jQuery object that includes the element and all following siblings with .group:

.map(function() {
    return $(this).nextUntil(":not(.group)").andSelf();
})

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

查看更多
做个烂人
3楼-- · 2020-06-17 02:09

Try wrapAll method instead:

$(".group").wrapAll("<div class='wrap' />");

DEMO: http://jsfiddle.net/LanMt/3/


For wrapping the separate groups of .group elements you can use the following:

$(".group").map(function() {
    if (!$(this).prev().hasClass("group")) {
        return $(this).nextUntil(":not(.group)").andSelf();
    }
}).wrap("<div class='wrap' />");

DEMO: http://jsfiddle.net/LanMt/5/

The code above was assembled with the help of @Jon's answer.

查看更多
Melony?
4楼-- · 2020-06-17 02:13

Use wrapAll instead of wrap.

$(".group").wrapAll('<div class="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 -

var group = [];
        $(".item").each(
          function(i, item) {            
            if ($(item).hasClass("group")) {
                group.push(item);
            }
            else {
                $(group).wrapAll('<div class="wrap" />');
                group = [];
            }
          }
        );
查看更多
登录 后发表回答