Use Node.cloneNode :
var container = document.getElementById('container'); var prototype = document.createElement('div'); prototype.innerHTML = "<p>Adding some <strong>arbitrary</strong> HTML in" +" here just to illustrate.</p> <p>Some <span>nesting</span> too.</p>" +"<p>CloneNode doesn't care how the initial nodes are created.</p>"; var prototype_copy = prototype.cloneNode(true); prototype_copy.id = 'whatever'; ); var container = document.getElementById('container'); var prototype = document.createElement('div'); prototype.innerHTML = "<p>Adding some <strong>arbitrary</strong> HTML in" +" here just to illustrate.</p> <p>Some <span>nesting</span> too.</p>" +"<p>CloneNode doesn't care how the initial nodes are created.</p>"; var prototype_copy = prototype.cloneNode(true); prototype_copy.id = 'whatever'; <strong> arbitrary </ strong> HTML in" var container = document.getElementById('container'); var prototype = document.createElement('div'); prototype.innerHTML = "<p>Adding some <strong>arbitrary</strong> HTML in" +" here just to illustrate.</p> <p>Some <span>nesting</span> too.</p>" +"<p>CloneNode doesn't care how the initial nodes are created.</p>"; var prototype_copy = prototype.cloneNode(true); prototype_copy.id = 'whatever'; care how the initial nodes are created </ p>."; var container = document.getElementById('container'); var prototype = document.createElement('div'); prototype.innerHTML = "<p>Adding some <strong>arbitrary</strong> HTML in" +" here just to illustrate.</p> <p>Some <span>nesting</span> too.</p>" +"<p>CloneNode doesn't care how the initial nodes are created.</p>"; var prototype_copy = prototype.cloneNode(true); prototype_copy.id = 'whatever';
Tips to speed
There are three operations that you want to minimize:
Parsing strings
This occurs when you use innerHTML . innerHTML is fast when you use it in isolation. This is often faster than the equivalent manual-DOM construct due to the overhead of all calls to the DOM methods. However, you want to leave innerHTML out of inner loops, and you do not want to use it to add. element.innerHTML += 'more html' , in particular, has a catastrophic behavior at runtime, since the element content is becoming more and more. It also destroys any event or data binding, because all of these components are destroyed and recreated.
Therefore, use innerHTML to create nodes "prototype" for convenience, but is used DOM-manipulation for internal cycles. To clone your prototypes using prototype.cloneNode(true) , which does not cause a parser. (Be careful with the id attribute in the cloned prototypes - you need to make sure that they are unique, when you add them to the document!)
Change the document tree (Recall appendChild )
Every time you change the document tree, you can call repaint the document window and update the relationship of a document DOM node, which can be slow. Instead, the package adds to the DocumentFragment and adds it to a DOM document only once.
Node search
If you already have a prototype of an object in memory and you want to change part of it, you will need to navigate through the DOM, to find and change these parts, regardless of whether you use bypass the DOM, getElement* or querySelector* .
Keep these searches is their internal cycles, while maintaining a link to the nodes that you want to change with the creation of a prototype. Then, whenever you want to clone an almost identical copy of the prototype, change the nodes you have links to, and then clone the modified prototype.
EXAMPLE template object
For this purpose, it is a basic (and probably fastest) template object illustrating the use cloneNode and cached reference node (which reduces the use of lines and parsing node).
Put it "prototype" node (or row) from the class names and attributes data-attr="slotname attributename" . Class names are "slots" to replace the text content; elements with data-attr are slots for installing / replacing the attribute name. Then you can give the object a method render() with the new values for the slots that you have defined, and you return the node clone made substitutions.
Example usage below.
function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); ) { function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); ]'); function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); e]; function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); -attr]'); function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); { function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); e, matches [ function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); ; function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); ); function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); ); function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df); val) { function Template(proto) { if (typeof proto === 'string') { this.proto = this.fromString(proto); } else { this.proto = proto.cloneNode(true); } this.slots = this.findSlots(this.proto); } Template.prototype.fromString = function(str) { var d = document.createDocumentFragment(); var temp = document.createElement('div'); temp.innerHTML = str; while (temp.firstChild) { d.appendChild(temp.firstChild); } return d; }; Template.prototype.findSlots = function(proto) { // textContent slots var slots = {}; var tokens = /^\s*(\w+)\s+(\w+)\s*$/; var classes = proto.querySelectorAll('[class]'); Array.prototype.forEach.call(classes, function(e) { var command = ['setText', e]; Array.prototype.forEach.call(e.classList, function(c) { slots[c] = command; }); }); var attributes = proto.querySelectorAll('[data-attr]'); Array.prototype.forEach.call(attributes, function(e) { var matches = e.getAttribute('data-attr').match(tokens); if (matches) { slots[matches[1]] = ['setAttr', e, matches[2]]; } e.removeAttribute('data-attr'); }); return slots; }; Template.prototype.render = function(data) { Object.getOwnPropertyNames(data).forEach(function(name) { var cmd = this.slots[name]; if (cmd) { this[cmd[0]].apply(this, cmd.slice(1).concat(data[name])); } }, this); return this.proto.cloneNode(true); }; Template.prototype.setText = (function() { var d = document.createElement('div'); var txtprop = (d.textContent === '') ? 'textContent' : 'innerText'; d = null; return function(elem, val) { elem[txtprop] = val; }; }()); Template.prototype.setAttr = function(elem, attrname, val) { elem.setAttribute(attrname, val); }; var tpl = new Template('<p data-attr="cloneid id">This is clone number <span class="clonenumber">one</span>!</p>'); var tpl_data = { cloneid: 0, clonenumber: 0 }; var df = document.createDocumentFragment(); for (var i = 0; i < 100; i++) { tpl_data.cloneid = 'id' + i; tpl_data.clonenumber = i; df.appendChild(tpl.render(tpl_data)); } document.body.appendChild(df);