DOMly - The fast template system that clones
Flashback: The state of innerHTML
performance in 2008
"The most obvious conclusion of these tests is that innerHTML is faster than "real" W3C DOM methods in all browsers."
Newsflash: innerHTML
is slow.
The time-honored best-practice is no longer best-practice. While you weren't looking, JavaScript engines have doubled in performance multiple times, numerous optimizations have been made, and it's no longer the case that innerHTML
is the fastest way to get elements into the DOM.
So why are we still using innerHTML
based templates?
Because the tooling doesn't exist.
We haven't seen a major release of a full-featured, production ready template system that compiles to DOM nodes. transparency and domjs aren't really template systems, pure is magical to the point of confusion, DOMBars is deprecated, and HTMLBars is still in the works.
Enter DOMly
DOMly, named after Dolly, the first mammal to be cloned, is a template system that takes advantage createElement
and cloneNode
.
DOMly's goal is to be approachable, fast, and lightweight.
Fast. Very fast.
With 7x the performance of doT and Handlebars and 2x the performance of HTMLBars, DOMly is arguably the fastest client-side templating system in existence. Check out the benchmarks.
Furthermore, if you use the handle="someName"
feature, you'll end up with references to elements you'll need to mutate in the future -- no querySelector
required.
Zero client-side dependencies
DOMly has no runtime library and no dependencies. Compiled templates are simply JavaScript functions that create DOM objects with native methods.
Pre-compile your templates as part of your build process, and when you render them in the browser, you'll get a Node or DocumentFragment that you can appendChild()
anywhere in the DOM.
Pretty syntax
The syntax is a combination of HTML for markup and control flow, dot notation for property references, ()
for invocation, and Mustache-style {{blocks}}
for safe variable substitution:
<h1>{{data.header}}</h1>
<if data.items.length>
<ul>
<foreach data.items,index>
<li if-data.current='class="current"'>
<if data.url>
<a href="{{data.url}}">{{index}}. {{data.name}}</a>
<else>
{{index}}. {{data.name}}
</if>
</li>
</foreach>
</ul>
<else>
<p>There are no items yet.</p>
</if>
Powerful (enough)
DOMly supports conditionals, loops, helpers, partials, and even raw JavaScript.
Partials
<partial MyApp.Templates.myPartial(data)></partial>
Block helpers
<helper MyApp.Helpers.myHelper(data)>
This string is evaluated {{data.forSubstitutions}} and passed as the last argument.
</helper>
Class methods and data
<if this.isFeatured(data.id,this.featured)>
This item is featured!
</if>
Object iteration with named iterator
<forin data.object,prop>
{{prop}}: {{data}}
</forin>
Raw JavaScript
<js>
data.count += 1;
</js>
Helpers & sub-expressions
{{captalize(reverse(data.name))}}
Limitations
No room to grow syntactically
Because DOMly templates are parsable HTML, you can't use /
, =
, or spaces in statements.
Must be be compiled on the server
Although it would be possible to compile on the client, you shouldn't be doing that anyway.
Doesn't support arbitrary expressions
You won't be able to write {{data.count+1}}
anywhere.
DOMly isn't yet battle tested
DOMly is the newest language on the block. Although it's unit tested and benchmarks, it's not battle-tested and can't be guaranteed to be bug-free.
However, in 2014, we'll see a number of template languages switch to pure DOM methods for a performance boost, and DOMly will be there to challenge them to be the best and fastest that they can be.
That's great, but why not base it off an existing template system?
1. It's an experiment
DOMly didn't set out to replace heavy hitters like Handlebars. DOMly started as an experiment to establish the performance gains of using DOM methods for templating, the feasibility of maintaining a set of these templates, and the actual amount of code required to do this from scratch.
The goal was to validate the hypothesis:
It's both possible and simple to build a template language that takes advantage of the performance gains of native DOM methods.
2. Simplicity
Existing template systems are quite complex. From advanced Jison parser tricks to dozens of regular expressions so complex that they've taken on a life of their own, to complex feature sets, these projects aren't very approachable. To re-tool an existing language would be a massive time sink just to validate a hypothesis.
Many existing template systems require runtimes that handle helpers and partials. This results in an additional call on the stack for method invocation which can affect performance. They're tiny, but the size doesn't help either gzipped, Handlebar's runtime is 4kB, Underscore is 5kB.
3. Syntax
After years of staring at PHP and JSPs, I think we could do better than this:
<h1><%- obj.header %></h1>
<% if(obj.hasItems) { %>
<ul>
<% _.each(obj.items, function(item) { %>
<% if(item.current) { %>
<li><strong><%- item.name %></strong></li>
<% } else { %>
<li><a href="<%- item.url %>"><%- item.name %></a></li>
<% } %>
<% }); %>
</ul>
<% } else { %>
<p>The list is empty.</p>
<% } %>
Your turn. How does this make you feel?
Does the mix of markup and template code bug you, or is the best thing since sliced bread?
See the source on Github and the benchmarks on jsPerf.