innerHTML including tags [duplicate] - javascript

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I do OuterHTML in firefox?
Could someone show me a method using javascript with which I can get the innerHTML of an element including the tags?
P.S. No jQuery please.
Edit:
Best Method:
function outerHTML(node){
// if IE, Chrome take the internal method otherwise build one
return node.outerHTML || (
function(n){
var div = document.createElement('div'), h;
div.appendChild( n.cloneNode(true) );
h = div.innerHTML;
div = null;
return h;
})(node);
}
Thanks to #Joel below for the solution.

The standard way is just to use the innerHTML property.
document.getElementById("element").innerHTML;
That will get you the full text of all of the HTML inside of the element. To get the element itself you use the outerHTML property.
document.getElementById("element").outerHTML;

I didn't like the posted function so here's something I consider better. Note that IE has different handling of event listeners for the innerHTML and outerHTML properties (that is a general comment, it is not specific to the following), so be careful. There are also differences in the serialisation algorithms so you probably won't get exactly the same inner or outerHTML from all browsers.
The first version below is essentially a more efficient version of the one posted earlier, it uses a better test (in my opinion) for the existence of an outerHTML property and it is more efficient because it doesn't create a new function every time and re-uses the div kept in a closure rather than creating a new one each time. Note that it only does this for browsers that don't have native support for outerHTML, otherwise the temporary div is not kept.
The second version is to be preferred, it is very similar to the above but rather than getting the innerHTML of a clone, it uses the actual node by temporarily replacing it with a shallow clone of itself, moving it to a div, getting the div's innerHTML, then putting it back. The shallow clone is necessary so the temporary replacement maintains a valid DOM (e.g. might be getting the outerHTML of a tr which can only be replaced with a tr).
xLib = {};
xLib.outerHTML = (function() {
var d = document.createElement('div');
// Use native outerHTML if available
if (typeof d.outerHTML == 'string') {
d = null;
return function(el) {
return el.outerHTML;
}
}
// Otherwise, use clone of node and innerHTML
return function(el) {
var html, t = el.cloneNode(true);
// Don't make a new div every time,
// use div in closure
d.appendChild(t);
html = d.innerHTML;
// Remove unwanted fragment
d.removeChild(t);
// Remove reference to fragment
t = null;
return html;
}
}());
xLib.outerHTML2 = (function() {
var d = document.createElement('div');
// Use native outerHTML if available
if (typeof d.outerHTML == 'string') {
d = null;
return function(el) {
return el.outerHTML;
}
}
// Otherwise, use a placeholder and
// remove node, add to temp element,
// get innerHTML and return node to document
return function(el) {
var html;
var d2 = el.cloneNode(false);
// Temporarily move el
el.parentNode.replaceChild(d2, el);
d.appendChild(t);
html = d.innerHTML;
// Put element back
el.parentNode.replaceChild(el, d2);
d2 = null;
return html;
}
}());

Related

jQuery append() vs appendChild()

Here's some sample code:
function addTextNode(){
var newtext = document.createTextNode(" Some text added dynamically. ");
var para = document.getElementById("p1");
para.appendChild(newtext);
$("#p1").append("HI");
}
<div style="border: 1px solid red">
<p id="p1">First line of paragraph.<br /></p>
</div>
What is the difference between append() and appendChild()?
Any real time scenarios?
The main difference is that appendChild is a DOM method and append is a jQuery method. The second one uses the first as you can see on jQuery source code
append: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
this.appendChild( elem );
}
});
},
If you're using jQuery library on your project, you'll be safe always using append when adding elements to the page.
No longer
now append is a method in JavaScript
MDN documentation on append method
Quoting MDN
The ParentNode.append method inserts a set of Node objects or DOMString objects after the last child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
This is not supported by IE and Edge but supported by Chrome(54+), Firefox(49+) and Opera(39+).
The JavaScript's append is similar to jQuery's append.
You can pass multiple arguments.
var elm = document.getElementById('div1');
elm.append(document.createElement('p'),document.createElement('span'),document.createElement('div'));
console.log(elm.innerHTML);
<div id="div1"></div>
append is a jQuery method to append some content or HTML to an element.
$('#example').append('Some text or HTML');
appendChild is a pure DOM method for adding a child element.
document.getElementById('example').appendChild(newElement);
I know this is an old and answered question and I'm not looking for votes I just want to add an extra little thing that I think might help newcomers.
yes appendChild is a DOM method and append is JQuery method but practically the key difference is that appendChild takes a node as a parameter by that I mean if you want to add an empty paragraph to the DOM you need to create that p element first
var p = document.createElement('p')
then you can add it to the DOM whereas JQuery append creates that node for you and adds it to the DOM right away whether it's a text element or an html element
or a combination!
$('p').append('<span> I have been appended </span>');
appendChild is a DOM vanilla-js function.
append is a jQuery function.
They each have their own quirks.
The JavaScript appendchild method can be use to append an item to another element. The jQuery Append element does the same work but certainly in less number of lines:
Let us take an example to Append an item in a list:
a) With JavaScript
var n= document.createElement("LI"); // Create a <li> node
var tn = document.createTextNode("JavaScript"); // Create a text node
n.appendChild(tn); // Append the text to <li>
document.getElementById("myList").appendChild(n);
b) With jQuery
$("#myList").append("<li>jQuery</li>")
appendChild is a pure javascript method where as append is a jQuery method.
I thought there is some confusion here so I'm going to clarify it.
Both 'append' and 'appendChild' are now native Javascript functions and can be used concurrently.
For example:
let parent_div = document.querySelector('.hobbies');
let list_item = document.createElement('li');
list_item.style.color = 'red';
list_item.innerText = "Javascript added me here"
//running either one of these functions yield same result
const append_element = parent_div.append(list_item);
const append_child_element = parent_div.appendChild(list_item);
However, the key difference is the return value
e.g
console.log(append_element) //returns undefined
whereas,
console.log(append_child_element) // returns 'li' node
Hence, the return value of append_child method can be used to store it in a variable and use it later, whereas, append is use and throw (anonymous) function by nature.

JS DOM equivalent for JQuery append

What is the standard DOM equivalent for JQuery
element.append("<ul><li><a href='url'></li></ul>")?
I think you have to extend the innerHTML property to do this
element[0].innerHTML += "<ul><li><a href='url'></a></li></ul>";
some explanation:
[0] needed because element is a collection
+= extend the innerHTML and do not overwrite
closing </a> needed as some browsers only allow valid html to be set to innerHTML
Hint:
As #dontdownvoteme mentioned this will of course only target the first node of the collection element. But as is the nature of jQuery the collection could contain more entries
Proper and easiest way to replicate JQuery append method in pure JavaScript is with "insertAdjacentHTML"
var this_div = document.getElementById('your_element_id');
this_div.insertAdjacentHTML('beforeend','<b>Any Content</b>');
More Info - MDN insertAdjacentHTML
Use DOM manipulations, not HTML:
let element = document.getElementById('element');
let list = element.appendChild(document.createElement('ul'));
let item = list.appendChild(document.createElement('li'));
let link = item.appendChild(document.createElement('a'));
link.href = 'https://example.com/';
link.textContent = 'Hello, world';
<div id="element"></div>
This has the important advantage of not recreating the nodes of existing content, which would remove any event listeners attached to them, for example.
from the jQuery source code:
append: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) {
this.appendChild( elem ); //<====
}
});
},
Note that in order to make it work you need to construct the DOM element from the string, it's being done with jQuery domManip function.
jQuery 1.7.2 source code
element.innerHTML += "<ul><li><a href='url'></li></ul>";

forcing firefox skip "text nodes" in DOM parsing by javascript

Hi
I'm writing a javascript code to traverse HTML dom and highlight elements.
My problem is firefox returns whitespaces as text node.
Is there any solution to force it to just return tags? for example I need "firstChild" always return first tag and not any text!
Thanks
You can check if a node is an element with node.nodeType === 1.
You can also implement the new DOM Travelsal API as functions.
var dummy = document.createElement("div");
var firstElementChild = ('firstElementChild' in dummy)
? function (el) {
return el.firstElementChild;
}
: function (el) {
el = el.firstChild;
while (el && el.nodeType !== 1)
el = el.nextSibling;
return el;
}
usage
firstElementChild(el)
You can use element.firstElementChild instead. Unfortunately, this isn't supported in IE8 and below.
Alternatively, you might want to write a small function to crawl the childNodes until you find the next element node.
Maybe you could try one of the other DOM traversal methods, such as a TreeWalker.

Remove all child elements of a DOM node in JavaScript

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>

Most efficient way to create and nest divs with appendChild using *plain* javascript (no libraries)

Is there a more efficient way to write the following appendChild / nesting code?
var sasDom, sasDomHider;
var d = document;
var docBody = d.getElementsByTagName("body")[0];
var newNode = d.createElement('span');
var secondNode = d.createElement('span');
// Hider dom
newNode.setAttribute("id", "sasHider");
docBody.appendChild(newNode);
sasDomHider = d.getElementById("sasHider");
// Copyier dom
secondNode.setAttribute("id", "sasText");
sasDomHider.appendChild(secondNode);
sasDom = d.getElementById("sasText");
Ok, question has changed. Blah. Here's the new answer:
You might gain a little bit in the way of execution efficiency by building the branch before appending it to the DOM tree (browser won't try to recalc anything while building). And a bit in the way of maintenance efficiency by reducing the number of superfluous variables:
var d = document;
var docBody = d.getElementsByTagName("body")[0];
// Copyier dom
var sasDom = d.createElement('span');
sasDom.setAttribute("id", "sasText");
// Hider dom
var sasDomHider = d.createElement('span');
sasDomHider.setAttribute("id", "sasHider");
sasDomHider.appendChild(sasDom); // append child to parent
docBody.appendChild(sasDomHider); // ...and parent to DOM body element
Original answer:
You're trying to insert the same element twice, in the same spot...
var newNode = d.createElement('span');
...That's the only place you're creating an element in this code. So there's only one element created. And you insert it after the last child element in the body here:
docBody.appendChild(newNode);
So far, so good. But then, you modify an attribute, and try to insert the same node again, after the last child of sasDomHider... which is itself! Naturally, you cannot make a node its own child.
Really, you want to just create a new element and work with that:
newNode = d.createElement('span');
newNode.setAttribute("id", "sasText");
sasDomHider.appendChild(newNode);
// the next line is unnecessary; we already have an element reference in newNode
// sasDom = d.getElementById("sasText");
// ... so just use that:
sasDom = newNode;
You don't need to search again for the nodes:
var d = document;
var docBody = d.body;
var sasDomHider = d.createElement('span');
var sasDom = d.createElement('span');
// Hider dom
sasDomHider.setAttribute("id", "sasHider");
docBody.appendChild(sasDomHider);
// Copyier dom
sasDom.setAttribute("id", "sasText");
sasDomHider.appendChild(sasDom);
It's because newNode references an instance of a HtmlElement which you are attempting to insert into two different places within the DOM. You'll need to create a new element each time (or use cloneNode, but there are cross browser discrepancies with how that works).
Something like this should work
var sasDom,
d = document,
docBody = d.getElementsByTagName("body")[0],
sasDomHider = d.createElement('span');
// Hider dom
sasDomHider.setAttribute("id", "sasHider");
docBody.appendChild(sasDomHider);
// Copyier dom
sasDom = sasDomHider.cloneNode(true);
sasDom.setAttribute("id", "sasText");
sasDomHider.appendChild(sasDom);
// job done. sasDomHider and sasDom still reference the
// created elements.
There are a few ways to make this more efficient (in terms of performance and code size/readability), most of which have been covered already:
// Hider dom
var sasDomHider = document.createElement('span');
sasDomHider.id = "sasHider";
// Copier dom
var sasDom = document.createElement('span');
sasDom.id = "sasText";
sasDomHider.appendChild(sasDom);
document.body.appendChild(sasDomHider);
Obtains body using document.body
Uses only one variable each for the nodes you've created
Removes the getElementById lines, since they get you references to the same elements you had already
Uses the id property of the elements rather than setAttribute, which is an unnecessary function call and more verbose
Creates the whole branch being added to the document before adding it, thus avoiding unnecessary repaint/reflow
Removes d as an alias for document: there's no need to keep another reference to the document hanging around
Removes the docBody variable, since you're only using it once
Generally one set of the body's innerHTML with the desired HTML be the most efficient method.
e.g.
document.body.innerHTML = document.body.innerHTML + '<span id="foo"></span><span id="bar"></span>';
In your example, the objects referenced by newNode, sasDomHider, and sasDom are all the same, all pointing at a single DOM element. You're trying to put it in two places at once. You need to clone it, or simply make a new <span> for the second instance. Merely changing the id attribute is not enough (you're changing the id of the one already in the document).

Categories

Resources