Hacker News new | ask | show | jobs
Laconic: A Sane Method of Generating DOM Content in JavaScript (joestelmach.github.com)
55 points by joestelmach 5174 days ago
19 comments

Just for fun, here's my version from 2006:

http://mg.to/2006/02/27/easy-dom-creation-for-jquery-and-pro...

It's based on Bob Ippolito's version from 2005. :-)

The comments on the post have a few other interesting implementations that others contributed.

At the time I got pretty excited about this idea, but I abandoned it after a while for performance reasons. I was building large DOM structures and it was really slow. I knew that innerHTML was faster, so I tried keeping the same syntax and generating HTML from it instead of DOM insertions - and it was almost as slow that way!

Then I realized that it was just the sheer amount of JavaScript code being run that was slowing it down. I changed my code to build HTML strings with array pushes and a join at the end and then innerHTML to insert, and it was much faster.

Of course, JavaScript has gotten a lot faster since then (at least in new browsers!), so the more code-intensive techniques may be more practical now.

Thanks for sharing.

JavaScript has come a long way, even from 2006, yet most developers still have the mindset that this sort of thing can't perform well. What we're left with are solutions that are more complex, and perform similarly for most applications.

This is fine for small, one-person projects, but if you generate DOM content in your JS views it will come back to bite you in the ass once you hit a certain size. The first time you need to bring in a designer, or integrate a static mockup from a designer, you'll be wishing you used a template engine.

You wouldn't embed database queries into your view controllers, so why would you couple your html to your javascript?

Thanks for the tip, but I've had much success using this method on teams of 5+ developers and a full-time designer.
Obviously you should do whatever works for your team, and I'm not commenting on the quality or necessity of your framework. In my opinion, it's just much easier to edit well formatted markup, verbose as it may be, than wading through a forest of $el and appendTo and such. Not to mention, hiring a talented visual designer that knows html & css is much easier than finding one that knows their way around a client-side MVC framework. Unicorns and all that. To each his own.
Because database queries and views belong to different logical layers whilst JavaScript and HTML are just different technologies that may be used for things on the same logical layer.

But I think your point about designers is a valid one for many projects.

Your API will explode violently in IE < 8 || Quirks. This is because of gratuitous use of `setAttribute` along with `Array.prototype.slice.call` (dirty hack)[0]. IE < 8 || Quirks already treats HTML attributes and DOM properties identically[1], so really, DOM properties are preferred. More standards-compliant environments will reflect the property value in the corresponding attribute value (if possible).

Using `Object.prototype.toString.call` is also a pretty dirty hack, especially to detect an "array". I think `typeof obj === "object" && obj.length` would suffice, even if certain false positives like string objects leak through.

[0]: http://i.imgur.com/RNBYd.png

[1]: http://msdn.microsoft.com/en-us/library/dd347148(v=VS.85).as...

The "Object.prototype.toString.call" solution is guaranteed to work according to ECMA-262, and will only return true for a real Array instance (not something masquerading as an Array):

Object.prototype.toString ( ) When the toString method is called, the following steps are taken:

1. If the this value is undefined, return "[object Undefined]".

2. If the this value is null, return "[object Null]".

3. Let O be the result of calling ToObject passing the this value as the argument.

4. Let class be the value of the [[Class]] internal property of O.

5. Return the String value that is the result of concatenating the three Strings "[object ", class, and "]".

That may be, but it's poison for host objects. In older IE and Opera builds, `"[object Object]"` comes up often, even for host methods.

This is because there is no defined standard for `toString` results of host objects.

I simply generate global functions corresponding to tags, so my stuff looks like this:

  div(span('hello world')).inject(document.body);
Very simple to do and even simpler to use.
Many would frown upon polluting the global name space with functions corresponding to all known HTML tags.
If you end up wrapping your code in an immediately called lambda anyhow I'm sure it would be fine.
Sort of, but there are some tags you probably don't want to pollute your own top-level namespace with, like a, b, i, object, p, q, and s. There's at least one tag (var) which is not allowed as an identifier on its own.
I think 'a' and 'p' are the only ones among those you'd actually want. I could probably live with with those limitations.
Now that I think about it, that might break a few sortFunction(a,b) {//do stuff with a and b} type functions of mine...
You could use with(){} in js so you don't have to pollute your global namespace and still have pretty code.
Hardly seems worth it to invoke that confusing construct when you could just pack them in an object with a short name instead.
This is really neat. The coolest part is being able to register your own "tags" which take any arguments you want.

The problem I see is that this only works for "all known HTML tags." If you want to use custom tags, like <template> (I believe that's what meteor uses), this won't cut it since it uses $.el.tagname syntax, every tag has to be loaded into the $.el object.

How about a very similar, more jQuery-like syntax instead:

   $.el('template', [
     $.el('div', {'class' : 'foo'}, [
       //other children here
       //so basically, the last arg is always an array of children
     ]
   ]);
Though I did a poor job at documenting it, $.el is actually a function that accepts a tag name as the first parameter. The following two snippets are equivalent:

  $.el.div({className : 'foo'});

  $.el('div', {className : 'foo'});
That's awesome! Yeah I see it now in the source's comments. Thanks for pointing that out; I'm sold.
Even more laconic: http://coffeekup.org/
We use this for a mobile app and it's very nice. Haven't run into any issues, at least so far.
Similar approach to what we took.

http://blog.fastmail.fm/2012/02/20/building-the-new-ajax-mai...

Used the CSS syntax to easily set class/id on a newly created tag.

Benchmarks included to show it's just as fast as innerHTML on modern browsers.

It's really boring to write appendChild again and again.. Here's my idea(as a begginer), just converts json(in CoffeeScript) to html string. http://docview.cnodejs.net/projects/json2page/show.html?html
I did something similar here:

https://github.com/petehunt/htmldry/blob/master/demos/events...

It's a little more HAML-inspired though.

Another very cool take on this idea: https://github.com/nathansobo/space-pen
Similar library with namespace support (SVG, MathML):

https://github.com/mal/watson

Here's another one I did: https://github.com/insin/DOMBuilder/

It also allows you to generate HTML strings from the same code and create templates, with template inheritance, using the same sort of syntax.

That looks ugly to me.

I much prefer CoffeeScript, because plain JS makes me mad, and CoffeeKup (http://coffeekup.org/) for generating DOM content.

In what way is this better than writing HTML? And just using innerHTML, outerHTML etc?

It's a serious question. What benefit is there to doing this?

The only thing that came to mind is now you have the dom node so you can bind events to it. But you're not going to be doing that everywhere. So why make things more complicated?

Clever DSL. Thanks for sharing.
i don't understand the appeal of this when there's tons of client side js templating options (handlebars, jade, haml-js,...) that result in far more readable code than a $.el. prefixathon
While I agree the $.el prefix is annoying, I don't believe it has a negative effect on readability. For me, templates are just another level of unneccesary indirection.
This is pretty, but unfortunately the only performant way to generate dom elements in javascript is with HTML. HTML parsing is orders of magnitude faster in most browsers.

How about a library like this to generate a string of HTML instead of dom elements?

I tried that some years ago (see my other comment). I had a very similar DOM creation function and I updated it to generate HTML instead. It was almost as slow as before - and then I realized that the sheer amount of JS code I was running to generate the HTML was taking up the time.

Of course the performance tradeoff would be different in modern browsers.

While I don't entirely agree with your first statement, you could just grab the outerHTML instead of appending the element directly to the DOM:

  $.el.div($.el.span()).outerHTML
Seems like a reasonable solution.

My first statement was certainly true in the previous generation of browsers, including IE 7. I'm not sure about more modern browsers. What basis do you have for disagreeing?

Refer to:

http://www.quirksmode.org/dom/innerhtml.html

I'm only disagreeing with the use of the word 'performant', and the popular viewpoint that any solution that isn't as fast as possible can't be labeled as such.
I started one in 2009 (https://github.com/dburrows/markup-builder) inspired by Ruby's Builder library but abandoned it - it was only really useful for small amounts of code and for any sizeable project it's far better to use templates
Seems to me like an implementation detail that shouldn't have to affect the interface at all.
Like a template engine?
Perhaps like some template engine, but not like any I know of.
Does anyone have a recommendation for a not-too-bloated template engine? I'm going to be building a small-business Web site to display products from a MySQL database and possibly offer shopping-cart functionality.

Thanks!

Oh well. Guess not.