166

I have some simple HTML which I need to strip simple formatting.

A nice house was found in <b>Toronto</b>.

I need to remove the bold, but leave the sentence intact.

How is this possible in jQuery?

Braiam
  • 1
  • 11
  • 47
  • 78
Ian Vink
  • 66,960
  • 104
  • 341
  • 555

7 Answers7

324
$('b').contents().unwrap();

This selects all <b> elements, then uses .contents() to target the text content of the <b>, then .unwrap() to remove its parent <b> element.


For the greatest performance, always go native:

var b = document.getElementsByTagName('b');

while(b.length) {
    var parent = b[ 0 ].parentNode;
    while( b[ 0 ].firstChild ) {
        parent.insertBefore(  b[ 0 ].firstChild, b[ 0 ] );
    }
     parent.removeChild( b[ 0 ] );
}

This will be much faster than any jQuery solution provided here.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • 2
    +1 I was sitting there writing out a near identical answer regarding `unwrap()` and couldn't remember how to get the text part, forgot about `.contents()` - excellent. – Orbling Nov 20 '10 at 13:38
  • 2
    While this works, it's also fairly slow compared to `.replacewith()` because of the extra DOM traversal...if it's a `` tag with only HTML it gets even faster. – Nick Craver Nov 20 '10 at 13:51
  • Also, if you want to just unwrap a specific span or font tag, you could do something like this: $(".className font").contents().unwrap(); – Steve Cochrane Apr 05 '18 at 18:04
  • if html is
    abc
    abc break line not hold? how I can hold it if use
    for break line? @user113716
    – Hoa.Tran Aug 31 '18 at 08:12
  • 1
    Thank you, this was very helpful. I added [`parent.normalize()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/normalize) after `parent.removeChild(...` to merge adjacent text nodes. This was helpful if you're continuously modifying the page. – Justin Harris Jan 23 '20 at 16:08
55

You can also use .replaceWith(), like this:

$("b").replaceWith(function() { return $(this).contents(); });

Or if you know it's just a string:

$("b").replaceWith(function() { return this.innerHTML; });

This can make a big difference if you're unwrapping a lot of elements since either approach above is significantly faster than the cost of .unwrap().

Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • Nick, I really like your answer as well. I chose the other because the amount of tags/text I'll be using this for in my iPad app (local data) is small so performance is not too important. Partick's answer is a little simpler for me to maintain. Splitting hairs on 2 great solutions, but I could only give one the check. – Ian Vink Nov 20 '10 at 14:07
  • 1
    @BahaiResearch.com - If performance isn't an issue then absolutely go simple...I'm just providing this for others finding it later who may be concerned about it :) – Nick Craver Nov 20 '10 at 14:09
  • I was trying to use this example with a textbox but it was killing me because the content inside were html tags that I wanted to be evaluated and not just displayed as text. I ended up using: $('textarea').replaceWith(function(){return $(this).val();}); – Will Sampson Sep 26 '12 at 17:24
14

The simplest way to remove inner html elements and return only text would the JQuery .text() function.

Example:

var text = $('<p>A nice house was found in <b>Toronto</b></p>');

alert( text.html() );
//Outputs A nice house was found in <b>Toronto</b>

alert( text.text() );
////Outputs A nice house was found in Toronto

jsFiddle Demo

WebChemist
  • 4,393
  • 6
  • 28
  • 37
9

Behold, for the simplest answer is mind blowing:

outerHTML is supported down to Internet Explorer 4 !

Here is to do it with javascript even without jQuery

element.outerHTML = element.innerHTML

with jQuery

var element = $('b')[0];
element.outerHTML = element.innerHTML;

or without jQuery

var element = document.querySelector('b');
element.outerHTML = element.innerHTML

If you want it as a function:

function unwrap(selector) {
    var nodelist = document.querySelectorAll(selector);
    Array.prototype.forEach.call(nodelist, function(item,i){
        item.outerHTML = item.innerHTML; // or item.innerText if you want to remove all inner html tags 
    })
}

unwrap('b')

This should work in all major browser including old IE.

If you get NoModificationAllowedError or DOMException, it means the element has no parent. Usually you get this when you're trying this answer by creating a new node from javascript console without putting it as other element's child. But don't worry and remember that any element in the document is at least has one parent (<html></html> element)


NOTE:

this rewrite the innerHTML, so if you have a variable referencing to the inner element, it will not point to the same element.

If you need to keep the reference of some of the inner elements in your coding, you can use the jQuery (top answer) which move the elements to the expected position without rewriting the element.

izzulmakin
  • 559
  • 1
  • 6
  • 17
  • Does not work in Safari: "NoModificationAllowedError: The object can not be modified." – Austin Feb 09 '21 at 17:22
  • I just tried on my safari and it works. You can try it on any element on this stackoverflow page with safari. However if you create an element and that element is not yet inside any other element, you will get NoModificationAllowedError when operating this. If you put that element inside another element, even when both element is not inside / not in DOM, this still works without error. – izzulmakin Feb 10 '21 at 09:47
  • `NoModificationAllowedError` occurs when you attempt to set outerHTML on an element like `document.documentElement`, where the parent of the element is the `Document` itself. `DOMException` occurs when the string is not valid HTML. See [the working draft recommendation](https://www.w3.org/TR/DOM-Parsing/#widl-Element-outerHTML). – Heretic Monkey Aug 30 '21 at 14:28
  • Won't this create new elements instead of keeping the old ones? For example, in case I'm holding reference of some elements in the content... – tsh Oct 07 '21 at 11:18
  • @izzulmakin Here is my fiddle: https://jsfiddle.net/ebfpqn40/ I had tested on Firefox 93 and it alerts `false`. – tsh Oct 12 '21 at 09:55
  • @tsh you're right. it did rewrite the innerHTML when outerHTML is set to a new value. i'll write a note about this – izzulmakin Oct 12 '21 at 14:01
5

How about this?

$("b").insertAdjacentHTML("afterend",$("b").innerHTML);
$("b").parentNode.removeChild($("b"));

The first line copies the HTML contents of the b tag to the location directly after the b tag, and then the second line removes the b tag from the DOM, leaving only its copied contents.

I normally wrap this into a function to make it easier to use:

function removeElementTags(element) {
   element.insertAdjacentHTML("afterend",element.innerHTML);
   element.parentNode.removeChild(element);
}

All of the code is actually pure Javascript, the only JQuery being used is that to select the element to target (the b tag in the first example). The function is just pure JS :D

Also look at:

Toastrackenigma
  • 7,604
  • 4
  • 45
  • 55
3
// For MSIE:
el.removeNode(false);

// Old js, w/o loops, using DocumentFragment:
function replaceWithContents (el) {
  if (el.parentElement) {
    if (el.childNodes.length) {
      var range = document.createRange();
      range.selectNodeContents(el);
      el.parentNode.replaceChild(range.extractContents(), el);
    } else {
      el.parentNode.removeChild(el);
    }
  }
}

// Modern es:
const replaceWithContents = (el) => {
  el.replaceWith(...el.childNodes);
};

// or just:
el.replaceWith(...el.childNodes);

// Today (2018) destructuring assignment works a little slower
// Modern es, using DocumentFragment.
// It may be faster than using ...rest
const replaceWithContents = (el) => {
  if (el.parentElement) {
    if (el.childNodes.length) {
      const range = document.createRange();
      range.selectNodeContents(el);
      el.replaceWith(range.extractContents());
    } else {
      el.remove();
    }
  }
};
redisko
  • 559
  • 5
  • 4
1

Another native solution (in coffee):

el = document.getElementsByTagName 'b'

docFrag = document.createDocumentFragment()
docFrag.appendChild el.firstChild while el.childNodes.length

el.parentNode.replaceChild docFrag, el

I don't know if it's faster than user113716's solution, but it might be easier to understand for some.

GijsjanB
  • 9,944
  • 4
  • 23
  • 27