skip to content

jQuery Snippets: innerWrap

3 min read

jQuery provides a method for wrapping an element with one or more elements. This is a really nice method but sometimes you need to wrap the contents of an element. Now you can do so with one method, innerWrap. The innerWrap method acts almost identical to the wrap method. The only real difference is that it wraps the contents of the element instead of the actual element.

Lets say I have a definition list that I want to unobtrusively enhance for users that have JavaScript enabled. The definition list is simply a typical FAQ section. The definition title is the question and the definition is the answer. The mark-up looks like this.

<dl>
    <dt>How do I place an order by phone?</dt>
    <dd>You can place an order by phone by calling 1.800.000.0000.</dt>
    <dt>How do cancel my membership?</dt>
    <dd>You can cancel your membership by visiting this page or by calling 1.800.000.0000.</dt>
</dl>

Without JavaScript the list just displays as a normal definition list would. With JavaScript the definitions (the answers) would collapse and the contents of the definition title (the question) would be wrapped with an <a> tag. Then a click event could be bound to that <a> tag to show the definition for that question. The code without the innerWrap method might look something like this.

$(document).ready(function() {
    $('dt')
        .each(function() {
            this.innerHTML = '<a href="#">' + this.innerHTML + '</a>';
        })
        .find('a')
            .click(function() {
                $(this).next().show();
                return false;
            });
});

Wrapping the contents of the titles doesn’t take much since we can quickly loop through them with the each method. However, wouldn’t it be nice if we could get rid of that each and replace it with a much cleaner and more explicit method (not to mention replacing that innerHTML usage with some DOM methods)? Of course it would so here it is! :)

jQuery.fn.innerWrap = function() {
    var a, args = arguments;
    return this.each(function() {
        if (!a)
            a = jQuery.clean(args, this.ownerDocument);
        // Clone the structure that we're using to wrap
        var b = a[0].cloneNode(true),
            c = b;
        // Find the deepest point in the wrap structure
        while ( b.firstChild )
            b = b.firstChild;
        // append the child nodes to the wrapper
        jQuery.each(this.childNodes, function(i, node) {
            b.appendChild(node);
        });
        jQuery(this)
            // clear the element
            .empty()
            // add the new wrapper with the previous child nodes appeneded
            .append(c);
    });
};

The innerWrap method can take a String of HTML, an actual DOM Element or a jQuery object.

Now lets take another look at our code but this time using the innerWrap method.

$(document).ready(function() {
    $('dt')
        .innerWrap('<a href="#"></a>')
        .find('a')
            .click(function() {
                $(this).next().show();
                return false;
            });
});