64

In my code, I fairly frequently need to replace all children of a certain HTML container with a new list of children.

What is the fastest way to do this? My current approach is collecting all new elements into a DocumentFragment. The only way I've found to then actually replace the children is to remove all the children one by one, and append the fragment. Is there no faster way?

Note: the solution needs not be cross-browser, but should preferably not require 3d-party components such as jQuery. The target-device is WebKit on a very slow CPU so I need to keep full control of any reflows.

The Fool
  • 16,715
  • 5
  • 52
  • 86
Rawler
  • 1,480
  • 1
  • 11
  • 23
  • 1
    possible duplicate of [Remove all child elements of a DOM node in JavaScript?](http://stackoverflow.com/questions/3955229/remove-all-child-elements-of-a-dom-node-in-javascript) – cimmanon Apr 17 '15 at 19:47
  • 3
    Old question, but please note that instead of erasing the content and then adding new content, you can use the replaceChild function ( https://developer.mozilla.org/en-US/docs/Web/API/Node/replaceChild ) like this: `parentNode.replaceChild(newNode , nodeToBeReplaced);` – flen Jul 05 '17 at 08:14

6 Answers6

71

If you simply want to replace all children, regarding of the type, why don't you just set its content to '' and then add your code:

container.innerHTML = '';
container.appendChild( newContainerElements );

that would basically remove all the children in the fastest possible way :)

Stefan
  • 3,962
  • 4
  • 34
  • 39
  • Found this solution myself later when concentrating on optimizing the remove-process. AFAIU the innerHTML-optimization isn't fully cross-browser-safe, and often not mentioned in "remove all children"-questions, but as stated I only care about WebKit and it seems to work for me. :) – Rawler Feb 14 '11 at 20:16
  • 1
    Using `inner`HTML = "";` doesn't appear to work in either Edge, or apparently IE. – Carcigenicate Jan 02 '16 at 22:00
  • that is two actionable render view maybe ther is a falster way – pery mimon Aug 12 '17 at 20:33
  • 2
    Try `container.textContent = ''` instead of `innerHTML` – fregante Aug 28 '17 at 11:30
  • 4
    we can now use `container.empty();` instead of `container.innerHTML = ''` – Eric Aug 02 '19 at 18:02
  • 1
    @Eric I can't find any documentation on that. Can you provide a link? – dkniffin Jun 02 '20 at 19:11
  • 4
    @dkniffin this was a while ago. i'm guessing i was referring to [the `empty()` jQuery method](https://www.w3schools.com/jquery/html_empty.asp#:~:text=The%20empty()%20method%20removes,use%20the%20detach()%20method), instead of a vanilla JS function. (whoops) also just tested..vanilla JS doesn't seem to have an `empty()` funciton – Eric Jun 02 '20 at 19:25
45

2020 Update - 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: a) remove all existing children, and b) 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 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 a) without requiring innerHTML, and b) in one step instead of multiple.

Mason Freed
  • 5,493
  • 1
  • 19
  • 13
  • Safari doesn't support `replaceChildren` as of Feb 2021, [source](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/replaceChildren) – Duy Phan Feb 12 '21 at 14:47
  • 4
    **Yes, it does.** Did you [try it in Safari](https://jsbin.com/kuqucid)? It works great. I've raised an [issue with MDN](https://github.com/mdn/browser-compat-data/issues/8145) to get the compat data corrected. – Mason Freed Feb 14 '21 at 19:04
  • 3
    Also, please don't edit my post to make it less accurate. I've put back Safari 14+. – Mason Freed Feb 14 '21 at 19:09
  • MDN data has now been corrected to show Safari support: https://caniuse.com/?search=replacechildren – Mason Freed Apr 29 '21 at 16:57
  • `replaceChildren()` with nothing inside is not working for me on Safari 14.0.3 (macOS). It works with `replaceChildren('')` or `replaceChildren([])`. Pretty weird – Levy Srugo Sep 12 '21 at 20:30
  • Damn, I thought the "..." was just an ellipse, but was the necessary syntax. Without "...", it just add the string of the array itself. – Damn Vegetables Apr 19 '22 at 17:37
8

Use modern JS! Directly use remove rather than removeChild

while (container.firstChild) {
    container.firstChild.remove();
}

Alternatively:

let child;
while (child = container.firstChild) {
    child.remove();
}
Community
  • 1
  • 1
Gibolt
  • 42,564
  • 15
  • 187
  • 127
  • 1
    How portable is it? – George Sovetov Oct 20 '18 at 20:42
  • 3
    This should only be used if backward compatibility is, and will never be, an issue. I don't think of code in terms of "modern", but in terms of "flexibility" and if I must type a few more characters to leave backward compatibility, why not.. good habit – vsync Feb 07 '20 at 08:57
7

It is not directly solving the question but in most cases it is usable and probably one of the more performant ways.

You can swap out the whole node instead of deleting and filling its content.

oldNode.parentElement.replaceChild(newNode, oldNode)
The Fool
  • 16,715
  • 5
  • 52
  • 86
2

A possible alternative where setting innerHTML doesn't work:

while(container.firstChild)
{
  container.removeChild(container.firstChild);
}
container.appendChild(newChild)
Tom Kay
  • 1,512
  • 15
  • 25
  • 3
    There's (nearly) zero chance `innerHTML` *"doesn't work"*. Maybe a re-phrasing should be considered – vsync Feb 07 '20 at 08:58
1

from the top answer, we could also use outerHTML to do it in one line.

container.innerHTML = newContainerElements.outerHTML
  • how didn't i think of that... now instead of cloning an element removing all attributes from it then removing all children from the new element and appending the new one I can just do this: `popup.innerHTML = \`${target.parentElement.innerHTML}\`;` – Volper Aug 06 '21 at 08:28