Reputation: 59658
I have an HTML string representing an element: '<li>text</li>'
. I'd like to append it to an element in the DOM (a ul
in my case). How can I do this with Prototype or with DOM methods?
(I know i could do this easily in jQuery, but unfortunately we're not using jQuery.)
Upvotes: 979
Views: 1019330
Reputation: 155155
HTML 5 introduced the <template>
element which can be used for this purpose (as now described in the WhatWG spec and MDN docs).
A <template>
element is used to declare fragments of HTML that can be utilized in scripts. The element is represented in the DOM as a HTMLTemplateElement
which has a .content
property of DocumentFragment
type, to provide access to the template's contents. This means that you can convert an HTML string to DOM elements by setting the innerHTML
of a <template>
element, then reaching into the template
's .content
property.
Examples:
/**
* @param {String} HTML representing a single node (which might be an Element,
a text node, or a comment).
* @return {Node}
*/
function htmlToNode(html) {
const template = document.createElement('template');
template.innerHTML = html;
const nNodes = template.content.childNodes.length;
if (nNodes !== 1) {
throw new Error(
`html parameter must represent a single node; got ${nNodes}. ` +
'Note that leading or trailing spaces around an element in your ' +
'HTML, like " <img/> ", get parsed as text nodes neighbouring ' +
'the element; call .trim() on your input to avoid this.'
);
}
return template.content.firstChild;
}
const td = htmlToNode('<td>foo</td>'),
div = htmlToNode('<div><span>nested</span> <span>stuff</span></div>');
/**
* @param {String} HTML representing any number of sibling nodes
* @return {NodeList}
*/
function htmlToNodes(html) {
const template = document.createElement('template');
template.innerHTML = html;
return template.content.childNodes;
}
const rows = htmlToNodes('<tr><td>foo</td></tr><tr><td>bar</td></tr>');
Note that similar approaches that use a different container element such as a div
don't quite work. HTML has restrictions on what element types are allowed to exist inside which other element types; for instance, you can't put a td
as a direct child of a div
. This causes these elements to vanish if you try to set the innerHTML
of a div
to contain them. Since <template>
s have no such restrictions on their content, this shortcoming doesn't apply when using a template.
Using <template>
s was previously not always possible due to Internet Explorer not supporting <template>
s, but IE support is almost never a consideration nowadays and Can I Use characterises <template>
s as "baseline" functionality "widely available across major browsers". Except in the rare few cases where you find yourself writing web content for dead browsers, this approach will now reliably work for you.
Upvotes: 721
Reputation: 21
What about using outerHTML ?
*element*.appendChild( document.createElement('li')).outerHTML= '<li>text</li>' ;
The parameter for createElement doesn't need to match the element type specified in your string, but it does need to be a legal child of element
*element*.appendChild( document.createElement('a')).outerHTML= '<li>text</li>' ;
works just as well
Upvotes: 2
Reputation: 608
Solution - works with all browsers since IE 4.0
var htmlString = `<body><header class="text-1">Hello World</header><div id="table"><!--TABLE HERE--></div></body>`;
var tableString = `<table class="table"><thead><tr><th>th cell</th></tr></thead><tbody><tr><td>td cell</td></tr></tbody></table>`;
var doc = document.implementation.createHTMLDocument();
doc.open();
doc.write(htmlString);
doc.getElementById('table').insertAdjacentHTML('beforeend', tableString);
doc.close();
console.log(doc);
Or you can use DOMParser
var doc = (new DOMParser).parseFromString(htmlString, "text/html");
doc.getElementById('table').insertAdjacentHTML('beforeend', tableString);
console.log(doc);
Upvotes: 5
Reputation: 47
Visit https://www.codegrepper.com/code-examples/javascript/convert+a+string+to+html+element+in+js
const stringToHtml = function (str) {
var parser = new DOMParser();
var doc = parser.parseFromString(str, 'text/html');
return doc.body;
}
Upvotes: 1
Reputation: 2088
Example with latest JS:
<template id="woof-sd-feature-box">
<div class="woof-sd-feature-box" data-key="__KEY__" data-title="__TITLE__" data-data="__OPTIONS__">
<h4>__TITLE__</h4>
<div class="woof-sd-form-item-anchor">
<img src="img/move.png" alt="">
</div>
</div>
</template>
<script>
create(example_object) {
let html = document.getElementById('woof-sd-feature-box').innerHTML;
html = html.replaceAll('__KEY__', example_object.dataset.key);
html = html.replaceAll('__TITLE__', example_object.dataset.title);
html = html.replaceAll('__OPTIONS__', example_object.dataset.data);
//convertion HTML to DOM element and prepending it into another element
const dom = (new DOMParser()).parseFromString(html, "text/html");
this.container.prepend(dom.querySelector('.woof-sd-feature-box'));
}
</script>
Upvotes: -1
Reputation: 117028
Note: most current browsers support HTML <template>
elements, which provide a more reliable way of turning creating elements from strings. See Mark Amery's answer below for details.
For older browsers, and node/jsdom: (which doesn't yet support <template>
elements at the time of writing), use the following method. It's the same thing the libraries use to do to get DOM elements from an HTML string (with some extra work for IE to work around bugs with its implementation of innerHTML
):
function createElementFromHTML(htmlString) {
var div = document.createElement('div');
div.innerHTML = htmlString.trim();
// Change this to div.childNodes to support multiple top-level nodes.
return div.firstChild;
}
Note that unlike HTML templates this won't work for some elements that cannot legally be children of a <div>
, such as <td>
s.
If you're already using a library, I would recommend you stick to the library-approved method of creating elements from HTML strings:
update()
method.jQuery(html)
and jQuery.parseHTML
methods.Upvotes: 1118
Reputation: 3403
I added a Document
prototype that creates an element from string:
Document.prototype.createElementFromString = function (str) {
const element = new DOMParser().parseFromString(str, 'text/html');
const child = element.documentElement.querySelector('body').firstChild;
return child;
};
Usage:
document.createElementFromString("<h1>Hello World!</h1>");
Upvotes: 11
Reputation: 1505
I have searched a lot for this myself and came across this solution which is neat.
const stringToHTML = (str) => {
var parser = new DOMParser();
var doc = parser.parseFromString(str, 'text/html');
return doc.body;
};
String that I wanted to convert:
'<iframe src="https://player.vimeo.com/video/578680903?h=ea840f9223&app_id=122963" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen title="Total Body Balance"></iframe>'
The result:
<body><iframe src="https://player.vimeo.com/video/578680903?h=ea840f9223&app_id=122963" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen="" title="Total Body Balance"></iframe></body>
Upvotes: 2
Reputation: 921
Here's how to do it with PrototypeJS (as originally requested by the OP 12 years ago):
HTML:
<ul id="mylist"></ul>
JS:
$('mylist').insert('<li>text</li>');
Note that this is not jQuery!
Upvotes: 4
Reputation: 1862
For certain html fragments like <td>test</td>
, div.innerHTML, DOMParser.parseFromString and range.createContextualFragment (without the right context) solutions mentioned in other answers here, won't create the <td>
element.
jQuery.parseHTML() handles them properly (I extracted jQuery 2's parseHTML function into an independent function that can be used in non-jquery codebases).
If you are only supporting Edge 13+, it is simpler to just use the HTML5 template tag:
function parseHTML(html) {
var t = document.createElement('template');
t.innerHTML = html;
return t.content;
}
var documentFragment = parseHTML('<td>Test</td>');
Upvotes: 47
Reputation: 8138
I've linked from this article.( Converting HTML string into DOM elements? )
For me, I want to find a way to convert a string into an HTML element. If you also have this need, you can try the following
const frag = document.createRange().createContextualFragment(
`<a href="/link.js">js</a>
<a>go</a>
`
)
const aCollection = frag.querySelectorAll("a")
for (let [key, a] of Object.entries(aCollection)) {
console.log(a.getAttribute("href"), a.textContent)
}
Upvotes: -1
Reputation: 1146
Why don't do with native js?
var s="<span class='text-muted' style='font-size:.75em; position:absolute; bottom:3px; left:30px'>From <strong>Dan's Tools</strong></span>"
var e=document.createElement('div')
var r=document.createRange();
r.selectNodeContents(e)
var f=r.createContextualFragment(s);
e.appendChild(f);
e = e.firstElementChild;
Upvotes: 8
Reputation: 718
Template
Template's
innerHTML to your string
.trim()
Array
of Template's
childrenchildren
, child
, or
function toElement(s='',c,t=document.createElement('template'),l='length'){
t.innerHTML=s.trim();c=[...t.content.childNodes];return c[l]>1?c:c[0]||'';}
console.log(toElement());
console.log(toElement(''));
console.log(toElement(' '));
console.log(toElement('<td>With td</td>'));
console.log(toElement('<tr><td>With t</td></tr>'));
console.log(toElement('<tr><td>foo</td></tr><tr><td>bar</td></tr>'));
console.log(toElement('<div><span>nested</span> <span>stuff</span></div>'));
Upvotes: 6
Reputation: 4510
No need for any tweak, you got a native API:
const toNodes = html =>
new DOMParser().parseFromString(html, 'text/html').body.childNodes[0]
Upvotes: 52
Reputation: 31
Fastest solution to render DOM from string:
let render = (relEl, tpl, parse = true) => {
if (!relEl) return;
const range = document.createRange();
range.selectNode(relEl);
const child = range.createContextualFragment(tpl);
return parse ? relEl.appendChild(child) : {relEl, el};
};
And here u can check performance for DOM manipulation React vs native JS
Now u can simply use:
let element = render(document.body, `
<div style="font-size:120%;line-height:140%">
<p class="bold">New DOM</p>
</div>
`);
And of course in near future u use references from memory cause var "element" is your new created DOM in your document.
And remember "innerHTML=" is very slow :/
Upvotes: 3
Reputation:
<template>
Demo
"use strict";
/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @description HTML5 Template
* @augments
* @example
*
*/
/*
<template>
<h2>Flower</h2>
<img src="https://www.w3schools.com/tags/img_white_flower.jpg">
</template>
<template>
<div class="myClass">I like: </div>
</template>
*/
const showContent = () => {
// let temp = document.getElementsByTagName("template")[0],
let temp = document.querySelector(`[data-tempalte="tempalte-img"]`),
clone = temp.content.cloneNode(true);
document.body.appendChild(clone);
};
const templateGenerator = (datas = [], debug = false) => {
let result = ``;
// let temp = document.getElementsByTagName("template")[1],
let temp = document.querySelector(`[data-tempalte="tempalte-links"]`),
item = temp.content.querySelector("div");
for (let i = 0; i < datas.length; i++) {
let a = document.importNode(item, true);
a.textContent += datas[i];
document.body.appendChild(a);
}
return result;
};
const arr = ["Audi", "BMW", "Ford", "Honda", "Jaguar", "Nissan"];
if (document.createElement("template").content) {
console.log("YES! The browser supports the template element");
templateGenerator(arr);
setTimeout(() => {
showContent();
}, 0);
} else {
console.error("No! The browser does not support the template element");
}
@charset "UTf-8";
/* test.css */
:root {
--cololr: #000;
--default-cololr: #fff;
--new-cololr: #0f0;
}
[data-class="links"] {
color: white;
background-color: DodgerBlue;
padding: 20px;
text-align: center;
margin: 10px;
}
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Template Test</title>
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<section>
<h1>Template Test</h1>
</section>
<template data-tempalte="tempalte-img">
<h3>Flower Image</h3>
<img src="https://www.w3schools.com/tags/img_white_flower.jpg">
</template>
<template data-tempalte="tempalte-links">
<h3>links</h3>
<div data-class="links">I like: </div>
</template>
<!-- js -->
</body>
</html>
Upvotes: 4
Reputation: 5630
Use insertAdjacentHTML(). It works with all current browsers, even with IE11.
var mylist = document.getElementById('mylist');
mylist.insertAdjacentHTML('beforeend', '<li>third</li>');
<ul id="mylist">
<li>first</li>
<li>second</li>
</ul>
Upvotes: 265
Reputation: 1
Here is working code for me
I wanted to convert 'Text' string to HTML element
var diva = UWA.createElement('div');
diva.innerHTML = '<a href="http://wwww.example.com">Text</a>';
var aelement = diva.firstChild;
Upvotes: -3
Reputation: 19
You can use the following function to convert the text "HTML" to the element
function htmlToElement(html)
{
var element = document.createElement('div');
element.innerHTML = html;
return(element);
}
var html="<li>text and html</li>";
var e=htmlToElement(html);
Upvotes: -2
Reputation: 77167
Newer DOM implementations have range.createContextualFragment
, which does what you want in a framework-independent way.
It's widely supported. To be sure though, check its compatibility down in the same MDN link, as it will be changing. As of May 2017 this is it:
Feature Chrome Edge Firefox(Gecko) Internet Explorer Opera Safari
Basic support (Yes) (Yes) (Yes) 11 15.0 9.1.2
Upvotes: 34
Reputation: 73988
You can create valid DOM nodes from a string using:
document.createRange().createContextualFragment()
The following example adds a button element in the page taking the markup from a string:
let html = '<button type="button">Click Me!</button>';
let fragmentFromString = function (strHTML) {
return document.createRange().createContextualFragment(strHTML);
}
let fragment = fragmentFromString(html);
document.body.appendChild(fragment);
Upvotes: 17
Reputation: 539
I am using this method (Works in IE9+), although it will not parse <td>
or some other invalid direct childs of body:
function stringToEl(string) {
var parser = new DOMParser(),
content = 'text/html',
DOM = parser.parseFromString(string, content);
// return element
return DOM.body.childNodes[0];
}
stringToEl('<li>text</li>'); //OUTPUT: <li>text</li>
Upvotes: 11
Reputation: 734
function domify (str) {
var el = document.createElement('div');
el.innerHTML = str;
var frag = document.createDocumentFragment();
return frag.appendChild(el.removeChild(el.firstChild));
}
var str = "<div class='foo'>foo</div>";
domify(str);
Upvotes: -1
Reputation: 1731
For the heck of it I thought I'd share this over complicated but yet simple approach I came up with... Maybe someone will find something useful.
/*Creates a new element - By Jamin Szczesny*/
function _new(args){
ele = document.createElement(args.node);
delete args.node;
for(x in args){
if(typeof ele[x]==='string'){
ele[x] = args[x];
}else{
ele.setAttribute(x, args[x]);
}
}
return ele;
}
/*You would 'simply' use it like this*/
$('body')[0].appendChild(_new({
node:'div',
id:'my-div',
style:'position:absolute; left:100px; top:100px;'+
'width:100px; height:100px; border:2px solid red;'+
'cursor:pointer; background-color:HoneyDew',
innerHTML:'My newly created div element!',
value:'for example only',
onclick:"alert('yay')"
}));
Upvotes: 1
Reputation: 2506
Heres a simple way to do it:
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);
Upvotes: 17
Reputation: 11586
Late but just as a note;
It's possible to add a trivial element to target element as a container and remove it after using.
// Tested on chrome 23.0, firefox 18.0, ie 7-8-9 and opera 12.11.
<div id="div"></div>
<script>
window.onload = function() {
var foo, targetElement = document.getElementById('div')
foo = document.createElement('foo')
foo.innerHTML = '<a href="#" target="_self">Text of A 1.</a> '+
'<a href="#" onclick="return !!alert(this.innerHTML)">Text of <b>A 2</b>.</a> '+
'<hr size="1" />'
// Append 'foo' element to target element
targetElement.appendChild(foo)
// Add event
foo.firstChild.onclick = function() { return !!alert(this.target) }
while (foo.firstChild) {
// Also removes child nodes from 'foo'
targetElement.insertBefore(foo.firstChild, foo)
}
// Remove 'foo' element from target element
targetElement.removeChild(foo)
}
</script>
Upvotes: 3
Reputation: 937
var jtag = $j.li({ child:'text' }); // Represents: <li>text</li>
var htmlContent = $('mylist').html();
$('mylist').html(htmlContent + jtag.html());
Use jnerator
Upvotes: -4
Reputation: 731
This will work too:
$('<li>').text('hello').appendTo('#mylist');
It feels more like a jquery way with the chained function calls.
Upvotes: -9
Reputation: 663
Here's my code, and it works:
function parseTableHtml(s) { // s is string
var div = document.createElement('table');
div.innerHTML = s;
var tr = div.getElementsByTagName('tr');
// ...
}
Upvotes: 2