Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looking for a jQuery function similar to wrapAll that will only wrap consecutive elements

Tags:

jquery

wrapall

Is there a version of wrapAll that will only wrap consecutive elements? So this:

<p>foo</p>
<p>foo</p>
<h2>bar</h2>
<p>foo</p>

turns into this:

<div>
    <p>foo</p>
    <p>foo</p>
</div>
<h2>bar</h2>
<div>
    <p>foo</p>
</div>

When this is run?

$('p').wrapAll2('<div />')
like image 537
mattalxndr Avatar asked Nov 18 '11 06:11

mattalxndr


2 Answers

Here's one way to do it:

$(function(){
    var cWrap=$('<div />');
    $('p').each(function(){
        var o = $(this).next('p').length;
        $(this).replaceWith(cWrap).appendTo(cWrap);
        if (!o) cWrap=$('<div />');
    });
});

or as a fully fledged plugin:

(function($){
    $.fn.wrapAll2 = function(wrapper){
        if (!this.length) return this;
        var cWrap=$(wrapper),
            tag = this.get(0).nodeName.toLowerCase();
        return this.each(function(){
            var o = $(this).next(tag).length;
            $(this).replaceWith(cWrap).appendTo(cWrap);
            if (!o) cWrap = $(wrapper);
        });
    };
})(jQuery);
$(function(){
    $('p').wrapAll2('<div />');
});

*Note that it presumes you will call it on homogeneous collections (as least all the same node type)

EDIT

Experimentally, I've looked into jQuery's innards and found that it stores the selector used to create a collection! [at least on instantiation], so with that in mind I've created another version that at least works with the current version of jQuery, and accepts any selector!

(function($) {
    $.fn.wrapAll2 = function(wrapper) {
        if (!this.length) return this;
        var wrap = $(wrapper),
            sel = this.selector || this.get(0).nodeName.toLowerCase();
        return this.each(function() {
            var more = $(this).next(sel).length;
            console.log(this,more);
            $(this).replaceWith(wrap).appendTo(wrap);
            if (!more) wrap = $(wrapper);
        });
    }
})(jQuery);
$(function() {
    $('p, span').wrapAll2('<div />');
});

It does not work with things like this though: $('p').add('span').wrapAll2('<div />');

like image 140
Shad Avatar answered Oct 05 '22 06:10

Shad


I expounded on Shad's excellent answer and created some code that would take any selector, not just node types:

(function($){
    $.wrapAll2 = function(sel, wrapSel) {
        var wrap = $(wrapSel);
        $(sel).each(function() {
            var more = $(this).next(sel).length;
            $(this).replaceWith(wrap).appendTo(wrap);
            if (!more) wrap = $(wrapSel);
        });
    };
})(jQuery);
$(function() {
    $.wrapAll2('p', '<div/>');
});

Wish it could be passed a JQuery object instead of a selector string, but this works well enough for my purposes now.

like image 33
mattalxndr Avatar answered Oct 05 '22 08:10

mattalxndr