When removing an element with standard JavaScript, you must go to its parent first:
var element = document.getElementById("element-id");
element.parentNode.removeChild(element);
Having to go to the parent node first seems a bit odd to me, is there a reason JavaScript works like this?
I know that augmenting native DOM functions isn't always the best or most popular solution, but this works fine for modern browsers.
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
}
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
for(var i = this.length - 1; i >= 0; i--) {
if(this[i] && this[i].parentElement) {
this[i].parentElement.removeChild(this[i]);
}
}
}
And then you can remove elements like this
document.getElementById("my-element").remove();
or
document.getElementsByClassName("my-elements").remove();
Note: this solution doesn't work for IE 7 and below. For more info about extending the DOM read this article.
EDIT: Reviewing my answer in 2019, node.remove() has come to the rescue and can be used as follows (without the polyfill above):
document.getElementById("my-element").remove();
or
[...document.getElementsByClassName("my-elements")].map(n => n && n.remove());
These functions are available in all modern browsers (not IE). Read more on MDN.
Crossbrowser and IE >= 11:
document.getElementById("element-id").outerHTML = "";
You could make a remove function so that you wouldn't have to think about it every time:
function removeElement(id) {
var elem = document.getElementById(id);
return elem.parentNode.removeChild(elem);
}
Update 2011
This was added to the DOM spec back in 2011, so you can just use:
element.remove()
The DOM is organized in a tree of nodes, where each node has a value, along with a list of references to its child nodes. So element.parentNode.removeChild(element) mimics exactly what is happening internally: First you go the parent node, then remove the reference to the child node.
As of DOM4, a helper function is provided to do the same thing: element.remove(). This works in 96% of browsers (as of 2020), but not IE 11.
If you need to support older browsers, you can:
Remove elements via the parent node
Modify the native DOM functions, as in Johan Dettmar's answer, or
Use a DOM4 polyfill.
It's what the DOM supports. Search that page for "remove" or "delete" and removeChild is the only one that removes a node.
For removing one element:
var elem = document.getElementById("yourid");
elem.parentElement.removeChild(elem);
For removing all the elements with for example a certain class name:
var list = document.getElementsByClassName("yourclassname");
for(var i = list.length - 1; 0 <= i; i--)
if(list[i] && list[i].parentElement)
list[i].parentElement.removeChild(list[i]);
you can just use element.remove()
You can directly remove that element by using remove() method of DOM.
here's an example:
let subsWrapper = document.getElementById("element_id");
subsWrapper.remove();
//OR directly.
document.getElementById("element_id").remove();
The ChildNode.remove() method removes the object from the tree it belongs to.
https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove
Here is a fiddle that shows how you can call document.getElementById('my-id').remove()
https://jsfiddle.net/52kp584L/
**
There is no need to extend NodeList. It has been implemented already.
**
According to DOM level 4 specs, which is the current version in development, there are some new handy mutation methods available: append(), prepend(), before(), after(), replace(), and remove().
https://catalin.red/removing-an-element-with-plain-javascript-remove-method/
You can simply use
document.getElementById("elementID").outerHTML="";
It works in all browsers, even on Internet Explorer.
Having to go to the parent node first seems a bit odd to me, is there a reason JavaScript works like this?
The function name is removeChild(), and how is it possible to remove the child when there's no parent? :)
On the other hand, you do not always have to call it as you have shown. element.parentNode is only a helper to get the parent node of the given node. If you already know the parent node, you can just use it like this:
Ex:
// Removing a specified element when knowing its parent node
var d = document.getElementById("top");
var d_nested = document.getElementById("nested");
var throwawayNode = d.removeChild(d_nested);
https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild
=========================================================
To add something more:
Some answers have pointed out that instead of using parentNode.removeChild(child);, you can use elem.remove();. But as I have noticed, there is a difference between the two functions, and it's not mentioned in those answers.
If you use removeChild(), it will return a reference to the removed node.
var removedChild = element.parentNode.removeChild(element);
console.log(removedChild); //will print the removed child.
But if you use elem.remove();, it won't return you the reference.
var el = document.getElementById('Example');
var removedChild = el.remove(); //undefined
https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove
This behavior can be observed in Chrome and FF. I believe It's worth noticing :)
Hope my answer adds some value to the question and will be helpful!!
Functions that use ele.parentNode.removeChild(ele) won't work for elements you've created but not yet inserted into the HTML. Libraries like jQuery and Prototype wisely use a method like the following to evade that limitation.
_limbo = document.createElement('div');
function deleteElement(ele){
_limbo.appendChild(ele);
_limbo.removeChild(ele);
}
I think JavaScript works like that because the DOM's original designers held parent/child and previous/next navigation as a higher priority than the DHTML modifications that are so popular today. Being able to read from one <input type='text'> and write to another by relative location in the DOM was useful in the mid-90s, a time when the dynamic generation of entire HTML forms or interactive GUI elements was barely a twinkle in some developer's eye.
Shortest
I improve Sai Sunder answer because OP uses ID which allows to avoid getElementById:
elementId.remove();
box2.remove(); // remove BOX 2
this["box-3"].remove(); // remove BOX 3 (for Id with 'minus' character)
<div id="box1">My BOX 1</div>
<div id="box2">My BOX 2</div>
<div id="box-3">My BOX 3</div>
<div id="box4">My BOX 4</div>
Having to go to the parent node first seems a bit odd to me, is there
a reason JavaScript works like this?
IMHO: The reason for this is the same as I've seen in other environments: You are performing an action based on your "link" to something. You can't delete it while you're linked to it.
Like cutting a tree limb. Sit on the side closest to the tree while cutting or the result will be ... unfortunate (although funny).
From what I understand, removing a node directly does not work in Firefox, only Internet Explorer. So, to support Firefox, you have to go up to the parent to remove it's child.
Ref: http://chiragrdarji.wordpress.com/2007/03/16/removedelete-element-from-page-using-javascript-working-in-firefoxieopera/
This one actually comes from Firefox... for once, IE was ahead of the pack and allowed the removal of an element directly.
This is just my assumption, but I believe the reason that you must remove a child through the parent is due to an issue with the way Firefox handled the reference.
If you call an object to commit hari-kari directly, then immediately after it dies, you are still holding that reference to it. This has the potential to create several nasty bugs... such as failing to remove it, removing it but keeping references to it that appear valid, or simply a memory leak.
I believe that when they realized the issue, the workaround was to remove an element through its parent because when the element is gone, you are now simply holding a reference to the parent. This would stop all that unpleasantness, and (if closing down a tree node by node, for example) would 'zip-up' rather nicely.
It should be an easily fixable bug, but as with many other things in web programming, the release was probably rushed, leading to this... and by the time the next version came around, enough people were using it that changing this would lead to breaking a bunch of code.
Again, all of this is simply my guesswork.
I do, however, look forward to the day when web programming finally gets a full spring cleaning, all these strange little idiosyncracies get cleaned up, and everyone starts playing by the same rules.
Probably the day after my robot servant sues me for back wages.
// http://javascript.crockford.com/memory/leak.html
// cleans dom element to prevent memory leaks
function domPurge(d) {
var a = d.attributes, i, l, n;
if (a) {
for (i = a.length - 1; i >= 0; i -= 1) {
n = a[i].name;
if (typeof d[n] === 'function') {
d[n] = null;
}
}
}
a = d.childNodes;
if (a) {
l = a.length;
for (i = 0; i < l; i += 1) {
domPurge(d.childNodes[i]);
}
}
}
function domRemove(id) {
var elem = document.getElementById(id);
domPurge(elem);
return elem.parentNode.removeChild(elem);
}
This is the best function to remove an element without script error:
function Remove(EId)
{
return(EObj=document.getElementById(EId))?EObj.parentNode.removeChild(EObj):false;
}
Note to EObj=document.getElementById(EId).
This is ONE equal sign not ==.
if element EId exists then the function removes it, otherwise it returns false, not error.
Related
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.
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>
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.
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
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.
If you use jQuery:
$('#foo').empty();
If you don't:
var foo = document.getElementById('foo');
while (foo.firstChild) foo.removeChild(foo.firstChild);
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.
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.
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
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.
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
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();
}
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.
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()
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.
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.
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
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.
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())
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.
Simplest way of removing the child nodes of a node via Javascript
var myNode = document.getElementById("foo");
while(myNode.hasChildNodes())
{
myNode.removeChild(myNode.lastChild);
}
let el = document.querySelector('#el');
if (el.hasChildNodes()) {
el.childNodes.forEach(child => el.removeChild(child));
}
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!
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.
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]);
}
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.
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.
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')
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.
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>
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>
contenteditable div
((grade >= passingMark) && (grade<101))
HTML
<span class="cond frag">(<span class="cond frag">(grade >= passingMark)</span> && <span class="cond frag">(grade<101)</span>)</span>
How would i know if i am the last child of cond or there is no next sibling following me (grade<101)
Something like (in a loop):
$elem = $('.frag').eq(fragNum);
if ($elem.nextAll('.cond').length === 0 ) {
alert('last child detected');
}
If by "How would i know ..." you mean "In JavaScript, how would I get..."
MooTools makes it easy:
var last = document.getElement('.cond').getLast('span');
In this case, document.getElement will get the first element that matches .cond in document.
There will be a simple jQuery equivalent too.
If you are using native JS, you run into problems as there is no native support for "getElemntByClassName" in IE<9. There are a bunch of frameworks and tools to bridge these browser gaps like MooTools, JQuery, Prototype, etc, but if you need native and know the index position of the span with class .cond within a specific container, you could try:
var conds = document.getElementsByTagName("span")[0].getElementsByTagName("span");
var last = conds[conds.length-1];
Where document is your specific container and 0 is the position (first in container).
If this is the only problem you would like to solve, there is this 2008 "getElementsByClassName" solution that still works nicely is IE<9 by Robert Nyman - http://robertnyman.com/2008/05/27/the-ultimate-getelementsbyclassname-anno-2008/
Do you mean something like this?
http://jsfiddle.net/KyleMuir/nujr7/1/
var elements = document.getElementsByClassName('cond');
var totalElements = elements.length;
var lastElement = elements[totalElements -1];
Added console.logs to illustrate this clearer in the fiddle.
Hope this helps.
I have seen a few different methods to add elements to the DOM. The most prevelent seem to be, for example, either
document.getElementById('foo').innerHTML ='<p>Here is a brand new paragraph!</p>';
or
newElement = document.createElement('p');
elementText = document.createTextNode('Here is a brand new parahraph!');
newElement.appendChild(elementText);
document.getElementById('foo').appendChild(newElement);
but I'm not sure of the advantages to doing either one. Is there a rule of thumb as to when one should be done over the other, or is one of these just flat out wrong?
Some notes:
Using innerHTML is faster in IE, but slower in chrome + firefox. Here's one benchmark showing this with a constantly varying set of <div>s + <p>s; here's a benchmark showing this for a constant, simple <table>.
On the other hand, the DOM methods are the traditional standard -- innerHTML is standardized in HTML5 -- and allow you to retain references to the newly created elements, so that you can modify them later.
Because innerHTML is fast (enough), concise, and easy to use, it's tempting to lean on it for every situation. But beware that using innerHTML detaches all existing DOM nodes from the document. Here's an example you can test on this page.
First, let's create a function that lets us test whether a node is on the page:
function contains(parent, descendant) {
return Boolean(parent.compareDocumentPosition(descendant) & 16);
}
This will return true if parent contains descendant. Test it like this:
var p = document.getElementById("portalLink")
console.log(contains(document, p)); // true
document.body.innerHTML += "<p>It's clobberin' time!</p>";
console.log(contains(document, p)); // false
p = document.getElementById("portalLink")
console.log(contains(document, p)); // true
This will print:
true
false
true
It may not look like our use of innerHTML should have affected our reference to the portalLink element, but it does. It needs to be retrieved again for proper use.
There are a number of differences:
innerHTML has only been standardised by the W3C for HTML 5; even though it has been a de facto standard for some time now across all popular browsers, technically in HTML 4 it's a vendor extension that standards-adherent developers would never be caught dead using. On the other hand, it's much more convenient and practically it's supported by all browsers.
innerHTML replaces the current content of the element (it does not let you modify it). But again, you gain in convenience if you don't mind this limitation.
innerHTML has been measured to be much faster (admittedly, that test involves older versions browsers that are not widely used today).
innerHTML might represent a security risk (XSS) if it's set to a user-supplied value that has not been properly encoded (e.g. el.innerHTML = '<script>...').
Based on the above, it seems that a practical conclusion might be:
If you don't mind the fact that innerHTML is a bit limiting (only total replacement of DOM sub-tree rooted at target element) and you don't risk a vulnerability through injecting user-supplied content, use that. Otherwise, go with DOM.
Though this is an old thread, one thing that is not mentioned is the while innerHTML can be faster, care should be taken. Using innerHTML will render every child of the modified element, old and new alike. As such, one single innerHTML assignment is faster (slightly) than DOM create/append, but multiple innerHTML will definetly be slower.
For example:
for(let i=0; i < 10; i++)
document.body.innerHTML+='<div>some text</div>';
will be nearly nearly 5x slower than
let html = '';
for(let i=0; i < 10; i++)
html += '<div>some text</div>';
document.body.innerHTML = html;
Since innerHTML assignment is letting the browser natively create/append elements, the second methods results in 10 elements being natively created/appended, while the firstmethod results in 55 elements being created/appended (and 45 being destroyed): 1 element created on first loop-iteration, 2 elements created on the second loop-iteration (the original being destroyed), 3 elements created on the third loop-iteration (the previous 2 being destroyed), and so on.
If you use innerHTML for speed, you must make sure to create the entire html string first before making the innerHTML assignment, such as creating fresh DOM containers/elements. innerHTML, on the other hand, is a performance loser when appending any container with existing childNodes, especially those with large number of childNodes.
According to this benchmark data, you will receive much faster results with innerHTML than creating DOM elements. It's especially clear when using older IE versions.
First one is straight forward, easier to read, less code and might be faster.
Second one gives you much more control over the element you create, i.e. makes it much easier to modify the new Element using JS (like attaching events, or, just use it in your code).
Second way is for "purist" who like "clean" code (no quick and dirty).
I say, use both, see what fits you better and go with it.
I always prefer readability unless the perf difference is extreme. In a one-off case of this, it probably will be a marginal difference.
In a one-off case like this, setting the innerHTML property will be easiest to read.
But if you are doing a lot of programmatic content generation in JavaScript, it is cleaner and easier to read and understand the DOM option.
Example:
Compare this innerHTML code:
http://jsfiddle.net/P8m3K/1/
// Takes input of a value between 1 and 26, inclusive,
// and converts it to the appropriate character
function alphaToChar(alpha)
{
return String.fromCharCode('a'.charCodeAt() + alpha - 1);
}
var content = "<ul>";
for(i = 0; i < 10; ++i)
{
content += "<li>";
for(j = 1; j <= 26; ++j)
{
content += "<a href=\"" + alphaToChar(j) + ".html\">"
+ alphaToChar(j)
+ "</a>";
}
content += "</li>";
}
document.getElementById("foo").innerHTML = content;
To this DOM code:
http://jsfiddle.net/q6GB8/1/
// Takes input of a value between 1 and 26, inclusive,
// and converts it to the appropriate character
function alphaToChar(alpha)
{
return String.fromCharCode('a'.charCodeAt() + alpha - 1);
}
var list = document.createElement("ul");
for(i = 0; i < 10; ++i)
{
var item = document.createElement("li");
for(j = 1; j <= 26; ++j)
{
var link = document.createElement("a");
link.setAttribute("href", alphaToChar(j) + ".html");
link.innerText = alphaToChar(j);
item.appendChild(link);
}
list.appendChild(item);
}
document.getElementById("foo").appendChild(list);
At this level they start to become quite similar length wise.
But the DOM code will be easier to maintain, and you're a bit less likely to make a typo or mistake that is hard to diagnose, like omitting a closing tag. Either your elements will be in your document, or they won't.
With more complicated scenarios (like building treed menus), you'll probably come out ahead with DOM code.
With scenarios where you have to append multiple types of content together to build a document with more heterogeneous content, it becomes a slam dunk. You don't have to ensure you call your child append code before calling the parent append code.
With scenarios where add, remove, or modify existing static content, DOM will usually win.
If you start doing complicated DOM modifications (one of the last things I mentioned), you'll definitely want to check out a library built around DOM modifications, like jQuery.
Does anyone know how to tell if a cached jQuery object has gone stale, e.g. is no longer in the DOM? For example:
var $cached_elem = $('.the_button');
// .. and then later
$cached_elem.text('updating...');
I have recently encountered the situation where the $cached_elem is removed from the DOM due to some other event. So what I would like to do:
if ( $cache_elem.isStillInDOM() ){
// now do time consuming stuff with $cached_elem in DOM
}
Before anyone offers, I have already employed this, which is a fair analog for what I'm trying to do:
if ( $cached_elem.is(':visible') === true ){ ... }
However, this is not really the same thing and could fail in some cases.
So can anyone think of a simple way to check directly if a cached jQuery object is "stale"? I may be forced to write a plugin if not ...
if($elem.closest('body').length > 0) seems like it could do the trick.
$(function() {
var $button = $(".the_button");
alert (isStale($button));
$button.remove();
alert (isStale($button));
});
function isStale($elem)
{
return $elem.closest("body").length > 0;
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span class="the_button">Hello World</span>
</div>
Edit: Updated in response to Yi Jiang's comment so that it will return correctly if its parent element is removed
Edit 2: Updated in response to lonesomeday's comment - changed parents() to 'closest()` for performance improvement
The native document.contains() method should be faster than jQuery to determine if a cached jQuery element exists in the DOM.
if (document.contains($cached_elem[0])) {
// Element is still in the DOM
}
If the selector hasn't changed, just reinitialize it:
var $cached_elem = $('.the_button');
// code that might remove some elements of $cached_elem
$cached_elem = $('.the_button');
If you want to avoid duplicating the selector string, use the selector property of the original jQuery object:
var $cached_elem = $('.the_button');
// code that might remove some elements of $cached_elem
$cached_elem = $($cached_elem.selector);
if ($cached_elem.length) {
// element still exists
}
In a slightly variant case, one can directly compare the native DOM element wrapped by the jQuery object $cached_elem.
$cached_elem[0] === $($cached_elem.selector)[0]
Besides checking if the $cached_elem is still in DOM, this can tell you if it's still the original DOM element, rather than a re-created one with same attributes after certain/partial page refresh.
I have some code doing this :
var changes = document.getElementsByName(from);
for (var c=0; c<changes.length; c++) {
var ch = changes[c];
var current = new String(ch.innerHTML);
etc.
}
This works fine in FF and Chrome but not in IE7. Presumably because getElementsByName isn't working in IE. What's the best workaround?
In case you don't know why this isn't working in IE, here is the MSDN documentation on that function:
When you use the getElementsByName method, all elements in the document that have the specified NAME attribute or ID attribute value are returned.
Elements that support both the NAME attribute and the ID attribute are included in the collection returned by the getElementsByName method, but elements with a NAME expando are not included in the collection; therefore, this method cannot be used to retrieve custom tags by name.
Firefox allows getElementsByName() to retrieve elements that use a NAME expando, which is why it works. Whether or not that is a Good Thing™ may be up for debate, but that is the reality of it.
So, one option is to use the getAttribute() DOM method to ask for the NAME attribute and then test the value to see if it is what you want, and if so, add it to an array. This would require, however, that you iterate over all of the nodes in the page or at least within a subsection, which wouldn't be the most efficient. You could constrain that list beforehand by using something like getElementsByTagName() perhaps.
Another way to do this, if you are in control of the HTML of the page, is to give all of the elements of interest an Id that varies only by number, e.g.:
<div id="Change0">...</div>
<div id="Change1">...</div>
<div id="Change2">...</div>
<div id="Change3">...</div>
And then have JavaScript like this:
// assumes consecutive numbering, starting at 0
function getElementsByModifiedId(baseIdentifier) {
var allWantedElements = [];
var idMod = 0;
while(document.getElementById(baseIdentifier + idMod)) { // will stop when it can't find any more
allWantedElements.push(document.getElementById(baseIdentifier + idMod++));
}
return allWantedElements;
}
// call it like so:
var changes = getElementsByModifiedId("Change");
That is a hack, of course, but it would do the job you need and not be too inefficient compare to some other hacks.
If you are using a JavaScript framework/toolkit of some kind, you options are much better, but I don't have time to get into those specifics unless you indicate you are using one. Personally, I don't know how people live without one, they save so much time, effort and frustration that you can't afford not to use one.
There are a couple of problems:
IE is indeed confusing id="" with name=""
name="" isn't allowed on <span>
To fix, I suggest:
Change all the name="" to class=""
Change your code like this:
-
var changes = document.getElementById('text').getElementsByTagName('span');
for (var c=0; c<changes.length; c++) {
var ch = changes[c];
if (ch.className != from)
continue;
var current = new String(ch.innerHTML);
It's not very common to find elements using the NAME property. I would recommend switching to the ID property.
You can however find elements with a specific name using jQuery:
$("*[name='whatevernameYouWant']");
this will return all elements with the given name.
getElementsByName is supported in IE, but there are bugs. In particular it returns elements whose ‘id’ match the given value, as well as ‘name’. Can't tell if that's the problem you're having without a bit more context, code and actual error messages though.
In general, getElementsByName is probably best avoided, because the ‘name’ attribute in HTML has several overlapping purposes which can confuse. Using getElementById is much more reliable. When specifically working with form fields, you can more reliably use form.elements[name] to retrieve the fields you're looking for.
I've had success using a wrapper to return an array of the elements. Works in IE 6, and 7 too. Keep in mind it's not 100% the exact same thing as document.getElementsByName, since it's not a NodeList. But for what I need it for, which is to just run a for loop on an array of elements to do simple things like setting .disabled = true, it works well enough.
Even though this function still uses getElementsByName, it works if used this way. See for yourself.
function getElementsByNameWrapper(name) {
a = new Array();
for (var i = 0; i < document.getElementsByName(name).length; ++i) {
a.push(document.getElementsByName(name)[i]);
}
return a;
}
Workaround
var listOfElements = document.getElementsByName('aName'); // Replace aName with the name you're looking for
// IE hack, because it doesn't properly support getElementsByName
if (listOfElements.length == 0) { // If IE, which hasn't returned any elements
var listOfElements = [];
var spanList = document.getElementsByTagName('*'); // If all the elements are the same type of tag, enter it here (e.g.: SPAN)
for(var i = 0; i < spanList.length; i++) {
if(spanList[i].getAttribute('name') == 'aName') {
listOfElements.push(spanList[i]);
}
}
}
Just another DOM bug in IE:
Bug 1: Click here
Bug 2: Click here