Reputation:
There are several libs for JavaScript templates (Mustache, underscore templates). What they do is to create new DOM nodes.
In my app I am trying to reuse DOM nodes to decrease memory consumption. An example is a thumbnail gallery with pagination. I reuse the same 50 nodes when i load the 50 next thumbnails.
I could easily render new nodes with a template lib, but is there some template libs that instead of creating new nodes, can reuse or update existing ones?
Upvotes: 0
Views: 423
Reputation: 6741
My solution is somehow like adeneo's but a little more generic. I use this in my projects, it uses only native DOM API, keep js and html totally separated.
<ul class="list">
<li class="item">
<label><span class="name">Field:</span><input name="" type="" value="" /></label>
</li>
</ul>
Note that, white spaces should be removed when send to client.
First generate a template based on the delivered HTML, it takes a root element, and some binding points as parameters.
var ul = document.querySelector('.list');
var tpl = new Tmpl(ul.querySelector('li'), [['.name', 'input@name'], 'input@type', 'input@value']);
Then use data to generate <li>
s.
var data = [['test1', 'text', 'test value'], ['test2', 'checkbox', 'true']]
for (var i = 0; i < data.length; i++)
ul.appendChild(tpl.generate(data[i]));
When need to update one instance, you can do it like:
tpl.apply(ul.querySelector('li'), ['test3', 'button', 'it\'s a button now']);
Now the list is generated.
The Tmpl class (minimized, removed other features, and works without any dependency) is pasted below:
function Tmpl(node, targets, singleton) {
this.elem = node;
this.begins = [];
this.targets = [];
this.fields = [];
this.mapping = [];
for (var i = 0; i < targets.length; i++) {
var tmp = targets[i] instanceof Array ? targets[i] : [targets[i]];
this.begins.push(this.targets.length);
for (var j = 0; j < tmp.length; j++) {
var field = this.parse(node, tmp[j]);
if (field) {
this.targets.push(tmp[j]);
this.fields.push(field);
this.mapping.push(i);
}
}
}
this.begins.push(this.targets.length);
node.parentNode.removeChild(node);
return this;
}
Tmpl.prototype = {
generate: function(data) {
for (var i = 0; i < this.fields.length; i++)
this.fields[i].nodeValue = data[this.mapping[i]];
return this.elem.cloneNode(true);
},
apply: function(node, data) {
for (var i = 0; i < this.fields.length; i++)
this.parse(node, this.targets[i]).nodeValue = data[this.mapping[i]];
return node;
},
applySingle: function(node, index, datum) {
for (var i = this.begins[index]; i < this.begins[index+1]; i++)
this.parse(node, this.targets[i]).nodeValue = datum;
return node;
},
parse: function(that, selector) {
selector = selector.split('@');
var node = selector[0] == '.' ? that : that.querySelector(selector[0]);
if (selector[1]) {
var attr = node.getAttributeNode(selector[1]);
if (!attr) {
attr = document.createAttribute(selector[1]);
node.setAttributeNode(attr);
}
node = attr;
}
if (node instanceof HTMLElement && node.nodeType != 2 && node.nodeType != 3)
node = node.firstChild && node.firstChild.nodeType == 3 ?
node.firstChild : node.appendChild(document.createTextNode(''));
return node;
}
};
Upvotes: 0
Reputation: 318182
In my experience the fastest way to create elements and do stuff with them is to create as few as possible, clone them when neccessary as that's faster, and use fragments to hold them and do as few changes to the DOM as possible, limiting reflows. I do a lot of this (simplified a lot) :
var div = document.createElement('div'),
anchor = document.createElement('a'),
span = document.createElement('span'),
frag = document.createDocumentFragment();
anchor.className = 'link';
span.style.height = '20px';
for (var i=0; i<something; i++) {
var wrapper = div.cloneNode(false),
link = anchor.cloneNode(true),
child = span.cloneNode(true);
wrapper.id = 'myWrapper-'+i;
wrapper.setAttribute('data-somevalue', 'myValue'+i);
child.appendChild(link);
wrapper.appendChild(child);
frag.appendChild(wrapper);
}
document.body.appendChild(frag);
In my experience this is as fast as it gets, and any templating library will just add a lot of overhead and inefficiency as it checks for this and that etc.
Upvotes: 1