Introducing: JSHTML

For me, unobtrusive JavaScript is only almost perfect. There’s one thing that has never stopped sucking; adding content to a document! It’s something so simple yet can take ages to get right. Doing it the “right way”, using DOM methods like document.createElement() and node.appendChild(), can take a long time and takes up a lot of space (code-wise). Abstractions like jQuery have helped but it’s still a messy process:

jQuery('<p id="random">Go to: <a href="page.html?id=23" class="eLink">Section 23</a>!</p>').appendTo('body');

It just looks ugly. This looks a little better:

({
    p : {
        id : 'random',
        content : [
            'Go to: ',
            a : {
                href : 'page.html?id=23',
                'class' : 'eLink',
                content : 'Section 23'
            },
            '!'
        ]
    }
})
// Of course, you would need to write a parser to make this work.

But it’s still a compromise! Just look how much space that takes up! Plus, it’s a little hard to understand at first – it would definitely take some getting used to.

A solution, JSHTML!

What I’m about to propose is no where near perfection but it provides something that you cannot get with either of the above methods: The ability to define an HTML structure exactly as you would normally… within the HTML document itself.

Before you get all holy on me about how obtrusive you think this is let me just go over a couple of points.

The paradigm we’ve all come to know and love as “unobtrusive JavaScript” is still important to me. But I think, when dealing with HTML, it should all be in one place. You shouldn’t have to go through a bunch of JavaScript code just to change one property of an element that will subsequently be injected into the DOM. Wouldn’t it be better to define those “enhanced elements” (JavaScript dependent elements) in place, where they’re needed?

What if you could define your JavaScript-dependent HTML structures right alongside the vanilla HTML?

<div id="content">
    <!-- JSHTML {{
        <ul id="controls">
            <li><a href="#add">Add content</a></li>
            <li><a href="#print">Print this</a></li>
        </ul>
    }} -->
    <p>Lorem ipsum dolor...</p>
</div>

Thanks to the fact that HTML comments are part of the DOM (they count as DOM nodes), the above example is entirely possible. ‘JSHTML’ does exactly this, here’s how it works:

  1. Iterate through ALL nodes within the document.
  2. Check that the current node is a comment (nodeType = 8).
  3. Extract intended HTML (between the specified flag and delimiters)
  4. Use the browser’s HTML parsing capabailities to create a node collection from the HTML string.
  5. Create a document fragment (like a mini-document object) and append all nodes to it.
  6. Replace the comment node with the fragment node.

The script itself is quite small (~1.5k). You can download it here or view it here.

You can configure the delimiters yourself (by default they’re set to {{ and }}), plus there’s the option to provide a fragmentCallback which is fired whenever a new fragment is created; this can be useful for debugging and if/when you need to process the nodes before they’re injected. To initiate JSHTML, call its parse method:

JSHTML.parse();
 
// Only call this once the DOM has loaded!
//  - Either put it at the bottom of the document (above </body>)
//  - Or, use a bespoke 'ready' event like jQuery's:
$(document).ready(function(){
    JSHTML.parse();
});

If you’re not a fan of delimiters then you can remove them (JSHTML.config.delimiters = ['','']) – in this situation, the JSHTML would be specified like so: (notice, no curly braces!)

<!-- JSHTML
    <strong><em>Normal</em> HTML goes here...</strong>
-->

Or… you could remove the delimiters and the flag (‘JSHTML’) – every comment within the document would be parsed as HTML.

You can visit the DEMO PAGE to see it in action, “view source” to see what’s going on…

JSHTML has been tested in all modern browsers, including IE6. If you’re using a strange or old browser (or any platform other than XP) I would appreciate confirmation that it works. Also, if you notice any bugs please let me know.

Thanks for reading!