Get the string representation of a DOM node - javascript

Javascript: I have the DOM representation of a node (element or document) and I'm looking for the string representation of it. E.g.,
var el = document.createElement("p");
el.appendChild(document.createTextNode("Test"));
should yield:
get_string(el) == "<p>Test</p>";
I have the strong feeling, that I'm missing something trivially simple, but I just don't find a method that works in IE, FF, Safari and Opera. Therefore, outerHTML is no option.

You can create a temporary parent node, and get the innerHTML content of it:
var el = document.createElement("p");
el.appendChild(document.createTextNode("Test"));
var tmp = document.createElement("div");
tmp.appendChild(el);
console.log(tmp.innerHTML); // <p>Test</p>
EDIT:
Please see answer below about outerHTML. el.outerHTML should be all that is needed.

What you're looking for is 'outerHTML', but wee need a fallback coz it's not compatible with old browsers.
var getString = (function() {
var DIV = document.createElement("div");
if ('outerHTML' in DIV)
return function(node) {
return node.outerHTML;
};
return function(node) {
var div = DIV.cloneNode();
div.appendChild(node.cloneNode(true));
return div.innerHTML;
};
})();
// getString(el) == "<p>Test</p>"
You'll find my jQuery plugin here: Get selected element's outer HTML

I dont think you need any complicated script for this. Just use
get_string=(el)=>el.outerHTML;

Under FF you can use the XMLSerializer object to serialize XML into a string. IE gives you an xml property of a node. So you can do the following:
function xml2string(node) {
if (typeof(XMLSerializer) !== 'undefined') {
var serializer = new XMLSerializer();
return serializer.serializeToString(node);
} else if (node.xml) {
return node.xml;
}
}

Use Element#outerHTML:
var el = document.createElement("p");
el.appendChild(document.createTextNode("Test"));
console.log(el.outerHTML);
It can also be used to write DOM elements. From Mozilla's documentation:
The outerHTML attribute of the element DOM interface gets the serialized HTML fragment describing the element including its descendants. It can be set to replace the element with nodes parsed from the given string.
https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML

Try
new XMLSerializer().serializeToString(element);

Use element.outerHTML to get full representation of element, including outer tags and attributes.

You can simply use outerHTML property over the element. It will return what you desire.
Let's create a function named get_string(element)
var el = document.createElement("p");
el.appendChild(document.createTextNode("Test"));
function get_string(element) {
console.log(element.outerHTML);
}
get_string(el); // your desired output

If your element has parent
element.parentElement.innerHTML

I've found that for my use-cases I don't always want the entire outerHTML. Many nodes just have too many children to show.
Here's a function (minimally tested in Chrome):
/**
* Stringifies a DOM node.
* #param {Object} el - A DOM node.
* #param {Number} truncate - How much to truncate innerHTML of element.
* #returns {String} - A stringified node with attributes
* retained.
*/
function stringifyEl(el, truncate) {
var truncateLen = truncate || 50;
var outerHTML = el.outerHTML;
var ret = outerHTML;
ret = ret.substring(0, truncateLen);
// If we've truncated, add an elipsis.
if (outerHTML.length > truncateLen) {
ret += "...";
}
return ret;
}
https://gist.github.com/kahunacohen/467f5cc259b5d4a85eb201518dcb15ec

While outerHTML is usually the answer, for Attr and Text(Child classes of the Node interface), there are other properties:
(<Element>x).outerHTML
(<Text>x).data
(<Attr>x).value;
See https://developer.mozilla.org/en-US/docs/Web/API/Node for more types of Nodes such as DocumentFragment and Comment

I have wasted a lot of time figuring out what is wrong when I iterate through DOMElements with the code in the accepted answer. This is what worked for me, otherwise every second element disappears from the document:
_getGpxString: function(node) {
clone = node.cloneNode(true);
var tmp = document.createElement("div");
tmp.appendChild(clone);
return tmp.innerHTML;
},

if using react:
const html = ReactDOM.findDOMNode(document.getElementsByTagName('html')[0]).outerHTML;

Related

object HTMLDivElement to string [duplicate]

Imagine I have the following HTML:
<div><span><b>This is in bold</b></span></div>
I want to get the HTML for the div, including the div itself. Element.innerHTML only returns:
<span>...</span>
Any ideas? Thanks
Use outerHTML:
var el = document.getElementById( 'foo' );
alert( el.outerHTML );
Expanding on jldupont's answer, you could create a wrapping element on the fly:
var target = document.getElementById('myElement');
var wrap = document.createElement('div');
wrap.appendChild(target.cloneNode(true));
alert(wrap.innerHTML);
I am cloning the element to avoid having to remove and reinsert the element in the actual document. This might be expensive if the element you wish to print has a very large tree below it, though.
First, put on element that wraps the div in question, put an id attribute on the element and then use getElementById on it: once you've got the lement, just do 'e.innerHTML` to retrieve the HTML.
<div><span><b>This is in bold</b></span></div>
=>
<div id="wrap"><div><span><b>This is in bold</b></span></div></div>
and then:
var e=document.getElementById("wrap");
var content=e.innerHTML;
Note that outerHTML is not cross-browser compatible.
old question but for newcomers that come around :
document.querySelector('div').outerHTML
You'll want something like this for it to be cross browser.
function OuterHTML(element) {
var container = document.createElement("div");
container.appendChild(element.cloneNode(true));
return container.innerHTML;
}
If you want a lighter footprint, but a longer script, get the elements innerHTML and only create and clone the empty parent-
function getHTML(who,lines){
if(!who || !who.tagName) return '';
var txt, ax, str, el= document.createElement('div');
el.appendChild(who.cloneNode(false));
txt= el.innerHTML;
ax= txt.indexOf('>')+1;
str= txt.substring(0, ax)+who.innerHTML+ txt.substring(ax);
el= null;
return lines? str.replace(/> *</g,'>\n<'): str;
//easier to read if elements are separated
}
var x = $('#container').get(0).outerHTML;
as outerHTML is IE only, use this function:
function getOuterHtml(node) {
var parent = node.parentNode;
var element = document.createElement(parent.tagName);
element.appendChild(node);
var html = element.innerHTML;
parent.appendChild(node);
return html;
}
creates a bogus empty element of the type parent and uses innerHTML on it and then reattaches the element back into the normal dom
define function outerHTML based on support for element.outerHTML:
var temp_container = document.createElement("div"); // empty div not added to DOM
if (temp_container.outerHTML){
var outerHTML = function(el){return el.outerHTML||el.nodeValue} // e.g. textnodes do not have outerHTML
} else { // when .outerHTML is not supported
var outerHTML = function(el){
var clone = el.cloneNode(true);
temp_container.appendChild(clone);
outerhtml = temp_container.innerHTML;
temp_container.removeChild(clone);
return outerhtml;
};
};
var el = document.getElementById('foo');
el.parentNode.innerHTML;

$(selector, element) Native JS alternative

Hi I'm trying to remove all jQuery from my platform one line at a time.
But I'm having some trouble finding a replacement for this
$('[data-attribute="value"]', GenericHTMLElement);
I was hoping it would be something simple like
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value"></span><span data-something-else="1000"></span></div>';
var b = div.childNodes;
var a = b.querySelector('[data-attribute="value"]');
But that's not working either. Does have any suggestions for me?
As commented,
childNodes will give you a list of elements. This list will not have querySelector. If you loop over nodes, you should be able to get it though. But, my suggestion is just do div.querySelector(...)
To be specific, it will be of type NodeList. This is a collection of nodes. So you cannot run querySelector on it. You can either loop over all nodes and do querySelector on them or just so this operation on parent div.
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value">Dummy Text</span><span data-something-else="1000"></span></div>';
var b = div.childNodes;
console.log('Type of childNodes is: ', Object.prototype.toString.call(b))
// getting element using loop over childNodes
for(var i = 0; i<b.length; i++) {
var el = b[i].querySelector('[data-attribute="value"]');
el && console.log(el.textContent)
}
// getting element using parent elenent.
var el1 = div.querySelector('[data-attribute="value"]');
console.log(el1.textContent)
First you need to understand what the first code does. It searches for given selector, limiting it to HTMLElementObject scope. Understanding that we can try to do something similar.
From MSDN example, he is using body element:
var el = document.body.querySelector("style[type='text/css'], style:not([type])");
They have this example with data-attributes, take a look please.
The reason your attempt isn't working is that you're trying to call querySelector on a NodeList, which doesn't have a querySelector method.
If you try it on a single element, it works fine:
function mySelect(selector, el) {
return el.querySelector(selector);
}
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value"></span><span data-something-else="1000"></span></div>';
var b = div.childNodes[0];
console.log(mySelect('[data-attribute="value"]', b));
But this makes it so that mySelect(selector, el) is nothing more than an alias for el.querySelector(selector).
Presumably, you'd want to be able to evaluate a selector on multiple elements at once, and return multiple results, like jQuery does. In that case, you can do so by making some adjustments:
function flatMap(values, f) {
return Array.prototype.concat.apply([], values.map(f));
}
function mySelect(selector, els) {
return flatMap(els.length ? Array.from(els) : [els], function (el) {
return Array.from(el.querySelectorAll(selector));
});
}
var div = document.createElement('div');
div.innerHTML = '<div><span data-attribute="value">span 1</span><span data-something-else="1000"></span></div><div><span data-attribute="value">span 2</span></div>';
console.log(mySelect('[data-attribute="value"]', div.childNodes));
console.log(mySelect('[data-attribute="value"]', div.childNodes[0]));

Javascript Append Child AFTER Element

I would like to append an li element after another li inside a ul element using javascript, This is the code I have so far..
var parentGuest = document.getElementById("one");
var childGuest = document.createElement("li");
childGuest.id = "two";
I am familiar with appendChild,
parentGuest.appendChild(childGuest);
However this appends the new element inside the other, and not after. How can I append the new element after the existing one? Thanks.
<ul>
<li id="one"><!-- where the new li is being put --></li>
<!-- where I want the new li -->
</ul>
You can use:
if (parentGuest.nextSibling) {
parentGuest.parentNode.insertBefore(childGuest, parentGuest.nextSibling);
}
else {
parentGuest.parentNode.appendChild(childGuest);
}
But as Pavel pointed out, the referenceElement can be null/undefined, and if so, insertBefore behaves just like appendChild. So the following is equivalent to the above:
parentGuest.parentNode.insertBefore(childGuest, parentGuest.nextSibling);
You need to append the new element to existing element's parent before element's next sibling. Like:
var parentGuest = document.getElementById("one");
var childGuest = document.createElement("li");
childGuest.id = "two";
parentGuest.parentNode.insertBefore(childGuest, parentGuest.nextSibling);
Or if you want just append it, then:
var parentGuest = document.getElementById("one");
var childGuest = document.createElement("li");
childGuest.id = "two";
parentGuest.parentNode.appendChild(childGuest);
If you are looking for a plain JS solution, then you just use insertBefore() against nextSibling.
Something like:
parentGuest.parentNode.insertBefore(childGuest, parentGuest.nextSibling);
Note that default value of nextSibling is null, so, you don't need to do anything special for that.
Update: You don't even need the if checking presence of parentGuest.nextSibling like the currently accepted answer does, because if there's no next sibling, it will return null, and passing null to the 2nd argument of insertBefore() means: append at the end.
Reference:
nextSibling
insertBefore
.
IF you are using jQuery (ignore otherwise, I have stated plain JS answer above), you can leverage the convenient after() method:
$("#one").after("<li id='two'>");
Reference:
jQuery after()
after is now a JavaScript method
MDN Documentation
Quoting MDN
The ChildNode.after() method inserts a set of Node or DOMString objects in the children list of this ChildNode's parent, just after this ChildNode. DOMString objects are inserted as equivalent Text nodes.
The browser support is Chrome(54+), Firefox(49+) and Opera(39+). It doesn't support IE and Edge.
Snippet
var elm=document.getElementById('div1');
var elm1 = document.createElement('p');
var elm2 = elm1.cloneNode();
elm.append(elm1,elm2);
//added 2 paragraphs
elm1.after("This is sample text");
//added a text content
elm1.after(document.createElement("span"));
//added an element
console.log(elm.innerHTML);
<div id="div1"></div>
In the snippet, I used another term append too
This suffices :
parentGuest.parentNode.insertBefore(childGuest, parentGuest.nextSibling || null);
since if the refnode (second parameter) is null, a regular appendChild is performed. see here : http://reference.sitepoint.com/javascript/Node/insertBefore
Actually I doubt that the || null is required, try it and see.
You could also do
function insertAfter(node1, node2) {
node1.outerHTML += node2.outerHTML;
}
or
function insertAfter2(node1, node2) {
var wrap = document.createElement("div");
wrap.appendChild(node2.cloneNode(true));
var node2Html = wrap.innerHTML;
node1.insertAdjacentHTML('afterend', node2Html);
}

What's the effectiviest way of creating an element with multiple attributes and appending it to another element and have it available as a variable??

I wonder what's the effectiviest way of creating an element with id='id' class='class' attr='attr' .. and appending it to another element and have it available as a variable?
So far I can do it with one line of code. But it requires a new set of javascript framework. I just want to make sure that there is no effectivier way first.
My solution is to create a javascript framework, which got the following syntax:
var variable = elementTagAppend/Prepend/After/Before('JQuerySelector', elementToAppend/Prepend/After/BeforeTo);
For instance, if I want to create an div element with id='id', class='class', attr='attr' and append it to an another element called "another"
var variable = divAppend('#id.class[attr = "attr"]', another); Very effective right?
Or if I want to prepend a form element with id='inputForm', class='inputForms':
var variable = formPrepend('#inputForm.inputForms', another);
var element = $("<div/>", {
id: "id",
class: "class",
attr: "attr"
}).appendTo("#element");
appendTo returns a JQuery object, you can read more on http://api.jquery.com/appendTo/
Edit: It looks like you're asking if an effective way to create elements is having methods for all HTML tags, so that your library doesn't have to do any sort of text parsing/regex.
This isn't a solution as it makes development a lot slower.
One way is to use a function and DOM methods:
<script type="text/javascript">
var elementData = {
tagName: 'div',
properties: {
id: 'div0',
onclick: function(){alert(this.id);},
className: 'someClass'
},
content: '<h1>New Div</h1>'
}
function elementBuilder(obj) {
var el = document.createElement(obj.tagName);
var props = obj.properties;
for (var p in props) {
if (props.hasOwnProperty(p)) {
el[p] = props[p];
}
}
if (typeof obj.content == 'string') {
el.innerHTML = obj.content;
}
return el;
}
</script>
<button onclick="
var newElement = elementBuilder(elementData);
document.body.insertBefore(newElement, this.nextSibling);
">Insert element</button>
The above could be expanded to create more than one element per call, I'll leave that you.
Another method is to take an HTML string, convert it to an element (or elements) and return it in a document fragment:
<script type="text/javascript">
var htmlString = '<p><b>paragraph</b></p>' +
'<p>Another p</p>';
function byInnerHTML(s) {
var d = document.createElement('div');
d.innerHTML = s;
var frag = document.createDocumentFragment();
while (d.firstChild) {
frag.appendChild(d.firstChild);
}
return frag;
}
</script>
<button onclick="
var newContent = byInnerHTML(htmlString);
document.body.insertBefore(newContent, this.nextSibling);
">Insert element</button>
The second one returns a document fragment that can be inserted into the page. Of course if you want to generate parts of a table, the second method isn't quite so good.
In tune with a pure JavaScript implementation, here's my take on the problem:
function createElement(type, attributes, parent){
var node = document.createElement(type);
for (i in attributes){
node.setAttribute(i, attributes[i])
}
if (parent && parent.__proto__.appendChild){
parent.appendChild(node)
}
return node;
}
And you use it like this:
createElement("span", { "id": "theID", "class": "theClass", "title": "theAttribute"}, document.getElementById("theParent"))
I kept one of your initial requirements: append the node to some other element and also return it.
I particularly like RobG's second example with the fragment. Definitely something I'd try if I were using plain strings to represent HTML.
If it doesn't warrant the use-case, it doesn't always make sense to import a JS framework. jQuery is no exception. Should you decide more complex workflows are needed, that's your warrant! ;)

How to correctly use innerHTML to create an element (with possible children) from a html string?

Note: I do NOT want to use any framework.
The goal is just to create a function that will return an element based on an HTML string.
Assume a simple HTML Document like such:
<html>
<head></head>
<body>
</body>
</html>
All functions mentioned are in included the head section and all DOM creation/manipulation is done at the end of the body in a script tag.
I have a function createElement that takes a well formed HTML String as an argument. It goes like this:
function createElement(str)
{
var div = document.createElement('div');
div.innerHTML = str;
return div.childNodes;
}
Now this functions works great when you call it like such:
var e = createElement('<p id="myId" class="myClass">myInnerHTML</p>');
With the minor (possibly HUGE) problem that the element created isn't a 'true' element, it still has a parentNode of 'div'. If anyone knows how to fix that, then that would be awesome.
Now if I call the same function with a more complex string:
var e = createElement('<p id="myId" class="myClass">innerHTML<h2 id="h2ID" class="h2CLASS">Heading2</h2></p>');
It creates TWO children instead of ONE child with another child having another child.Once you do div.innerHTML = str. The innerHTML instead of
`<p id="myId" class="myClass">innerHTML <h2 id="h2ID" class="h2CLASS">Heading2</h2> </p>`
turns to
`<p id="myId" class="myClass">innerHTML</p> <h2 id="h2ID" class="h2CLASS">Heading2</h2>`
Questions:
Can I somehow get an element without a parent node after using .innerHTML?
Can I (in the case of the slightly complex string) get my function to return ONE element with the appropriate child instead of two elements. [It actually returns three, <p.myClass#myId>,<h2.h2CLASS#h2ID>, and another <p>]
This is similar to the answer from palswim, except that it doesn't bother with creating a clone, and uses a while() loop instead, always appending the node at [0].
function createElement( str ) {
var frag = document.createDocumentFragment();
var elem = document.createElement('div');
elem.innerHTML = str;
while (elem.childNodes[0]) {
frag.appendChild(elem.childNodes[0]);
}
return frag;
}
You'd have to attach the new element somewhere. Try using a DocumentFragment object in conjunction with the div you created:
function createElement(str) {
var div = document.createElement('div');
div.innerHTML = str;
var container = document.createDocumentFragment();
for (var i=0; i < div.childNodes.length; i++) {
var node = div.childNodes[i].cloneNode(true);
container.appendChild(node);
}
return container.childNodes;
}
It's more overhead, but it does what you want. Note that DOM elements' .insertAdjacentHTML member function is coming in HTML5.
For that complex string you passed, it isn't valid XHTML syntax - you can't have a block element as a child of <p> (<h2> is a block level element).

Categories

Resources