Missing feature of the day: grouping DOM nodes
Let's say that you’re developing a JS Framework that has the concept of widgets. A widget is a JavaScript Object with a representation in the DOM. As you have one reference on the JS Object somewhere in your application, you’ll want to have one reference to its DOM structure, and unfortunately the only way is to wrap the template into one parent DOM element. For example, one of your widgets has the following template:
When converting this to dom nodes (how you do it doesn't matter here) you may have a list of dom elements, one p tag, and one ul tag, or have a parent dom element wrapping them. Let’s call these two items a group. But when this group gets appended to the dom, you lose the notion of group, as they become two nodes among the others. To illustrate this, let’s imagine that after being attached (how you do it doesn't matter here) we have the following structure:
As the notion of group is lost, there is no way now to distinguish them from the dom nodes of another widget. We can’t take them out and append them somewhere else. This problem is solved by wrapping them into a parent dom element, which creates the notion of groups. Usually a div is chosen. But the drawback here is that we have added an unnecessary dom element that could have been avoided. And unnecessary dom elements are one major cause of performance loss. One way of solving this issue would have been with using a document fragment. A documentFragment looks like a dom element though it can't have a representation in the actual dom. Unfortunately, once appended to the dom, it loses its children. // this is our dom element that doesn't exist in the DOM var domFrag = document.createDocumentFragment(); // We add it actual dom elements, they're part of our widget domFrag.appendChild(domElement1); domFrag.appendChild(domElement2); // It has two children domFrag.childNodes.length == 2; // true // We append our non-dom element somewhere into the DOM destinationDom.appendChild(domFrag); // Our documentFragment still exists, but has no more children // Of course, it doesn't exist in the DOM, though its child now do. domFrag.childNodes.length == 0; // true Another way of doing it would have been to have a grouping method in the DOM. // A real dom element without tag var domGroup = document.createDocumentGroup(); // Let's call its innerHTML, it's fast and cross browser. domGroup.innerHTML = "
"; // That's two items domGroup.childNodes.length == 2; // true // They get appended to destinationDom, no extra HTML tag added. destinationDom.appendChild(domGroup); // They're still part of the domGroup. domGroup.childNodes.length == 2; // true // So I can move them around otherDestinationDom.appendChild(domGroup) The domGroup would not have a representation in the dom, so its children could be direct children of the destinationDom, but can still be moved around by appending the group somewhere else. If ever a childNode gets appended somewhere else, it would simply leave the group. You may also have noticed that a documentFragment doesn't have an innerHTML property, which is a shame as it's a cool way for creating dom nodes out of strings, and it's fast because it's using the browser's rendering engine. Fortunately, the domGroup would have this feature, which would become the number one tool for defining a widget's view: var widget = { this.element: document.createDocumentGroup(), this.render: function (template) { if (template instanceof HTMLElement) { this.element.appendChild(template); } else if (typeof template == "string") { this.element.innerHTML = template; } else { throw new Error('template must be either a string a set of dom elements'); } }, this.place: function (destinationDom) { destinationDom.appendChild(this.element); } }; How cool would that be? Update 13/07/2013: Shadow DOM will solve this problem and add other cool features: http://www.w3.org/TR/shadow-dom/