305

Is there a way to convert HTML like:

<div>
<a href="#"></a>
<span></span>
</div>

or any other HTML string into DOM element? (So that I could use appendChild()). I know that I can do .innerHTML and .innerText, but that is not what I want -- I literally want to be capable of converting a dynamic HTML string into a DOM element so that I could pass it in a .appendChild().

Update: There seems to be confusion. I have the HTML contents in a string, as a value of a variable in JavaScript. There is no HTML content in the document.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Tower
  • 98,741
  • 129
  • 357
  • 507
  • 7
    Check this out: http://stackoverflow.com/questions/494143/how-do-i-create-a-new-dom-element-from-an-html-string-using-built-in-dom-methods – jhleath Jun 23 '10 at 18:00
  • If you want something closer HTML instead of node, I suggeste to use the following function ;) ` var stringToHTML = function (str) { var dom = document.createElement('div'); dom.innerHTML = str; return dom; }; ` – La pach' Jul 28 '21 at 09:08

9 Answers9

502

You can use a DOMParser, like so:

var xmlString = "<div id='foo'><a href='#'>Link</a><span></span></div>";
var doc = new DOMParser().parseFromString(xmlString, "text/xml");
console.log(doc.firstChild.innerHTML); // => <a href="#">Link...
console.log(doc.firstChild.firstChild.innerHTML); // => Link
maerics
  • 151,642
  • 46
  • 269
  • 291
  • That looks cleaner than my own solution. Let me see. – Tower Jun 23 '10 at 18:02
  • 3
    Thanks, it works great. After reading Mozilla article I realized that the browsers can also parse XML AJAX responses -- so browsers like IE that do not support DOMParser, I use synchronous AJAX calls with data URIs to parse the XML. :) – Tower Jun 23 '10 at 18:08
  • This is just what I was looking for! Works great for custom printing in a Windows 8 applications. – RestingRobot Oct 19 '12 at 20:41
  • There can be issues with this approach when IE goes into 'compatibility mode' as it is IE8+. – cmc Jan 03 '13 at 10:43
  • 10
    If you get errors because you're trying to load **HTML** instead of **XML** like the
    tag than look here: https://developer.mozilla.org/en-US/docs/DOM/DOMParser under "DOMParser HTML extension for other browsers" to support loading HTML
    – HMR May 10 '13 at 00:43
  • For anyone who is having trouble with inline styles not rendering, check out my Q&A: [Why is DOMParser not preserving inline styles of parsed DOM?](http://stackoverflow.com/q/38335920/1464112) – scniro Jul 12 '16 at 18:09
  • 15
    @maerics Why can't you just say `text/html` for HTML parsing? – John Strood Jul 14 '16 at 11:53
  • 11
    @Djack You can, but then you get 'html' and 'body' tags automatically generated. – akauppi Aug 05 '16 at 13:59
  • 34
    Note: I found it vital to use "text/html" instead of "text/xml", if placing the created nodes back to browser's DOM (Safari 9.1.2). Otherwise, the CSS rendering didn't work correctly. If you use this, use '.body' to bypass the auto-inserted body element. – akauppi Aug 05 '16 at 14:06
  • as of time of writing: this isn't supported on Opera Mini (3% of internet traffic) – Joseph Nields Jul 14 '17 at 19:25
  • I'm not able to get the rendered value. it showing me [object HTMLHtmlElement] – Shaikh Arbaaz Jun 12 '18 at 06:45
  • this worked for me adding an array of string "" tags to head dynamically. thanks. – arn-arn Apr 12 '19 at 04:09
  • This was very useful. I was attempting how to decode a document where the innerHTML was essentially another DOM fragment. – Ken Ingram Sep 17 '19 at 22:55
  • I'm trying to use this in jest, instead of getting document I'm getting DocumentImpl – vnxyz Jan 03 '20 at 06:23
  • 2
    @maerics This solution will not work, if you want to add special meaning to your element. For example `test` should redirect to *www.google.com* but it will not. To keep element context you have to change type to `text/html`. I have this [problem](https://stackoverflow.com/questions/63255501/a-element-doesnt-redirect-to-another-page-and-is-not-clickable) and I was really confused why this solution is not working. – Krzysztof Kaczyński Aug 05 '20 at 07:15
  • @ShaikhArbaaz may be you are converting it to string when you use `element.innerHTMl=dom;` use `element.appendChild(dom);` https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild – 27px Nov 19 '20 at 02:16
  • 2
    I get with `td.appendChild(domparsed)`: `Uncaught (in promise) DOMException: Failed to execute 'appendChild' on 'Node': Nodes of type '#document' may not be inserted inside nodes of type 'TD'.` – Timo May 25 '22 at 10:17
261

You typically create a temporary parent element to which you can write the innerHTML, then extract the contents:

var wrapper= document.createElement('div');
wrapper.innerHTML= '<div><a href="#"></a><span></span></div>';
var div= wrapper.firstChild;

If the element whose outer-HTML you've got is a simple <div> as here, this is easy. If it might be something else that can't go just anywhere, you might have more problems. For example if it were a <li>, you'd have to have the parent wrapper be a <ul>.

But IE can't write innerHTML on elements like <tr> so if you had a <td> you'd have to wrap the whole HTML string in <table><tbody><tr>...</tr></tbody></table>, write that to innerHTML and extricate the actual <td> you wanted from a couple of levels down.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • 17
    +1 this solution is likely more portable. – maerics Feb 18 '12 at 06:25
  • 5
    Keep in mind that this method will lose any event handlers or other properties that were assigned to it beforehand. – bryc Feb 03 '15 at 20:16
  • 17
    @bryc how does a string would have event handlers attached to it/ – Muhammad Umer Aug 16 '15 at 02:07
  • @Muhammad - perhaps by using something like `onclick="..."` although that won't work for event handlers not defined in the HTML of course, which is perhaps what you meant. – Danger Dec 27 '15 at 22:25
  • what if the string is like this `some string at the start
    anchor textinner text
    string end`?
    – Ari May 14 '16 at 10:37
  • This doesn't support HTML strings containing an `` element – Sam Denty Feb 09 '18 at 00:17
  • For complete fidelity, call `div.remove();` so that div.parentNode reflects the intention of what the dom is supposed to represent.. if you attach it immediately to another dom node then it won't much matter, but for the sake of fidelity, `div.remove();` is a good extra measure. – Steven Jan 14 '22 at 05:27
  • DOMParser made some errors in parsing but it worked perfect. – fingerpich Oct 27 '22 at 20:10
  • @Ari you can use this for html scripts containing `` if you use `document.createElement('html')` – Devin Rhode May 03 '23 at 14:43
83

Why not use insertAdjacentHTML

for example:

// <div id="one">one</div> 
var d1 = document.getElementById('one'); 
d1.insertAdjacentHTML('afterend', '<div id="two">two</div>');

// At this point, the new structure is:
// <div id="one">one</div><div id="two">two</div>here
Ram Y
  • 1,944
  • 17
  • 23
  • 4
    You are aware that you would have this element to be in the DOM for this to work right? and you don't really want this element anyway in the DOM, you only want to convert a string into HTML **in memory** – vsync Sep 29 '14 at 09:06
  • 1
    Well, @vsync, that's certainly what the MDN docs say, but it sounds fishy since there's no reason for this function to care whether the target node is in the DOM or not. Using today's browser (Chrome), I just tested it and, nope, doesn't have to be in the DOM at all. Any node with a parent will make insertAdjacentHTML() happy as a clam. Wonder if any other browser behaves differently... – Ron Burk Sep 09 '22 at 16:48
17

Check out John Resig's pure JavaScript HTML parser.

EDIT: if you want the browser to parse the HTML for you, innerHTML is exactly what you want. From this SO question:

var tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlString;
Community
  • 1
  • 1
orip
  • 73,323
  • 21
  • 116
  • 148
  • While writing an example I noticed that simple HTML tags render properly (on IE7) but when I try to do it with a script, which is the case i'm working on, the script doesn't work. Example: [Fiddle](http://jsfiddle.net/xETGh/). You might have to run the code on a local html page (jsfiddle doesn't work very well with IE7). – Yuval Apr 13 '14 at 14:34
  • This code has a minor issue: it doesn't work properly if `htmlString` likes to `...` :( – Aikon Mogwai Jun 06 '19 at 11:27
6

Okay, I realized the answer myself, after I had to think about other people's answers. :P

var htmlContent = ... // a response via AJAX containing HTML
var e = document.createElement('div');
e.setAttribute('style', 'display: none;');
e.innerHTML = htmlContent;
document.body.appendChild(e);
var htmlConvertedIntoDom = e.lastChild.childNodes; // the HTML converted into a DOM element :), now let's remove the
document.body.removeChild(e);
Tower
  • 98,741
  • 129
  • 357
  • 507
  • 11
    You don't need to add "e" to the document. You also don't need to do setAttribute on it. –  Jul 07 '12 at 09:20
2

Here is a little code that is useful.

var uiHelper = function () {

var htmls = {};

var getHTML = function (url) {
                /// <summary>Returns HTML in a string format</summary>
                /// <param name="url" type="string">The url to the file with the HTML</param>

    if (!htmls[url])
    {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url, false);
    xmlhttp.send();
    htmls[url] = xmlhttp.responseText;
     };
     return htmls[url];
    };

        return {
            getHTML: getHTML
        };
}();

--Convert the HTML string into a DOM Element

String.prototype.toDomElement = function () {

        var wrapper = document.createElement('div');
        wrapper.innerHTML = this;
        var df= document.createDocumentFragment();
        return df.addChilds(wrapper.children);
};

--prototype helper

HTMLElement.prototype.addChilds = function (newChilds) {
        /// <summary>Add an array of child elements</summary>
        /// <param name="newChilds" type="Array">Array of HTMLElements to add to this HTMLElement</param>
        /// <returns type="this" />
        for (var i = 0; i < newChilds.length; i += 1) { this.appendChild(newChilds[i]); };
        return this;
};

--Usage

 thatHTML = uiHelper.getHTML('/Scripts/elevation/ui/add/html/add.txt').toDomElement();
  • 2
    Uhhh... This is basically just @orip's answer but with a lot of utterly unnecessary cruft added, in addition to extending the native `String` and `HTMLElement` prototypes (something you generally really shouldn't do). This is irrelevant bordering on dangerous... – aendra Jul 04 '17 at 17:08
  • @aendrew this is to different examples. The prototype helper happens to be one of the best helpers I've got in my entire solution(s) I pass in an array of DOM elements [ele,ele,ele,ele,ele], so I beg to differ. And also, the first convert to DOM Element is awesome as well. Learn more about the DocumentFragment, "Which no one ever uses" and you see what's going on. THESE ARE HELPERS, in a util file. This is what is wrong with developers' in todays' world, if it is not a 3rd party they're clueless. I write all my JS from scratch. – Filling The Stack is What I DO May 06 '18 at 12:26
  • And to expand on my last comment, performance is to be taken seriously in most of my JS apps. – Filling The Stack is What I DO May 06 '18 at 12:27
  • 2
    I'm simply saying don't pollute the prototype of builtins. That's considered best practice these days and has absolutely nothing to do with writing JS from scratch, using libraries, or performance — it's about preventing bugs further down the road. – aendra May 11 '18 at 13:36
  • @FillingTheStackisWhatIDO, what is the difference between returning `wrapper.firstChild` from `toDomElement` and returning `df.addChilds(wrapper.children)`, apart from the fact that the later return an array of Nodes. – Tamil Vendhan Kanagarasu Jan 27 '22 at 02:15
1

You can do it like this:

String.prototype.toDOM=function(){
  var d=document
     ,i
     ,a=d.createElement("div")
     ,b=d.createDocumentFragment();
  a.innerHTML=this;
  while(i=a.firstChild)b.appendChild(i);
  return b;
};

var foo="<img src='//placekitten.com/100/100'>foo<i>bar</i>".toDOM();
document.body.appendChild(foo);
william malo
  • 2,480
  • 2
  • 18
  • 18
1

Just give an id to the element and process it normally eg:

<div id="dv">
<a href="#"></a>
<span></span>
</div>

Now you can do like:

var div = document.getElementById('dv');
div.appendChild(......);

Or with jQuery:

$('#dv').get(0).appendChild(........);
Sarfraz
  • 377,238
  • 77
  • 533
  • 578
  • This is a jQuery example you gave. I think @rFactor was looking for a more straight up Javascript example. – darcy Jun 23 '10 at 17:34
  • 3
    @clarke78: Before down voting, you should have seen that i have already given an example of plain javascript. – Sarfraz Jun 23 '10 at 17:35
  • 2
    The example does not work, because it applies to already existing DOM elements. Of course the situation is simple if the element is already a DOM element, but in my situation the HTML contents is the value of a variable, not part of the DOM. – Tower Jun 23 '10 at 17:47
  • @rFactor - are you willing/able to use jQuery? Parsing a string of HTML into DOM elements is very simple if you can use it – John Rasch Jun 23 '10 at 17:49
  • I am not using jQuery, but anything jQuery can do can be done with plain JavaScript, so, if you have any examples, let me know. Hmm, actually I think I found the answer my self. :P – Tower Jun 23 '10 at 17:54
0

Summery:

  1. make an invisible element
  2. add the HTML in
  3. get the element by getting the first child of it

function txtToElem(txt){
let a = document.createElement('div')
a.style.display='none'
document.body.appendChild(a)
a.innerHTML=txt
let b = a.children
document.body.removeChild(a)
return b
}

let htmltext='<span>hello sir</span>'
console.log(txtToElem(htmltext))
//testing that it works as an elem:
document.body.appendChild(txtToElem(htmltext)[0])
<h1>the rest of the website...</h1>

First, append a div onto the document, which is made invisible by using the hide css proporty.

Then, append the text to the div to the invisble div by using appendChild()

Finally, get the elements inside the invisible div using the Element.children proporty

Note:

replace the a.children with a.childnodes to get nested elements too

Cons:

  1. Only works in frontend applications