Monday, July 23, 2012

jQuery unwrapInner()

Update: Well, I made a nice post about extending jQuery with an unwrapInner function, but I discovered that the same thing can be done by using jQuery's replaceWith() function...
Starting with this HTML:
<div class="test">
    <span class="red">
        red text
    </span>
    <div class="green">
        green text
    </div>
</div>
the span from around the red text can be removed by using the replaceWith() function as follows:
// unwrap red text
$('.test').find('.red')
    .replaceWith( $('.test .red').contents() );

So you can pretty much ignore the rest of this post =(

jQuery has a wrap and wrapAll which can be removed by using the unwrap function. But it only targets the parents of the selected element. What about wrapInner? It doesn't have a method to remove it. So I put together this small extension which does just that.

So say we start with this HTML:
<div class="test">
    <span>some text</span>
</div>
To remove that span from around "some text", we just call the unwrapInner code below
// unwrapInner function
jQuery.fn.extend({
    unwrapInner: function(selector) {
        return this.each(function() {
            var t = this,
                c = $(t).children(selector);
            if (c.length === 1) {
                c.contents().appendTo(t);
                c.remove();
            }
        });
    }
});
You can include a selector to target a specific element
// examples
$("div").unwrapInner(); // remove immediate child
$("div").unwrapInner("span"); // remove immediate child if it's a span
$("div").unwrapInner("span.inner"); // remove all immediate children of class '.inner'
Note that the one issue with this function is when there are multiple children to be unwrapped, it will add the unwrapped content to the end. For example, with this HTML:
<div class="test">
    <span class="red">
        red text
    </span>
    <div class="green">
        green text
    </div>
</div>
We want to unwrap span.red, so we use
$(".test").unwrapInner(".red");
but we get this result:
<div class="test">
    <div class="green">
        green text
    </div>
    red text
</div>
Check out this demo:

4 comments:

  1. How about increasing the .unwrap() such as .parents(). I want to know the easiest way to do it. Why JQuery is only equipped with .unwrap() and not .unwraps()? :\

    The hope:

    $('button').unwrap('.outer');

    <div class='outer'>
    <span>
    <button>Lalalaaa...</button>
    </span>
    </div>


    Demo: http://jsfiddle.net/tovic/MsEz7/2/

    ReplyDelete
  2. Hi Taufik! The demo you shared was using .next() and targeted the div.outer, so instead of finding the .inner, it just needed to target the button to unwrap: demo

    What you are asking above can be done using $(this).parent().unwrap(); Even if there are multiple span wrapped buttons - demo.

    ReplyDelete
    Replies
    1. I got it!

      jQuery.fn.extend({
      unwrapParent: function(selector) {
      return this.each(function() {
      if (selector) {
      $(this).closest(selector).contents().unwrap();
      } else {
      $(this).unwrap();
      }
      });
      }
      });


      $(this).unwrapParent('.the-outer');

      http://jsfiddle.net/tovic/MsEz7/7/

      Delete
  3. Apologies for adding this, but in the spirit of jQuery's 'do more, write less' you can also do the same as `replaceWith()` using the following:

    $('.red').contents().unwrap();

    Nicely written post though. One thing I've learned from reading it is that you can embed jsfiddles... I had no idea :)

    ReplyDelete