Thursday, January 21st, 2010

Jaml: An HTML builder a la Haml

Category: HTML, JavaScript

<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:

javascript
< view plain text >
  1. div(
  2.   h1("Some title"),
  3.   p("Some exciting paragraph text"),
  4.  
  5.   br(),
  6.  
  7.   ul(
  8.     li("First item"),
  9.     li("Second item"),
  10.     li("Third item")
  11.   )
  12. );

You can also use templates like this:

javascript
< view plain text >
  1. Jaml.register('product', function(product) {
  2.   div({cls: 'product'},
  3.     h1(product.title),
  4.  
  5.     p(product.description),
  6.  
  7.     img({src: product.thumbUrl}),
  8.     a({href: product.imageUrl}, 'View larger image'),
  9.  
  10.     form(
  11.       label({'for': 'quantity'}, "Quantity"),
  12.       input({type: 'text', name: 'quantity', id: 'quantity', value: 1}),
  13.  
  14.       input({type: 'submit', value: 'Add to Cart'})
  15.     )
  16.   );
  17. });

Check it all out on his Github repo.

Posted by Dion Almaer at 6:43 am
26 Comments

+++--
3.4 rating from 36 votes

26 Comments »

Comments feed TrackBack URI

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.

Comment by fabienmenager — January 21, 2010

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.

Comment by BenGerrissen — January 21, 2010

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.

Comment by BenGerrissen — January 21, 2010

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

Comment by PeteB — January 21, 2010

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.

Comment by EdSpencer — January 21, 2010

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)

Comment by FredrikB — January 21, 2010

Remember that the more function calls you have the slower your code will execute!

Comment by andriijas — January 21, 2010

@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.

Comment by sixtyseconds — January 21, 2010

@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.

Comment by tercero12 — January 21, 2010

Remember that the more function calls you have the slower your code will execute!

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.

Comment by eyelidlessness — January 21, 2010

@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).

Comment by FredrikB — January 21, 2010

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).

Comment by saroskar — January 21, 2010

@FredrikB – Fair point. I guess it’s just not a style that I’m used to. Bring on new Element('div', {...}); any day!

Comment by sixtyseconds — January 21, 2010

ActiveView has a similar syntax, without the use of eval (use of “with” is optional). I’m planning on releasing a standalone version soon.

Comment by syntacticx — January 21, 2010

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

Comment by medikoo — January 22, 2010

Very intresting, have we heard this idea before?

Comment by Aphrodisiac — January 22, 2010

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

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).

Comment by eyelidlessness — January 22, 2010

@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.

Comment by dperini — January 22, 2010

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/

Comment by enekoalonso — January 22, 2010

Reminds me of domplate…

Comment by sroussey — January 22, 2010

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.

Comment by quixote218 — January 23, 2010

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.

Comment by eyelidlessness — January 24, 2010

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;

Comment by reacher — January 25, 2010

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 :)

Comment by enekoalonso — February 2, 2010

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);

Comment by twfarland — July 9, 2010

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);
}
})
});

Comment by twfarland — July 9, 2010

Leave a comment

You must be logged in to post a comment.