

Laconic: A Sane Method of Generating DOM Content in JavaScript - joestelmach
http://joestelmach.github.com/laconic/

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

[http://mg.to/2006/02/27/easy-dom-creation-for-jquery-and-
pro...](http://mg.to/2006/02/27/easy-dom-creation-for-jquery-and-prototype)

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.

~~~
joestelmach
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.

------
pault
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?

~~~
joestelmach
Thanks for the tip, but I've had much success using this method on teams of 5+
developers and a full-time designer.

~~~
pault
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.

------
mkmcdonald
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...](http://msdn.microsoft.com/en-
us/library/dd347148\(v=VS.85\).aspx)

~~~
Joeri
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 "]".

~~~
mkmcdonald
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.

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

~~~
joestelmach
Many would frown upon polluting the global name space with functions
corresponding to all known HTML tags.

~~~
tikhonj
If you end up wrapping your code in an immediately called lambda anyhow I'm
sure it would be fine.

~~~
mistercow
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.

~~~
WiseWeasel
I think 'a' and 'p' are the only ones among those you'd actually want. I could
probably live with with those limitations.

~~~
WiseWeasel
Now that I think about it, that might break a few sortFunction(a,b) {//do
stuff with a and b} type functions of mine...

~~~
mistercow
No, it won't break them; it will just make them confusing to read. The scope
of the function arguments would be more local, so they would hide the
declarations further up the scope chain.

What's more of a problem is if you have something like this:
<https://gist.github.com/2499913>

The problem is that the "var p" in the for loop will be hoisted to the top of
the scope, and so when you call p() in the first line, it will actually be
undefined.

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

~~~
joestelmach
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'});

~~~
firefoxman1
That's awesome! Yeah I see it now in the source's comments. Thanks for
pointing that out; I'm sold.

------
DanielRibeiro
Even more laconic: <http://coffeekup.org/>

~~~
lucian1900
We use this for a mobile app and it's very nice. Haven't run into any issues,
at least so far.

------
robmueller
Similar approach to what we took.

[http://blog.fastmail.fm/2012/02/20/building-the-new-ajax-
mai...](http://blog.fastmail.fm/2012/02/20/building-the-new-ajax-mail-ui-
part-2-better-than-templates-building-highly-dynamic-web-pages/)

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.

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

------
peterhunt
I did something similar here:

[https://github.com/petehunt/htmldry/blob/master/demos/events...](https://github.com/petehunt/htmldry/blob/master/demos/eventsite/demo.coffee)

It's a little more HAML-inspired though.

------
maxbrunsfeld
Another very cool take on this idea: <https://github.com/nathansobo/space-pen>

------
strayerror
Similar library with namespace support (SVG, MathML):

<https://github.com/mal/watson>

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

------
anonymous_mouse
That looks ugly to me.

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

------
phleet
See also jquery-haml: <https://github.com/creationix/jquery-haml>

------
ars
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?

------
tzm
Clever DSL. Thanks for sharing.

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

~~~
joestelmach
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.

------
aneth
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?

~~~
joestelmach
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

~~~
aneth
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>

~~~
joestelmach
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.

------
DavidAbrams
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!

~~~
DavidAbrams
Oh well. Guess not.

