jQuery unwrap: The opposite of .wrap, pretty much

|

This very basic jQuery plugin does exactly what you’d think: the selected elements are unwrapped, removing their parent element, which effectively “promotes” them (and their siblings).

As of jQuery 1.4, this method is now included in jQuery core! See the patch as well as the official .unwrap documentation.

  • Release v0.2
  • Tested with jQuery 1.3.2 in Internet Explorer 6-8, Firefox 3, Safari 3-4, Chrome, Opera 9.
  • Download Source, Minified (0.3kb)
  • View Unit Tests
  • View Example

This plugin is so simple that it really needs no explanation, just look at the example to see it in action. I will, however, use this space to explain how I got to this point, in case you’re curious.

Too naive

Originally, my unwrap method was very simple, very elegant, and very broken:

$.fn.unwrap = function() {
  return this.each(function(){
    $(this).parent().replaceWith( this );
  });
};

If you do a web search for “jQuery unwrap plugin”, you’ll find a few snippets of code that look almost exactly like that. The code looks clean and makes sense, “for each element, replace its parent with itself,” which works great until you realize that you’ve been losing sibling elements here and there.

Oops?

So, what we really want is “for each element, replace its parent with itself AND its siblings.” Here’s the literal interpretation of that pseudocode:

$.fn.unwrap = function() {
  return this.each(function(){
    var that = $(this);
    that.parent().replaceWith( that.add( that.siblings ) );
  });
};

Ok, not so good. I guess “for each element, replace its parent with itself AND its siblings.” isn’t quite what we want, since the child nodes are now out of order, because of the way they were selected. And even if the child nodes were selected in the correct order, what if we try to unwrap multiple sibling elements together?

Unwrapping the first element would replace its parent with itself and its siblings, which is great.. but wait, now unwrapping the second sibling element would replace the NEW parent with itself and its siblings.. which is a total mess, and not at all what we want. We need to figure out a better way!

Eureka!

The issue here is the parents. Unwrap only ought to do the “replace parent with its children” part once per parent.. So, let’s try “for each unique element’s parent, replace that parent with its children.” Fortunately, this is very easy to do, and the resulting code is the core of my unwrap plugin:

$.fn.unwrap = function() {
  this.parent(':not(body)')
    .each(function(){
      $(this).replaceWith( this.childNodes );
    });

  return this;
};

Note: the ':not(body)' selector keeps the user from trying to unwrap children of the body tag, which would end up deleting the body tag and make a mess of things. I’m sure that you wouldn’t do this, of course, I’m providing it just in case someone else does (I actually initially had .parent(':not(html,head,body)').not(document) but it seemed like total overkill).

Thanks to nlogax for pointing out that .parent() returns a unique list of parent elements, which allowed me to remove a lot of unnecessary $.unique() code!

Post A Comment

  • Any of these HTML tags may be used for style: a, b, i, br, p, strong, em, pre, code.
  • Multi-line JavaScript code should be wrapped in <pre class="brush:js"></pre>
    (supported syntax highlighting brushes: js, css, php, plain, bash, ruby, html, xml)
  • Use &lt; instead of < and &gt; instead of > in the examples themselves.