HyperScript
A 16-line hack to make the JS DOM API a tad more humane.
|
…absolutely amazing. I’ve yet to find a smaller and yet more astounding example of how you can encapsulate functionality within JavaScript and create brand new APIs on the fly.
Ian Smith, 3 must read JavaScript articles
|
|
Web pages are written in HTML WP but as they have become more and more complex, they now tend to be written, clientside, through Javascript WP, which can manipulate and insert HTML. Google Images, for instance, uses Javascript to write the HTML that displays your image results.
Yes, it’s roundabout, but it’s due to the nature of the languages: Javascript does stuff, HTML displays stuff. When you want the browser to do things (instead of merely displaying dumbly what it receives) and when these things themselves involve a lot of displaying, you end up writing HTML through Javascript.
It’s a little like writing French through English (André went to Marie and said: “Bonjour! Ça va, ma chérie?”) and just as frustrating, particularly because you sometimes have to narrate whole scenes in French (pidgin tends to be painfully verbose) and your English self is left completely in the dark — so you end up naming things in both French and English and it gets as ugly as you can imagine.
HyperScript is a bizarre and quixotic attempt to write French in English; that is, HTML in Javascript. Basically, you do what went on in the Norman conquest of England WP: you anglicize as many French words as you can; that is, you turn into Javascript as many HTML words as you can.
The lark itself takes gratefully (and rather surpisingly) only 16 paltry lines of Javascript code (highlighting thanks to Mark “Tarquin” Wilton-Jones.):
function each(a, f)) { for(var i=0, l=a.length; i<l; i++)) f(a[i])) }; each('a big blockquote br b center code div em form h1 h2 h3 h4 h5 h6 hr img iframe input i li ol option pre p script select small span strong style sub sup table tbody td textarea tr ul u'.split(' ')), function(label){ window[label]=function(){ var tag=document.createElement(label)); each(arguments, function(arg){ if(arg.nodeType)) tag.appendChild(arg)); else if(typeof arg=='string' || typeof arg=='number')) tag.innerHTML+=arg; else for(var attr in arg){ if(attr=='style')) for(var sty in arg[attr])) tag[attr][sty]=arg[attr][sty]; else tag[attr]=arg[attr]; }; })); return tag; }; }));
Test Area:
does it work now?
The translation between HTML and Hyperscript is straightforward, where you would have written
you now write,
b(‘Hello World!’).
Instead of
<em style=“background-color:yellow”>Hello world!</em>,
now it’s,
em({style:{backgroundColor:’yellow’}},‘Hello World!’).
And so on.
HTML in a Javascript syntax. Enjoy!

16 Comments
Why not? They’ve been doing it with sexps for awhile.
That is really impressive. Is there a way to put it into a function i.e.
html.div(b(“hello”),br()br())Nice, but mochikit did it first. 18 lines is pretty impressive through.
@Grimboy: Yes, I didn’t know it but as the reddit comments show, this class of programs even has a name: a DomBuilder. It’s even about to make it into Prototype. Ah well, I didn’t know it, :), hope it at least has the merit of being short and barebones (as shortest and barebonest as it can be made?).
@Landlord Insurance: Ah! Do you mean being able to insert HyperScript with a clean function call? Something like this?<div><br>
<script><br>
html.b("hello")<br>
</script><br>
</div>
If so, it has been a constant nightmare of mine. I don’t know of even a remotely elegant way to do that. Anyone?
Uhm well, heres the same function but it works with nesting functions that was constantly nightmaring you…
Omg in less lines too! fapfap
Nice code RantingJerk! I didn’t even know about the forEach function. Unfortunately neither it, nor setAttribute, nor instanceof Element, work in IE, which is to be expected, since they’re all very elegant.
Anyway, I pruned the code a little, changed this[item] to windows[item], and dropped my abstruse one-letter variable names. Oddly, the result was 2 lines less.This might well be the place to point out belatedly that so far I’ve tested the code in Firefox 2, IE 7, Opera 9.1, and Safari 2.0.4 and everything went just fine. The only dialectal quirk so far is silly IE demanding explicit tbody tags inside tables.
This is very interesting, but what about a version that doesn’t use the evil innerHTML property?
innerHTML is only there as a just-in-case backdoor. Doing away with it would just be a matter of substituting
with
And nothing but the backdoor would be lost. :)
with HTML. If you want to do it this way you should but it in it’s own namespace,
for example: function html.b in stead of function b for boldface.
I use the following functions for creating html:
<notextile></notextile>
Damn too bad your forms are fucked up.. the code is really ugly now.
But it works like this<span class=”hyperscript-hyperscript” style=”white-space:pre”>xmlElement(“b”, false, [
xmlElement(“div”, {clazz:”someClass”})
]);
</div>
To create this:
<span class=”hyperscript-html”></div>
This looks very much like HAML, which is an alternative templating engine for ruby on rails,
of course, it’s evaluated on the server,
but nevertheless,
a similar concept and syntax.
http://haml.hamptoncatlin.com/
Agreed that this is a cool code. But my take is that markup and code must be separate. I would be really happy to see it the way ASP.Net handles this. Going fowrard XAML is also separating markup and code. Although it is completely possible to generate same output using code w/o markup. Generating markup from code is bad idea as it is very tough to manage changes.
@MatthewRudy: Ooh aah! Didn’t know about HAML—it’s pure beauty!
@Tjerk: You’re right, you may want to implement this inside its own safe namespace. As for your code, it’s practically equal to HyperScript modulo the syntactic sugar. Sorry for the crappy comment form, I reformatted your comment as I thought you meant to.@Hemant: I can imagine extreme edge cases where mixing markup and code could cause problems but people tend to be quite moralistic about it without giving concrete examples. How could it make it very tough to manage changes?
Why not use JSON as the notation form? So you could write something like:
If properly formatted (how do you format code in these comments, btw?) it’s very readable.
It’s what I use to create innerHTML or DOM nodes (whichever is more appropriate or faster in the browser)Using JSON is not a bad idea at all since it is indeed readable. Unfortunately, it is also a tad too verbose and, well, lacks flair, doesn’t it? ;)
Not to complexify your elegance here, but beware of gotchas. Creating elements via DOM does not work 100% of the time. Behold:
http://cf-bill.blogspot.com/2006/03/another-ie-gotcha-dynamiclly-created.htmlMSIE6 hates making useful radio buttons via DOM.
Nifty, but if I may be the devils advocate…
Javascript and HTML for the most part should be separate. For the same reasons we separate HTML and styles (CSS); To separate Form from Function.To use this in an include/purpose built routine may be appropriate, but to design an index page from ground up with it would be a (re)design nightmare.. and I wonder how it goes for SEO.
...food for thought. :)