1326

How would I go about removing all of the child elements of a DOM node in JavaScript?

Say I have the following (ugly) HTML:

<p id="foo">
    <span>hello</span>
    <div>world</div>
</p>

And I grab the node I want like so:

var myNode = document.getElementById("foo");

How could I remove the children of foo so that just <p id="foo"></p> is left?

Could I just do:

myNode.childNodes = new Array();

or should I be using some combination of removeElement?

I'd like the answer to be straight up DOM; though extra points if you also provide an answer in jQuery along with the DOM-only answer.

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
DigitalZebra
  • 39,494
  • 39
  • 114
  • 146

39 Answers39

2268

Option 1 A: Clearing innerHTML.

  • This approach is simple, but might not be suitable for high-performance applications because it invokes the browser's HTML parser (though browsers may optimize for the case where the value is an empty string).

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  myNode.innerHTML = '';
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello</span>
</div>
<button id='doFoo'>Remove via innerHTML</button>

Option 1 B: Clearing textContent

  • As above, but use .textContent. According to MDN this will be faster than innerHTML as browsers won't invoke their HTML parsers and will instead immediately replace all children of the element with a single #text node.

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  myNode.textContent = '';
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello</span>
</div>
<button id='doFoo'>Remove via textContent</button>

Option 2 A: Looping to remove every lastChild:

  • An earlier edit to this answer used firstChild, but this is updated to use lastChild as in computer-science, in general, it's significantly faster to remove the last element of a collection than it is to remove the first element (depending on how the collection is implemented).
  • The loop continues to check for firstChild just in case it's faster to check for firstChild than lastChild (e.g. if the element list is implemented as a directed linked-list by the UA).

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  while (myNode.firstChild) {
    myNode.removeChild(myNode.lastChild);
  }
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello</span>
</div>
<button id='doFoo'>Remove via lastChild-loop</button>

Option 2 B: Looping to remove every lastElementChild:

  • This approach preserves all non-Element (namely #text nodes and <!-- comments --> ) children of the parent (but not their descendants) - and this may be desirable in your application (e.g. some templating systems that use inline HTML comments to store template instructions).
  • This approach wasn't used until recent years as Internet Explorer only added support for lastElementChild in IE9.

doFoo.onclick = () => {
  const myNode = document.getElementById("foo");
  while (myNode.lastElementChild) {
    myNode.removeChild(myNode.lastElementChild);
  }
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <!-- This comment won't be removed -->
  <span>Hello <!-- This comment WILL be removed --></span>
  <!-- But this one won't. -->
</div>
<button id='doFoo'>Remove via lastElementChild-loop</button>

Bonus: Element.clearChildren monkey-patch:

  • We can add a new method-property to the Element prototype in JavaScript to simplify invoking it to just el.clearChildren() (where el is any HTML element object).
  • (Strictly speaking this is a monkey-patch, not a polyfill, as this is not a standard DOM feature or missing feature. Note that monkey-patching is rightfully discouraged in many situations.)

if( typeof Element.prototype.clearChildren === 'undefined' ) {
    Object.defineProperty(Element.prototype, 'clearChildren', {
      configurable: true,
      enumerable: false,
      value: function() {
        while(this.firstChild) this.removeChild(this.lastChild);
      }
    });
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
  <span>Hello <!-- This comment WILL be removed --></span>
</div>
<button onclick="this.previousElementSibling.clearChildren()">Remove via monkey-patch</button>
FZs
  • 16,581
  • 13
  • 41
  • 50
Gabriel McAdams
  • 56,921
  • 12
  • 61
  • 77
  • 6
    From jQuery these two issues might be considered: This method removes not only child (and other descendant) elements, but also any text within the set of matched elements. This is because, according to the DOM specification, any string of text within an element is considered a child node of that element. AND "To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves." – jottos Nov 21 '11 at 05:26
  • 3
    @micha I think your test is not right. I have written my one on jsfidle (http://jsfiddle.net/6K6mv/5/) and here is the result. WHILE: 14ms; INNERHTML: 7ms; TEXTCONTENT: 7ms – m93a Feb 02 '13 at 19:01
  • 3
    @m93a: running your fiddle multiple times gives me results all over the place between 6ms to 11ms. This is basically telling me nothing. The results are closer together than the variance in ms between each testrun for the same test. For the record: It's not MY script, its jsperf.com. Look at this for a better understanding of the test results: http://stackoverflow.com/a/5262617/859877 and http://stackoverflow.com/questions/4986245/how-does-jsperf-work/4996963#4996963 – micha Feb 02 '13 at 20:51
  • @micha Yes, I know what the test results mean, but it seems strange. It shows me thas innerHTML is 98% - 99% slower. And my tests show the opposite. But this is not a chat, I'm leaving discussion. – m93a Feb 04 '13 at 15:32
  • 33
    innerHTML only works if you are only dealing with HTML. If there is e.g. SVG inside only Element removal will work – stwissel Mar 27 '13 at 14:45
  • 71
    NEVER NEVER NEVER use innerHTML = ''. Don't. The problem is that the items look like they're removed but internally, nodes persist and slow things down. You have to remove all the individual nodes for a reason. Tested in both Google Chrome and IE. Please consider removing the innerHTML "solution" since it's wrong. – Kenji Sep 21 '13 at 15:40
  • 6
    @micha http://jsperf.com/innerhtml-vs-removechild/151 using .remove() is even faster. – Joeytje50 Feb 11 '14 at 12:33
  • 1
    @joeytje50 Nice one! Unfortunately it's DOM4 and not available everywhere. I'm sticking with `.removeChild()` for better compatibility. Besides, the gain of `.remove()` is only 2%. – micha Feb 12 '14 at 03:21
  • 1
    I put this in a prototype so I could use it more easily: Element.prototype.clear = function(){while (this.firstChild) {this.removeChild(this.firstChild);}} – Abacus Sep 17 '14 at 14:24
  • @Kenji - what if I do `document.documentElement.replaceChild(newBody, document.body)` ? would the "old" elements and their events persist in memory? (when replacing the whole body element) – vsync Sep 18 '14 at 13:18
  • 3
    @Andrey Lushnikov: Depends on which browser and which version. Actually firstChild ist best overall: http://jsperf.com/innerhtml-vs-removechild/250 – Stefan Steiger Jan 09 '15 at 09:10
  • 43
    @vsync I don't believe Kenji's comment is well-founded. Doing `innerHTML = ''` is slower, but I don't think it's "wrong". Any claims of memory leaks should be backed up with evidence. – Chris Middleton May 13 '15 at 00:17
  • @Andrey Lushnikov firstChild and lastChild are pretty equivalent on my Chrome 43.0.2357.52 (first run removeChildFirst was a little faster, second run removeChildLast won), but both are winners over innerHTML. – thdoan May 13 '15 at 07:25
  • 3
    @Kenji, Surely the browser is smart enough to prevent a memory leak. Have you managed to cause a memory leak in the browser? – Pacerier Jun 03 '15 at 04:45
  • 4
    Your examples aren't wrong, but I think there's a pretty good case in [this answer](http://stackoverflow.com/a/22966637/1802924) that the jsperf test was flawed, and didn't measure what it's supposed to. (The claim in the linked answer is that they were only removing empty divs, as they erroneously assumed that the DOM would be repopulated between each test.) In other words, innerHTML is faster, and there's an even faster method (replacing nodes with empty ones). Since your answer is so highly upvoted, it would be nice if you'd revert that part. – trlkly Feb 13 '16 at 05:33
  • 2
    @Abacus Proceed with care and make sure your target audience's browsers work with it. http://perfectionkills.com/whats-wrong-with-extending-the-dom/ – gcampbell Oct 01 '16 at 10:24
  • function emptyElement(element) { var myNode = element; while (myNode.firstChild) { myNode.removeChild(myNode.firstChild); } } emptyElement(document.body) – Marian07 Oct 19 '16 at 17:41
  • (assuming its true) it would be nice to have the reason in the answer – Old Badman Grey Jan 27 '17 at 19:18
  • I tried `myNode.childNodes.forEach(function (c) { c.remove() })`, but this doesn't remove *all* the children. I realize some browsers don't support the method, but for the browsers that do support it, it doesn't remove all the nodes. Why does the `while` loop work better than [`NodeList#forEach()`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach)? – chharvey Nov 27 '17 at 04:50
  • 5
    @chharvey You are modifying a collection that you are actively iterating over when you use forEach in that manner. – Jim Pedid Mar 03 '18 at 15:38
  • 2
    This is ridiculous. Why performance matters here? Is it 120 fps or one-off operation? – Mars Robertson Dec 23 '18 at 19:42
  • I agree with @MichalStefanow. In this case, simplicity of code should precede the minimal performance gain, unless you are working on a specific scenario that requires this level of meticulous optimization. It will not make a difference when removing 5-15 nodes. – Felipe Jan 11 '19 at 20:39
  • As a new web developer, just wanna say u sir are a god. Thanks for both the methods. I have been playing around with both! – Gutsygibbon Feb 03 '20 at 02:46
  • `remove` gives an infinite loop for me on Brave 80.1.5.123. `removeChild` works fine – OpenSauce May 07 '20 at 09:27
  • 2
    If I have 10 children elements I want to remove and each of them has an event attached to it, should I remove the event first then element itself or I don't need to remove the event, I can just remove the element itself??(vanilla javascript) – JustANewCoder Jan 13 '21 at 16:29
  • Concerning @stwissel’s remark about innerHTML not working on SVG elements: that was fixed across all browsers by the start of 2015, except of course for IE that was no longer being developed by then (only maintained). So long as you’re not supporting IE11 (which you actively shouldn’t, let it die!), you can happily use innerHTML on SVG elements. – Chris Morgan Sep 22 '21 at 14:52
  • I feel like this answer should be edited to list `textContent` first, since that's the faster and safer option – 12Me21 Jun 18 '22 at 17:03
427

In 2022+, use the replaceChildren() API!

Replacing all children can now be done with the (cross-browser supported) replaceChildren API:

container.replaceChildren(...arrayOfNewChildren);

This will do both:

  • remove all existing children, and
  • append all of the given new children, in one operation.

You can also use this same API to just remove existing children, without replacing them:

container.replaceChildren();

This is fully supported in Chrome/Edge 86+, Firefox 78+, and Safari 14+. It is fully specified behavior. This is likely to be faster than any other proposed method here, since the removal of old children and addition of new children is done without requiring innerHTML, and in one step instead of multiple.

Mason Freed
  • 5,493
  • 1
  • 19
  • 13
  • 3
    The MDN data has now been corrected, and shows full cross-browser support: https://caniuse.com/?search=replacechildren – Mason Freed Apr 29 '21 at 16:57
  • 7
    It's a pity @Polaris878 hasn't been around since 2018. This should be the new accepted answer. – spkrtn May 15 '21 at 15:12
  • 1
    This is significantly slower than the other methods mentioned here, at least for me. –  Jul 04 '21 at 14:03
  • 1
    Would you mind linking to your test, and the details like which browser/platform you used? That would surprise me greatly, at least on Chrome. – Mason Freed Jul 16 '21 at 02:05
  • **Don't do this.** It may seem nice but will trigger a separate MutationRecord for each removed child (when using a MutationObserver on the element). Setting `textContent` is much faster for hundreds of children. (I mean you can do this, but always set `textContent` to remove many existing children first.) – ygoe Jun 25 '22 at 10:52
  • 2
    @ygoe, that should not be the case. Can you show an example? Here is my example code: https://jsbin.com/xopapef, which uses a target element containing 10,000 children. At least in Chrome, it shows exactly one mutation observer call in both cases. Do you see something different? If so, please clarify what browser and platform you're using. (My demo, at least for me, also shows that replaceChildren() is at least as fast, and is typically faster, than textContent.) – Mason Freed Jun 27 '22 at 16:40
  • 2
    I ran the site I linked above on a Mac, on Chrome, Firefox, and Safari, and all three agree: only one mutation observer call. And replaceChildren() is very slightly faster on all three. Let me know if you see something different. – Mason Freed Jun 27 '22 at 17:04
  • Well, in a simple demo case, it looks good indeed. But in my application it results in that behaviour. Setting `textContent = ""` before calling `replaceChildren(...newChildren)` resolves the issue completely. Since mutation observer events are asynchronous, I cannot determine what the causing code is. But I'm happy with my working code. Others might need to inspect their own situation close enough to decide themselves. – ygoe Jun 29 '22 at 12:41
  • Ok. If you can post a minimized version of your problem code as a bug (crbug.com/new), I could take a closer look. – Mason Freed Jun 30 '22 at 13:42
  • 6
    Changing this to the accepted answer, since it's now the suggested modern approach :) – DigitalZebra Oct 15 '22 at 23:55
  • It is slowest variant even tested. At least what I got in Firefox. – Maxim Mar 31 '23 at 15:21
  • Interesting! On the test site I linked above? Or something else? – Mason Freed Apr 01 '23 at 16:58
  • This stopped working for some reason. – Vector3 Jun 25 '23 at 21:40
162

Use modern Javascript, with remove!

const parent = document.getElementById("foo")
while (parent.firstChild) {
    parent.firstChild.remove()
}

This is a newer way to write node removal in ES5. It is vanilla JS and reads much nicer than relying on parent.

All modern browsers are supported.

Browser Support - 97% Jun '21

Gibolt
  • 42,564
  • 15
  • 187
  • 127
  • 22
    The for loop will not work since parent.children / parent.childNodes are live lists; calling remove modifies the list and your iterator therefore isn't guaranteed to iterate over everything. In chrome, e.g., you end up skipping every other node. Better to use a `while (parent.firstChild) { parent.removeChild(parent.firstChild); }` loop. https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/children https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes – qix Nov 16 '16 at 10:19
  • 4
    Cool shorthand, although less readable: `while (parent.firstChild && !parent.firstChild.remove());` – Gibolt May 08 '17 at 00:36
  • 3
    A one-liner for when performance isn't a huge deal: `[...parent.childNodes].forEach(el => el.remove());` –  Aug 13 '18 at 12:10
  • 3
    This does not work in Typescript... instead use `while (selectElem.firstElementChild) { selectElem.firstElementChild.remove(); }` – John Henckel Aug 20 '18 at 20:56
139

The currently accepted answer is wrong about innerHTML being slower (at least in IE and Chrome), as m93a correctly mentioned.

Chrome and FF are dramatically faster using this method (which will destroy attached jquery data):

var cNode = node.cloneNode(false);
node.parentNode.replaceChild(cNode, node);

in a distant second for FF and Chrome, and fastest in IE:

node.innerHTML = '';

InnerHTML won't destroy your event handlers or break jquery references, it's also recommended as a solution here: https://developer.mozilla.org/en-US/docs/Web/API/Element.innerHTML.

The fastest DOM manipulation method (still slower than the previous two) is the Range removal, but ranges aren't supported until IE9.

var range = document.createRange();
range.selectNodeContents(node);
range.deleteContents();

The other methods mentioned seem to be comparable, but a lot slower than innerHTML, except for the outlier, jquery (1.1.1 and 3.1.1), which is considerably slower than anything else:

$(node).empty();

Evidence here:

http://jsperf.com/innerhtml-vs-removechild/167 http://jsperf.com/innerhtml-vs-removechild/300 https://jsperf.com/remove-all-child-elements-of-a-dom-node-in-javascript (New url for jsperf reboot because editing the old url isn't working)

Jsperf's "per-test-loop" often gets understood as "per-iteration", and only the first iteration has nodes to remove so the results are meaningless, at time of posting there were tests in this thread set up incorrectly.

Jakub Kukul
  • 12,032
  • 3
  • 54
  • 53
npjohns
  • 2,218
  • 1
  • 17
  • 16
  • 4
    Your jsperf is completely broken. That's not very good evidence. – bryc Mar 15 '15 at 06:35
  • Thanks for noting that, fixed it, insertBefore spec changed and no longer takes undefined for last param. – npjohns Apr 28 '15 at 04:29
  • 4
    This is currently the best answer. Everything else in this thread is disinformation (!) Thanks for cleaning up all those bad old tests. – Ben May 15 '15 at 00:05
  • 6
    *"InnerHTML won't destroy your event handlers or mess up any jquery references"* It will orphan data held in `jQuery.cache`, causing a memory leak because the only reference available to manage that data was on the elements you just destroyed. –  Jun 04 '16 at 02:29
  • 1
    "InnerHTML won't destroy your event handlers or mess up any jquery references" refers to the container, not the children. `cloneNode` switches out the container, which means references to the container are not maintained (jquery or otherwise). Jquery's cache may make it worth using jquery to remove elements if you have jquery data attached to them. Hopefully they switch to [WeakMaps](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) at some point. Cleaning (or even the existence of) the $.cache doesn't appear to be officially documented. – npjohns Jun 04 '16 at 03:30
  • jsperf offline, would you mind uploading you test-code here, if you have it? I'd appriciate that! – radl Sep 04 '16 at 15:04
  • I don't have code, but jsperf is in the waybackmachine https://web.archive.org/web/20151008164028/http://jsperf.com/innerhtml-vs-removechild/300 – npjohns Sep 07 '16 at 06:26
  • Just a random query: why does your benchmark include code for setting up the data that is going to be removed inside the benchmarked section? Surely the setup should be done *outside* of the benchmark, but all of your benchmarks call `refreshRange()` (which clones a set of test data into a new node) from within the test. How can you be sure that this isn't skewing the results? – Periata Breatta Oct 22 '16 at 20:49
  • Also, why are you using `contents().remove()` in your jquery tests (which builds an internal list of all of the child nodes and then removes them) rather than `empty()` (which just removes them)? – Periata Breatta Oct 22 '16 at 20:54
  • Still not a proper benchmark imo. nodes should be generated with attached events, attributes, functions, etc... for a more realistic benchmark. It kind of make sense that removeChild will remove attached resources while innerHTML might not, thus being slower, but if you benchmark without any attached resources it does not make sense. – Gregoire D. Jan 12 '17 at 10:50
  • @PeriataBreatta The jsperf comments are lost, but what I was referring to about broken-ness was only performing removal once per test. Jsperf's "per-test-loop" often gets understood as "per-iteration", and then only the first iteration does any work and the results are meaningless. It would be ideal to set up outside of iteration, but as long as the setup is the same and minimized, the relative results are meaningful. I didn't know about empty(), thanks for the heads up, I'll fix it. – npjohns Jan 24 '17 at 02:29
  • @GregoireD. That info isn't really requested in the question, and I think the bigger concern for innerHTML usage in removing nodes is probably memory leaks more than performance, and throwing a leaky test premise in jsperf which has no ability to clean per-iteration would probably end up skewing the results – npjohns Jan 24 '17 at 02:41
  • 10
    The jsperf tests are broken here. The test engine reports the following: "Error: Dom nodes not recreated during iterations, test results will be meaningless." – senderle Mar 28 '17 at 17:47
  • 1
    `cloneNode(false)` is indeed superfast, but the problem is since it's destructive, you'll have to re-reference your variables, which is a big penalty, e.g.: https://jsperf.com/innerhtml-vs-removechild/467 – thdoan Feb 14 '19 at 08:01
  • 3
    I'm working on a DOM course and since it looks like jsperf.com is mia and might be for a while, I created a page on [jsben.ch](https://jsben.ch/DwfAd). As of today, `innerHTML` is still the winner. I've spent too long on this rabbit hole already, but in the year 2020 I have to wonder why we don't have a `.clear()` or `.empty()` DOM method yet... – Scott Means Aug 17 '20 at 09:14
  • just set .textContent to "". according to the spec, this is treated as a special case: https://dom.spec.whatwg.org/#string-replace-all – 12Me21 Jun 18 '22 at 17:00
48

If you use jQuery:

$('#foo').empty();

If you don't:

var foo = document.getElementById('foo');
while (foo.firstChild) foo.removeChild(foo.firstChild);
PleaseStand
  • 31,641
  • 6
  • 68
  • 95
47
var myNode = document.getElementById("foo");
var fc = myNode.firstChild;

while( fc ) {
    myNode.removeChild( fc );
    fc = myNode.firstChild;
}

If there's any chance that you have jQuery affected descendants, then you must use some method that will clean up jQuery data.

$('#foo').empty();

The jQuery .empty() method will ensure that any data that jQuery associated with elements being removed will be cleaned up.

If you simply use DOM methods of removing the children, that data will remain.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • The innerHTML method is by far the most performant. That said, the data being cleaned up by jquery's .empty(): Eliminates the data() inside jquery associated with the child tags using a recursive loop (slow, but may reduce memory usage significantly), Prevents memory leaks IF you attached events without jquery, like using .onclick=... . If you have no such events in the children to be removed, then setting innerHTML won't leak. So the best answer is - it depends. – Chris Moschini Nov 16 '11 at 15:10
  • 2
    Why not just put the firstChild expression directly in the condition of the while loop? `while ( myNode.firstChild ) {` – Victor Zamanian May 24 '16 at 09:07
  • `while(fc = myNode.firstChild){ myNode.removeChild(fc); }` – Valen Dec 25 '18 at 23:43
25

The fastest...

var removeChilds = function (node) {
    var last;
    while (last = node.lastChild) node.removeChild(last);
};

Thanks to Andrey Lushnikov for his link to jsperf.com (cool site!).

EDIT: to be clear, there is no performance difference in Chrome between firstChild and lastChild. The top answer shows a good solution for performance.

Gabe Halsmer
  • 808
  • 1
  • 9
  • 18
  • Same thing without LINT warning: clear: function ( container ) { for ( ; ; ) { var child = container.lastChild; if ( !child ) break; container.removeChild( child ); } } – cskwg Jan 03 '19 at 10:51
25

Use elm.replaceChildren().

It’s experimental without wide support, but when executed with no params will do what you’re asking for, and it’s more efficient than looping through each child and removing it. As mentioned already, replacing innerHTML with an empty string will require HTML parsing on the browser’s part.

MDN Documentation

Update It's widely supported now

Marc M.
  • 3,631
  • 4
  • 32
  • 53
  • https://caniuse.com/?search=replaceChildren Latest version of Chrome and Safari just got it. Give it a couple months. God I love auto-updates. – Marc M. Oct 20 '20 at 11:57
  • How much HTML parsing happens on an empty string? – NateS Oct 15 '21 at 04:21
10

If you only want to have the node without its children you could also make a copy of it like this:

var dupNode = document.getElementById("foo").cloneNode(false);

Depends on what you're trying to achieve.

DanMan
  • 11,323
  • 4
  • 40
  • 61
  • 3
    Maybe you could even follow this with `parent.replaceChild(cloned, original)`? That might be faster than removing children one by one and should work on everything that supports the DOM (so every type of XML document, including SVG). Setting innerHTML might also be faster than removing children one by one, but that doesn't work on anything but HTML documents. Should test that some time. – Maarten Jun 18 '12 at 21:56
  • 14
    That won't copy event listeners added using `addEventListener`. – Matthew Sep 08 '12 at 17:31
  • If you can live with that limitation, it's certainly quick: http://jsperf.com/innerhtml-vs-removechild/137 – DanMan Feb 01 '14 at 14:21
9

Ecma6 makes it easy and compact

myNode.querySelectorAll('*').forEach( n => n.remove() );

This answers the question, and removes “all child nodes”.

If there are text nodes belonging to myNode they can’t be selected with CSS selectors, in this case we’ve to apply (also):

myNode.textContent = '';

Actually the last one could be the fastest and more effective/efficient solution.

.textContent is more efficient than .innerText and .innerHTML, see: MDN

Marco Balestra
  • 167
  • 2
  • 6
8

Here's another approach:

function removeAllChildren(theParent){

    // Create the Range object
    var rangeObj = new Range();

    // Select all of theParent's children
    rangeObj.selectNodeContents(theParent);

    // Delete everything that is selected
    rangeObj.deleteContents();
}
Nathan K
  • 316
  • 3
  • 6
  • 1
    This is interesting but it seems fairly slow compared to other methods: http://jsperf.com/innerhtml-vs-removechild/256 – DigitalZebra Jan 05 '15 at 23:05
8
element.textContent = '';

It's like innerText, except standard. It's a bit slower than removeChild(), but it's easier to use and won't make much of a performance difference if you don't have too much stuff to delete.

bjb568
  • 11,089
  • 11
  • 50
  • 71
6

Here is what I usually do :

HTMLElement.prototype.empty = function() {
    while (this.firstChild) {
        this.removeChild(this.firstChild);
    }
}

And voila, later on you can empty any dom element with :

anyDom.empty()
Ado Ren
  • 3,511
  • 4
  • 21
  • 36
5

In response to DanMan, Maarten and Matt. Cloning a node, to set the text is indeed a viable way in my results.

// @param {node} node
// @return {node} empty node
function removeAllChildrenFromNode (node) {
  var shell;
  // do not copy the contents
  shell = node.cloneNode(false);

  if (node.parentNode) {
    node.parentNode.replaceChild(shell, node);
  }

  return shell;
}

// use as such
var myNode = document.getElementById('foo');
myNode = removeAllChildrenFromNode( myNode );

Also this works for nodes not in the dom which return null when trying to access the parentNode. In addition, if you need to be safe a node is empty before adding content this is really helpful. Consider the use case underneath.

// @param {node} node
// @param {string|html} content
// @return {node} node with content only
function refreshContent (node, content) {
  var shell;
  // do not copy the contents
  shell = node.cloneNode(false);

  // use innerHTML or you preffered method
  // depending on what you need
  shell.innerHTML( content );

  if (node.parentNode) {
    node.parentNode.replaceChild(shell, node);
  }

  return shell;
}

// use as such
var myNode = document.getElementById('foo');
myNode = refreshContent( myNode );

I find this method very useful when replacing a string inside an element, if you are not sure what the node will contain, instead of worrying how to clean up the mess, start out fresh.

jeroen
  • 502
  • 8
  • 17
  • 3
    the worst thing ever. if you are replacing the original node with its clone, you are losing the reference to the original node. none of the event handlers and other bindings won't work. – Arun Aravind Dec 07 '13 at 17:20
5

Using a range loop feels the most natural to me:

for (var child of node.childNodes) {
    child.remove();
}

According to my measurements in Chrome and Firefox, it is about 1.3x slower. In normal circumstances, this will perhaps not matter.

emu
  • 1,597
  • 16
  • 20
5

A one-liner to iteratively remove all the children of a node from the DOM

Array.from(node.children).forEach(c => c.remove())

Or

[...node.children].forEach(c => c.remove())
ShivCK
  • 557
  • 10
  • 16
4
var empty_element = function (element) {

    var node = element;

    while (element.hasChildNodes()) {              // selected elem has children

        if (node.hasChildNodes()) {                // current node has children
            node = node.lastChild;                 // set current node to child
        }
        else {                                     // last child found
            console.log(node.nodeName);
            node = node.parentNode;                // set node to parent
            node.removeChild(node.lastChild);      // remove last node
        }
    }
}

This will remove all nodes within the element.

YakovL
  • 7,557
  • 12
  • 62
  • 102
Henrik
  • 41
  • 4
  • ...because it's unnecessary complex, and no explanation is given as to how it works (which is actually non-obvious), I suspect. – Periata Breatta Oct 22 '16 at 21:06
4

There are couple of options to achieve that:

The fastest ():

while (node.lastChild) {
  node.removeChild(node.lastChild);
}

Alternatives (slower):

while (node.firstChild) {
  node.removeChild(node.firstChild);
}

while (node.hasChildNodes()) {
  node.removeChild(node.lastChild);
}

Benchmark with the suggested options

magiccrafter
  • 5,175
  • 1
  • 56
  • 50
4

element.innerHTML = "" (or .textContent) is by far the fastest solution

Most of the answers here are based on flawed tests

For example: https://jsperf.com/innerhtml-vs-removechild/15
This test does not add new children to the element between each iteration. The first iteration will remove the element's contents, and every other iteration will then do nothing. In this case, while (box.lastChild) box.removeChild(box.lastChild) was faster because box.lastChild was null 99% of the time

Here is a proper test: https://jsperf.com/innerhtml-conspiracy

Finally, do not use node.parentNode.replaceChild(node.cloneNode(false), node). This will replace the node with a copy of itself without its children. However, this does not preserve event listeners and breaks any other references to the node.

12Me21
  • 992
  • 10
  • 22
4

I reviewed all questions and decided to test performance for most crucial test case:

  • we need to clear content
  • we need to keep original element
  • we need to delete a lot of child elements

So my HTML:

<div id="target">
  <div><p>Title</p></div>
  <!-- 1000 at all -->
  <div><p>Title</p></div>
</div>
// innerHTML
target.innerHTML = "";
// lastChild
while (target.hasChildNodes()) {
    target.removeChild(target.lastChild);
}
// firstChild
while (target.hasChildNodes()) {
    target.removeChild(target.firstChild);
}
// replaceChildren
target.replaceChildren();
Results:
Checked test: innerHTML x 1,222,273 ops/sec ±0.47% (63 runs sampled)
Checked test: lastChild x 1,336,734 ops/sec ±0.87% (65 runs sampled)
Checked test: firstChild x 1,313,521 ops/sec ±0.74% (64 runs sampled)
Checked test: replaceChildren x 743,076 ops/sec ±1.08% (53 runs sampled)

Checked test: innerHTML x 1,202,727 ops/sec ±0.83% (63 runs sampled)
Checked test: lastChild x 1,360,350 ops/sec ±0.72% (65 runs sampled)
Checked test: firstChild x 1,348,498 ops/sec ±0.78% (63 runs sampled)
Checked test: replaceChildren x 743,076 ops/sec ±0.86% (53 runs sampled)

Checked test: innerHTML x 1,191,838 ops/sec ±0.73% (62 runs sampled)
Checked test: lastChild x 1,352,657 ops/sec ±1.42% (63 runs sampled)
Checked test: firstChild x 1,327,664 ops/sec ±1.27% (65 runs sampled)
Checked test: replaceChildren x 754,166 ops/sec ±1.88% (61 runs sampled)
Conclusions

Modern API is most slow. First child and last child ways are equal, some side effect can be present if comparison happens with multiple cases. But side by side you can see here:

Checked test: firstChild x 1,423,497 ops/sec ±0.53% (63 runs sampled)
Checked test: lastChild x 1,422,560 ops/sec ±0.36% (66 runs sampled)

Checked test: firstChild x 1,368,175 ops/sec ±0.57% (65 runs sampled)
Checked test: lastChild x 1,381,759 ops/sec ±0.39% (66 runs sampled)

Checked test: firstChild x 1,372,109 ops/sec ±0.37% (66 runs sampled)
Checked test: lastChild x 1,355,811 ops/sec ±0.35% (65 runs sampled)
Checked test: lastChild x 1,364,830 ops/sec ±0.65% (64 runs sampled)
Checked test: firstChild x 1,365,617 ops/sec ±0.41% (65 runs sampled)

Checked test: lastChild x 1,389,458 ops/sec ±0.50% (63 runs sampled)
Checked test: firstChild x 1,387,861 ops/sec ±0.40% (64 runs sampled)

Checked test: lastChild x 1,388,208 ops/sec ±0.43% (65 runs sampled)
Checked test: firstChild x 1,413,741 ops/sec ±0.47% (65 runs sampled)

P.S. Browser: Firefox 111.0.1

Maxim
  • 13,029
  • 6
  • 30
  • 45
3

innerText is the winner! http://jsperf.com/innerhtml-vs-removechild/133. At all previous tests inner dom of parent node were deleted at first iteration and then innerHTML or removeChild where applied to empty div.

Alexey
  • 65
  • 2
  • 5
    `innerText` is a proprietary MS thing though. Just saying. – DanMan Feb 01 '14 at 14:23
  • 1
    Pretty sure this is a flawed test - it relies on `box` being available not as a var but through DOM lookup based on id, and since the `while` loop makes this slow call many times it's penalized. – nrabinowitz Dec 10 '15 at 01:05
3

Simplest way of removing the child nodes of a node via Javascript

var myNode = document.getElementById("foo");
while(myNode.hasChildNodes())
{
   myNode.removeChild(myNode.lastChild);
}
Chaitanya Bapat
  • 3,381
  • 6
  • 34
  • 59
3
 let el = document.querySelector('#el');
 if (el.hasChildNodes()) {
      el.childNodes.forEach(child => el.removeChild(child));
 }
Thomas
  • 119
  • 1
  • 4
2

i saw people doing:

while (el.firstNode) {
    el.removeChild(el.firstNode);
}

then someone said using el.lastNode is faster

however I would think that this is the fastest:

var children = el.childNodes;
for (var i=children.length - 1; i>-1; i--) {
    el.removeNode(children[i]);
}

what do you think?

ps: this topic was a life saver for me. my firefox addon got rejected cuz i used innerHTML. Its been a habit for a long time. then i foudn this. and i actually noticed a speed difference. on load the innerhtml took awhile to update, however going by addElement its instant!

Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • 1
    well i think whether is fastest or not, some [jsperf tests](http://jsperf.com/innerhtml-vs-removechild/15) can prove either case – Nikos M. Oct 05 '14 at 21:11
  • super work thanks for those tests. they dont include the `for (var i=0...` one though. But here is what i got when i ran those tests: http://i.imgur.com/Mn61AmI.png so in those three tests firstNode was faster than lastNode and innerHTML which is real cool – Noitidart Oct 05 '14 at 23:15
  • i added the tests: http://jsperf.com/innerhtml-vs-removechild/220 interesting stuff doing el.firstNode method is the fastest way it seems – Noitidart Oct 05 '14 at 23:29
2

Why aren't we following the simplest method here "remove" looped inside while.

const foo = document.querySelector(".foo");
while (foo.firstChild) {
  foo.firstChild.remove();     
}
  • Selecting the parent div
  • Using "remove" Method inside a While loop for eliminating First child element , until there is none left.
Neelansh Verma
  • 134
  • 1
  • 6
2

Best Removal Method for ES6+ Browser (major browsers released after year 2016):

Perhaps there are lots of way to do it, such as Element.replaceChildren(). I would like to show you an effective solution with only one redraw & reflow supporting all ES6+ browsers.

function removeChildren(cssSelector, parentNode){
    var elements = parentNode.querySelectorAll(cssSelector);
    let fragment = document.createDocumentFragment(); 
    fragment.textContent=' ';
    fragment.firstChild.replaceWith(...elements);
}

Usage: removeChildren('.foo',document.body);: remove all elements with className foo in <body>

Chester Fung
  • 182
  • 1
  • 10
  • 1
    No need for the ``textContent`` just do ``fragment.replaceChildren(...elements)`` instead of ``fragment.firstChild.replaceWith(...elements)`` – Danny '365CSI' Engelman Oct 30 '21 at 10:25
  • 1
    @Danny'365CSI'Engelman `replaceChildren` is a relatively new DOM method. (e.g. FireFox >=78) while `replaceWith` can run with Firefox>=49. If your target browsers are the latest versions without considering Waterfox Classic, `replaceChildren` could be better. – Chester Fung May 08 '22 at 08:13
1

Generally, JavaScript uses arrays to reference lists of DOM nodes. So, this will work nicely if you have an interest in doing it through the HTMLElements array. Also, worth noting, because I am using an array reference instead of JavaScript proto's this should work in any browser, including IE.

while(nodeArray.length !== 0) {
  nodeArray[0].parentNode.removeChild(nodeArray[0]);
}
r00t hkr
  • 109
  • 1
  • 2
  • 5
1

Just saw someone mention this question in another and thought I would add a method I didn't see yet:

function clear(el) {
    el.parentNode.replaceChild(el.cloneNode(false), el);
}

var myNode = document.getElementById("foo");
clear(myNode);

The clear function is taking the element and using the parent node to replace itself with a copy without it's children. Not much performance gain if the element is sparse but when the element has a bunch of nodes the performance gains are realized.

1

Functional only approach:

const domChildren = (el) => Array.from(el.childNodes)
const domRemove = (el) => el.parentNode.removeChild(el)
const domEmpty = (el) => domChildren(el).map(domRemove)

"childNodes" in domChildren will give a nodeList of the immediate children elements, which is empty when none are found. In order to map over the nodeList, domChildren converts it to array. domEmpty maps a function domRemove over all elements which removes it.

Example usage:

domEmpty(document.body)

removes all children from the body element.

1

You can remove all child elements from a container like below:

function emptyDom(selector){
 const elem = document.querySelector(selector);
 if(elem) elem.innerHTML = "";
}

Now you can call the function and pass the selector like below:

If element has id = foo

emptyDom('#foo');

If element has class = foo

emptyDom('.foo');

if element has tag = <div>

emptyDom('div')
harish kumar
  • 1,732
  • 1
  • 10
  • 21
1

If you want to empty entire parent DOM then it's very simple...

Just use .empty()

function removeAll() {
  $('#parent').empty();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button onclick="removeAll()">Remove all the element of parent</button>
<div id="parent">
  <h3>Title</h3>
  <p>Child 1</p>
  <p>Child 2</p>
  <p>Child 3</p>
</div>
Rohit Tagadiya
  • 3,373
  • 1
  • 25
  • 25
0

I did it like this

data = {
    "messages": [
        [0, "userName", "message test"],
        [1, "userName1", "message test1"]
    ]
}
for (let i = 0; i < data.messages.length; i++) {
    var messageData = document.createElement('span')
    messageData.className = 'messageClass'
    messageData.innerHTML = `${data.messages[i][2]} ${'<br />'}`
    $(".messages").append(messageData)
}

$('#removeMessages').on('click', () => {
    node = $('.messages').get(0)
    while (node.firstChild) {
        node.firstChild.remove()
    }
    $('#removeMessages').toggle(500)
    $('#addMessages').toggle(500)
})

$('#addMessages').on('click', () => {
    for (let i = 0; i < data.messages.length; i++) {
        var messageData = document.createElement('span')
        messageData.className = 'messageClass'
        messageData.innerHTML = `${data.messages[i][2]} ${'<br />'}`
        $(".messages").append(messageData)
    }
    $('#addMessages').toggle(500)
    $('#removeMessages').toggle(500)
})
#addMessages{
  display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="messages"></div>
<button type='button' id="removeMessages">Remove</button>
<button type='button' id="addMessages">Add MEssages</button>
AllanRibas
  • 678
  • 5
  • 14
-1

simply only IE:

parentElement.removeNode(true);

true - means to do deep removal - which means that all child are also removed

Maciej Gurban
  • 5,615
  • 4
  • 40
  • 55
-1

An asynchronous, recursive approach, if need be:

document.iterCount=0;
async function removeChildren(cssSelector){
    var parent=document.querySelector(cssSelector);
    if (parent && parent.childElementCount){
        for (var child of parent.children){
            child.remove();
        };
        if (parent.childElementCount > 0){
                document.iterCount++;
                setTimeout(removeChildren(cssSelector), 0.1*1000);
        }
        else {
            console.log(`returned in ${document.iterCount} tries`);
        };

    } else {
        console.info(`there are no children for the element whose selector is '${cssSelector}'`)
        return 0;
    }
}

get rid of document.iterCount when implementing it, it's just there to show that sometimes a single pass at clearing the child nodes will not suffice.

endrias
  • 813
  • 8
  • 12
-2

simple and fast using for loop!!

var myNode = document.getElementById("foo");

    for(var i = myNode.childNodes.length - 1; i >= 0; --i) {
      myNode.removeChild(myNode.childNodes[i]);
    }

this will not work in <span> tag!

Mohit Karkar
  • 71
  • 1
  • 9
-2

Other ways in jQuery

var foo = $("#foo");
foo.children().remove();
//or
$("*", foo ).remove();
//or
foo.html("");
//or
foo.empty();
yogihosting
  • 5,494
  • 8
  • 47
  • 80
Anoop
  • 23,044
  • 10
  • 62
  • 76
-3

If you want to put something back into that div, the innerHTML is probably better.

My example:

<ul><div id="result"></div></ul>

<script>
  function displayHTML(result){
    var movieLink = document.createElement("li");
    var t = document.createTextNode(result.Title);
    movieLink.appendChild(t);
    outputDiv.appendChild(movieLink);
  }
</script>

If I use the .firstChild or .lastChild method the displayHTML() function doesnt work afterwards, but no problem with the .innerHTML method.

Axel
  • 3,331
  • 11
  • 35
  • 58
Bjathr
  • 85
  • 1
  • 10
-4

This is a pure javascript i am not using jQuery but works in all browser even IE and it is verry simple to understand

   <div id="my_div">
    <p>Paragraph one</p>
    <p>Paragraph two</p>
    <p>Paragraph three</p>
   </div>
   <button id ="my_button>Remove nodes ?</button>

   document.getElementById("my_button").addEventListener("click",function(){

  let parent_node =document.getElemetById("my_div"); //Div which contains paagraphs

  //Let find numbers of child inside the div then remove all
  for(var i =0; i < parent_node.childNodes.length; i++) {
     //To avoid a problem which may happen if there is no childNodes[i] 
     try{
       if(parent_node.childNodes[i]){
         parent_node.removeChild(parent_node.childNodes[i]);
       }
     }catch(e){
     }
  }

})

or you may simpli do this which is a quick way to do

document.getElementById("my_button").addEventListener("click",function(){

 let parent_node =document.getElemetById("my_div");
 parent_node.innerHTML ="";

})
Ir Calif
  • 460
  • 6
  • 7
-14

with jQuery :

$("#foo").find("*").remove();
Arnaud F.
  • 8,252
  • 11
  • 53
  • 102