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:
- Iterate through ALL nodes within the document.
- Check that the current node is a comment (nodeType = 8).
- Extract intended HTML (between the specified flag and delimiters)
- Use the browser’s HTML parsing capabailities to create a node collection from the HTML string.
- Create a document fragment (like a mini-document object) and append all nodes to it.
- 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!
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
That’s pretty interesting. I never thought about grabbing actual comment nodes out of the document. I like to follow a similar concept: putting the target HTML inside of a hidden TextArea element and then creating a new HTML element form the value:
$( templateTextArea.val() )
The downside to this is you need to have a FORM element to have the textarea. JSHTML would remove this restriction.
Cool idea!
I would think this would be helpful in circumstances where you would like utilize some server-side languages in the JavaScript-dependent markup. That would be a bit harder to do if the code was being appended rather than directly in the markup.
I wonder what search bots would make of all this? Do they completely ignore commented code? Might be an interesting way to completely hide things like JavaScript-dependent navigation or “flair” features.
Very interesting, but does this really have any use? After all the HTML has to already be there in order for it to be parsed, might as well remove the comment and just display the HTML…
Btw it works on Vista.
James,
Great post, and for a good reason. I’ve been doing something like this for a while, using a custom syntax, but it has made my HTML written through javaScript much more readable and easy to write (perfect for creating new nodes after an AJAX call).
Here is the implementation I use:
Notice how I can attach events, change css, and work with the actual element inline, even before it is placed in the DOM.
All I would have to do is this:
Here is the actual $.make() function:
P.S. Dear lord, you need a preview comment.
@Ben Nadel, Thanks! That’s an interesting idea with the textarea, I assume you hide it with CSS? One issue: People using screen readers might get a little confused. The great thing about comments is they’re completely ignored by everything! …other than the DOM API 😉
@Chris Coyier, Yes definitely! I think the main advantage with using this approach is that you can generate the content “in place”, where it’s needed in the document… I’m not sure about search engines, good point! I’d imagine that it’s completely ignored, otherwise people would’ve harnessed this technique for “cheating” the search engines.. (blackhat SEO etc.)
@Evan Byrne, I should’ve posted an example of where you might use this. If an element is JavaScript-dependent you don’t want to just stick it in there; users without JavaScript (or with JavaScript disabled) won’t be able to use it, it’ll just confuse them. The point in ‘JSHTML’ is to simplify the process of adding HTML (via JavaScript) to documents. For example, the colour-switcher in the header of this website – the markup needed for the “customize” box is generated in the JavaScript. With JSHTML I could’ve just placed it where it’s needed and it would be generated automatically. It’s not really necessary but just makes the whole process a lot easier. Essentially, JSHTML makes it easier to make a site degradable. It doesn’t require you to know JavaScript + DOM API in order to add new JS-dependent content…
@Robert Samuel Clay, that’s a pretty nice implementation there! I’m not quite sure why you’re riding on top of jQuery though? Your plugin seems quite self-sufficient.
@James,
Good point re: screen readers. Perhaps you can get the best of both worlds by creating a div with a given ID and then putting the comment in it. That way, you can directly address the div and get the first child rather than walking over the entire DOM.
@James, I would have to see it in code to really get it, I’m a very visual learner… Shouldn’t stuff like this be done server-side anyways? Pretty easy in PHP:
Sorry for that, but I am still not getting the point. In what real life situation would I use that. Like Evan Byrne said, why don’t just display the html and forget about that comment stuff? Can you give an example or a demo?
@Evan Byrne, @Mexx:
It’s a pain, but as web developers/designers we are strongly obliged to cater to ALL, all browsers and all users. Some users have JavaScript disabled, normally for security reasons. It’s a small percentage, yes, but those users are still important and so it’s important that we make our sites usable to them.
Becuase of these users we cannot rely on JavaScript always being available. When it isn’t, the user should still get a pleasant experience – most, if not all, of your website’s functionality should be operable.
When you create an Ajax contact form, for example, it’s always a good practice to make it functional without JavaScript. A web developer would test this by purposefully disabling JavaScript in their browser and then using the form to see if it works. If it does work then the developer can safely say that it’s a “degradable” enhancement. The “enhancement” is the Ajax’y bit – when JavaScript is not available this “enhancement” will degrade back to the “without-JavaScript” version. All users, JavaScript available or not, can use this form, it’s entirely usable!
When you enhance a website using JavaScript sometimes it’s necessary to add new HTML elements to the page. With our contact-form example, you might want to insert a notification at the top of the form when an error occurs, to notify the user what they’ve forgotten to fill in. It’s not safe to put these new elements there in the HTML beforehand (when you create the HTML document) because the users WITHOUT JavaScript will see these elements, but they won’t be able to interact with them because JavaScript isn’t available – this creates a usability issue with your site.
So, the solution is to add elements via JavaScript and the DOM API – unfortunately this is a bit of a pain. For example, let’s say I wanted to insert an anchor at the end of the
div#content
, using pure JS+DOM code this is how it would be achieved:It’s quite a struggle and the code is rather long! Libraries like jQuery simplify this process:
It’s much easier to do now, but it’s still not perfect.
So, we’ve established two things:
– Users without JavaScript available should not be able to see elements which are dependent on JavaScript. These elements may have event handlers attached to them to induce behaviour or interaction – these behaviours are not available without JavaScript so users should not see those elements unless JavaScript IS available.
– Using JavaScript to add elements via the DOM API is a pain, it’s long-winded and is normally a hastle. It requires you to locate the intended position of your insertion and then to construct the new HTML in a complicated way.
—
JSHTML makes it easier, for the developer, to add JavaScript-dependent HTML. No long-winded JavaScript or jQuery (or any other library) is required – you can place the markup as you normally would, within the HTML, but you have to wrap it in an HTML comment with a specific flag so that the JSHTML script can find the comment and parse the HTML in place.
Have a look at the demo: https://j11y.io/demos/JSHTML/
If you disable JavaScript in your browser you’ll notice that one of the paragraphs is missing, and then when you re-enable JavaScript the paragraph appears again. If you have a look at the source that paragraph is marked-up WITHIN an HTML comment, flagged with ‘JSHTML’ – the script finds this comment and parses whatever is inside it. This happens on page load and it’s a very quick process, the user wouldn’t notice it.
@James, but in the example of the web form you would still have to somehow remove the regular form that the non-javascript users would see. That could be done rather easily with jQuery, but then again you can make a regular form AJAX powered pretty easily with jQuery too without editing your HTML markup.
@Evan Byrne, ok, the contact form wasn’t the best example.
The users with JavaScript available see the same content as those without JavaScript (the form is not replaced; it’s enhanced), it’s just that certain aspects are enhanced – when you press the ‘send’ button the page doesn’t refresh – the form is sent via Ajax. Those without JavaScript receive the page without the enhancements when they press the button the page will refresh – or they’ll be navigated to a confirmation page.
I talked about the colour-switcher (at the top-right of my site) in an earlier comment. The stucture of that switcher is like so:
(note: ignore the “rel=nofollow” stuff, WordPress adds that to comments)
I could put this HTML in the original HTML file, but then the users WITHOUT JavaScript will see it – they can’t interact with it because JavaScript is disabled. I don’t want them to see it.
The correct way to add this HTML to the document is by using JavaScript. As discussed in the previous comment, there are a few different ways of doing this, JSHTML is one of them.
@James, now that makes more sense.
James, I really like this comment-access idea. I took it and tried to wrap it up in a jQuery plugin:
http://www.bennadel.com/index.cfm?dax=blog:1563.view
Anyway, just wanted to say I really liked it.
@Evan Byrne, Cool 🙂 … I’m glad I could help.
@Ben Nadel, Awesome work there! I like the fact that it has a ‘deep’ option too.
It would be cool if this could have a built-in templating engine? Probably something like:
I was using the latest Firefox version and had the JS blocker disabled for this. It worked. saw nothing odd.
@Lim Chee Aun, Excellent idea! I just added a very basic templating “system” (v.1.1). to set it up, simply add properties to
JSHTML.config.template
object:It’s very primitive at the moment, it doesn’t support directives or anything advanced but maybe it will in the future…
Interesting, i usually set a div display:none with the template code within it and then re-use the code via innerHTML – but this method seems nicer.
You’re still missing a couple of essential things – namely some kind of “id” on the comment tag so you can have multiple templates on the same page at once, and maybe a create() method for creating templates (useful for JSON interactivity).
Will you be porting this idea to Mootools?
Can i help? 🙂
Regards,
Carlos
I don’t understand this solution – why?
If you want to display content which are hidden and then show it, why not use CSS display: none respectively block.
Or am I missing something?
The most useful to time to generate DOM Nodes via JavaScript is when you’re presenting data that’s dynamic, say from an Ajax request. A good portion of the time this data is some sort of collection; the main draw-back I see with your code in it’s inability to handle collections of data. A common example that I find myself doing all the time in different projects is creating an unordered list, iterating of a collection of data, and creating a list item node for each item in the collection. Where I feel you’re correct about your argument that writing this using the standard DOM API would be overly verbose, especially if you had nested DOM elements structuring your data; how could you leverage JSHTML in this [extremely] common case?
Joe, this is not just a display:none element.
This is actually a html code template to use within javascript.
As a template the code can be re-used to inject anywhere else on the DOM when needed, and changing it anyway required.
Using the display:none method for templating causes unnecessary DOM elements, possible incorrect html code (because of the template {keys}), possible duplicate ids, etc, etc.
The method James describes here is non-intrusive in your html DOM and serves it’s purpose perfectly – a html template for javascript, not actual html code.
Regards,
Carlos
@Trouts, Thanks for the advice – I’ll try to make the template aspect of it more complete for the next version. What other features would you suggest? (other than
{keys}
…)@Joe, It’s just basic progressive enhancement. Not everyone has CSS enabled, those that have neither CSS not JS enabled will see the content. Also, AFAIK, screen readers don’t recognize ‘display:none;’…
@Eric Ferraiuolo, JSHTML is not meant to be used as a runtime solution – it’s only useful for parsing already-markup-up code within the document. Essentially it’s an alternative to what Joe suggested – but with accessibility taken into account + template capabilities (these are still in development though).
@James Hmm. Maybe YESSCRIPT would be a better name for this project then (as the post’s image suggests).
@James i believe the main necessary stuff for a template system would be:
– possibility of giving an id to each comment JSHTML tag;
– re-usability: easily convert any JSHTML tag to DOM object (keeping the original JSHTML tag intact, and having the {keys} options within the conversion method);
– easily create a JSHTML tag from a source DOM object;
I know this is probably a little different from your original JSHTML concept, but i believe the template application of this method would be great.
If you go ahead with it let me know.
I’m more a Mootools user and i’d love to port JSHTML to Mootools if you’re not intending to.
Regards,
Carlos
@Robert Samuel Clay: You should look at Mochikit.DOM and probably my YUI port of the same for a cleaner (IMO, since it involves less typing) way to do the same.
Excellent Job James! I did something similar to this in MooTools last year. The biggest difference in your implementation and mine (aside from using MooTools vs jQuery) is that I’m allowing for the hidden comment to contain a Hash object rather than just HTML content. This Hash object can then be used to extend the element that the comment is a direct child of. The advantage to this route is that you are not restricted to only extending HTML markup. You can extend events, classes, styles, properties, attributes, text and html.
You can see it generally described and used here:
http://www.tobymiller.com/articles/mootools_enhanced_elements/index.php
Maybe you could adopt some of these additional features into your solution. My primary need for this is to support web page features that need to work regardless of the presence of JavaScript (i.e. screen readers, search engines, locked down corporate environments, etc). You can really do a lot of cool things with this methodology.
Nice work, but in principle something like this has been around for a long time. I think the Trimpath template is a similar concept, no?
If my memory serves me right, in the case of Trimpath the templates are tucked away in hidden textareas and not in comments.
http://code.google.com/p/trimpath/wiki/JavaScriptTemplates
..and Domplate used in/by firebug i think
http://www.christophdorn.com/Blog/2008/09/07/domplate-debug-logging/
This is interesting, but I don’t find it particularly useful in its current state, it just seems really limited in what it can do. I am still leaning towards Resig’s “micro-templating” approach, as demonstrated here:
http://ejohn.org/blog/javascript-micro-templating/
One of the most used techniques is putting a script tag first into body which adds the class “jsEnabled” to the body tag and use CSS to hide/show those elements which should only be shown if JavaScript is enabled.
About your example: this is one of the reasons people choose to use the appendDom-Plugin – but it is far from being more readable. Yet, if you want readability, just use multiple strings, like in:
Sorry for the brackets, but the formatter seems to be erroneous; please replace them with HTML code. Anyway: Marvellous how the code is readable now, isn’t it? And you don’t have to waste as much time parsing the whole page for comments and DOM rebuilding.
Greetings, LX
This is a great feature you’re introducing here (to me anyway :-).
But I was wondering if it’s possible to create new HTML comments in the DOM, and parse them afterwards with JSHTML. I was trying to generate comments with jquery with the ‘after’ method and perform the parsing, but with no result:
JSHTML.config.delimiters = ['',''];
$('#foo').after('<!-- JSHTML bar -->');
JSHTML.parse();
Found a solution (should have more confidence in myself :-). If you use the createComment() method, everything works just fine!
x = document.createComment('JSHTML test');
document.body.appendChild(x);
JSHTML.parse();
Think of the CSS implications of this. Properties that do not validate or are not consistent between browsers could be expressed in this way. Examples would include border-radius and opacity.
I think this has potential. Keep up the good work James :D.
If you do take this further with the templating idea – Smarty for php might be a good idea to take a look at. It’s a fairly well done (imo) template library for php, some features might be adoptable from it.