Thursday, January 21st, 2010
Jaml: An HTML builder a la Haml
<p>There have been a few HTML builder APIs out there, but Ed Spencer (new lead of Ext JS) has put together something that looks and feels similar to Haml from the Ruby side.Jaml lets you write HTML like this:
-
-
div(
-
h1("Some title"),
-
p("Some exciting paragraph text"),
-
-
br(),
-
-
ul(
-
li("First item"),
-
li("Second item"),
-
li("Third item")
-
)
-
);
-
You can also use templates like this:
-
-
Jaml.register('product', function(product) {
-
div({cls: 'product'},
-
h1(product.title),
-
-
p(product.description),
-
-
img({src: product.thumbUrl}),
-
a({href: product.imageUrl}, 'View larger image'),
-
-
form(
-
label({'for': 'quantity'}, "Quantity"),
-
input({type: 'text', name: 'quantity', id: 'quantity', value: 1}),
-
-
input({type: 'submit', value: 'Add to Cart'})
-
)
-
);
-
});
-
Check it all out on his Github repo.
Related Content:











This kind of DOM builder is quite comfortable, I made nearly the same some time ago : http://code.google.com/p/phenx-web/source/browse/trunk/controls/controls.js#34 but it depends on PrototypeJS.
I should check it out before saying this I know, perhaps they use some eval magic (which is scary as well). But can I assume the global scope is poluted with methods: br, div, li, img, a etc? If so, use with GREAT care.
It uses eval magic and looks great, a few notes:
.
-> Potential to create closures with DOM elements (var div = element; in global scope), dispose of tpl function after stringify-ing it, keep toString version as .tpl if desired.
-> stringify and process tpl function before evaluating it, that way you can check for potential eval abuse and throw an error if the tpl function is mallformed.
I had a go at something like this while back:
http://the-echoplex.net/log/dom-branching
It uses CSS selectors to generate html fragments
Actually it uses with ‘with’ magic, therefore not polluting the global scope at all. I had to use a single eval as well to make it work.
I totally acknowledge the stigma behind such methods and usually avoid them myself. That said, we shouldn’t just dismiss these methods out of hand – they’re only evil 99% of the time. In this case it was an interesting experiment in seeing how pretty the syntax could become.
MochiKit (inspired by Python’s Nevow) has had this for quite some time:
http://mochikit.com/doc/html/MochiKit/DOM.html
The coercion rules give powerful templating.
(also Google’s Closure lib includes the very similar DomHelper)
Remember that the more function calls you have the slower your code will execute!
@FredrikB – And their code is also fugly…
var rows = [
["dataA1", "dataA2", "dataA3"],
["dataB1", "dataB2", "dataB3"]
];
row_display = function (row) {
return TR(null, map(partial(TD, null), row));
}
var newTable = TABLE({'class': 'prettytable'},
THEAD(null,
row_display(["head1", "head2", "head3"])),
TFOOT(null,
row_display(["foot1", "foot2", "foot3"])),
TBODY(null,
map(row_display, rows)));
…or not.
@sixtyseconds And it hasn’t kept up with the times. A series of quick updates in 2008 and since that, no activity since 2006.
Actually at a former workplace, I migrated them off of Mochikit to YUI and have never regretted it.
Remember that the more code you execute, the slower your code will execute. Duh?
How is it any different from any other way of interacting with the DOM? Abstractions necessarily add a little overhead, but not necessarily much.
@sixtyseconds
You’re comparing different code!
This is how the Jaml example would look like in MochiKit.DOM:
DIV(null,
H1(“Some title”),
P(“Some exciting paragraph text”),
BR(),
UL(null,
LI(“First item”),
LI(“Second item”),
LI(“Third item”)
)
);
(you can choose to use MochiKit.DOM: namespace for all tags also)
Agree the initial MochiKit example could be intimidating at first look, but that’s mostly because it also shows of the functional programming techniques and how they coerce to DOM (like templates).
If you are worried about use of eval() or global namespace pollution take a look at PaJes. It is very similar in nature and (optionally) tackles global namespace pollution problem by using CommonJS module pattern (PaJes-CommonJS.js). It also has some additional goodies like functional looping (forEach) and conditionals(checkIf), painless inclusion of JavaScript source code in the HTML output and support for adding new childrens to HTML tags created somewhere else in the code (example 6 from the documentation).
@FredrikB – Fair point. I guess it’s just not a style that I’m used to. Bring on
new Element('div', {...});any day!ActiveView has a similar syntax, without the use of eval (use of “with” is optional). I’m planning on releasing a standalone version soon.
I’ve used exactly same idea 3 years ago. Anyway, sharing is very important step :)
Side note: I don’t use it anymore, as it’s not fast. I always build html as text and inject it via innerHTML it’s ten times faster and (in real world scenarios) difference is noticable
Very intresting, have we heard this idea before?
This really depends on a lot of details, most prominently which browser you’re running. Single-reflow DOM operations (that is, single element additions, or elements added to a hidden div which is then attached) can be faster in the faster engines (WebKit, Gecko).
@eyelidlessness,
single-reflow DOM operations are always the fastest, using DOM fragments and cloning elements is usually improving DOM operations a lot too.
In javascript “calling out” from main function body is one of the most expensive operations (because the scope/stack have to be saved and restored), so in nested loops it is a must to avoid that.
Creating “expandos” on elements causes reflow, at least in IE.
Using innerHTML in IE is not safe and not suggested even by less expert javascripters and it is maybe unsafe to do that also on other browsers.
In frameworks like ExtJS using something like this JAML “builder” make perfect sense, speed will still be very high if the simple boosting rules above are followed.
I love Jaml, the idea is great and it is very user friendly and very useful when building Javascript driven applications (specially single page web applications).
A few days ago I created Mooml, a Jaml port that takes advantage of Mootools functions and classes. Check it out if you work with Mootools:
http://github.com/eneko/mooml/
Reminds me of domplate…
Seems to me that using Javascript to create HTML templates is a little backwards. Javascript is probably best when it only manipulates existing HTML, with as little code as possible linking the structure of the HTML to the behavior of the manipulation. Anything else is endorsing the idea that your JS code must change when your HTML changes, and that the person doing the maintenance must be an expert at both. It’s a similar problem to putting style information in your JS — sometimes it’s the easiest way to just get it done, but the easiest foundation to lay down is rarely the best foundation for continued growth.
quixote218,
It really depends on the structure of your application. The point isn’t really to keep your languages separate, but to keep your logic domains separate. In some circumstances—an application that requires Javascript, perhaps intended for only certain user agents (eg Air) or devices (eg iPhone); server-side Javascript—it can make sense to do all of your document construction in Javascript. Likewise, it can make sense to do all of your styling in Javascript.
I personally find that idea appealing. You know, assuming that the application is intended for that sort of environment. I’d still keep my Javascript “stylesheets” separate from my Javascript “views” separate from my Javascript “controllers” separate from my Javascript “models” and so on. But in some cases there’s no reason not to do everything in one language. Context-switching between the languages can be awful.
Everything old is new again :D
use CGI ':standard';
print blockquote(
"Many years ago on the island of",
a({href=>"http://crete.org/"},"Crete"),
"there lived a minotaur named",
strong("Fred."),
),
hr;
I think it’s obvious these tools are not meant to replace HTML files or server generated HTML.
In the other hand, nowadays we have sites that are more and more dynamic, putting a lot of resources on the client side. And it happens that we need to generate HTML code from Javascript, from building small page blocks to full page structures, single page sites, etc.
How we generate that HTML from JS is the question, and the Jaml syntax is a very good and nice way to do it :)
I just found Jaml and I like it. For portability, it might also be a nice addition to be able to specify the template with ‘pure’ json like so:
var data = {
“id” : 123,
“link_text” : “My link”
}
x.register(‘myhtml’,function(data){
“div” : [
{"class" : "myclass", "id" : "mydiv-" + data.id},
{ "a" : [{"id" : "mylink-" + data.id }, data.link_text]}
]
})
x.render(‘myhtml’, data);
Another piece of feedback for Jaml:
It would be nice to be able to use anonymous functions within the template registration call. It would help to be able to do this:
Jaml.register(‘selector’, function(c){
select({‘name’:'name-’+c.id},
function(){
for (i in c.opts){
option({‘value’:c.opts[i].val}, c.opts[i].text);
}
})
});