jQuery Comments() Plug-in To Access HTML Comments For DOM Templating
I was reading over on James Padolsey's blog about accessing HTML comments via Javascript. Frankly, it never even occurred to me that you could do this. I am sure that years ago, pre-jQuery, I probably had to deal with looping over comment nodes (to ignore them); but, it's been so long since I've had to manually loop over DOM nodes, I forgot comment nodes were even there. But anyway, he had a really interesting idea about using comment nodes to store HTML templates.
In the past, I've experimenting with HTML templating methods using hidden elements and textarea values, but there's something quite elegant about using HTML comments for this purpose. Not only do comments free you of form elements (such as are required for Textarea-based templating), but because they are comments, their content is ignored by screen readers. Like I said, there's something really elegant about it.
In what James has coined, "JSHTML", he is manually traversing the DOM looking for comments. I wanted to take that basic concept and wrap it up in a jQuery plug-in. Rather than searching the entire document or a single context, however, I wanted to search within a specified jQuery collection (with the option for deep traversal). To me, this makes the most sense as I would most likely keep the comments confined to specific hidden DIVs with accessible ID values. This way, each DIV would hold one comment-based template. Sure, the DIV itself still "exists", but as it is empty, it won't be read by screen readers.
Here's the jQuery plug-in that I came up with: comments(). As each comment is collected, it is wrapped in a DIV element and that DIV element is given a REL attribute equal to its parent element's ID. I figured this would allow for excellent filtering of the collection base on template type (ex. .filter( "[ rel = 'note-template' ]" ).html()).
// This jQuery plugin will gather the comments within
// the current jQuery collection, returning all the
// comments in a new jQuery collection.
//
// NOTE: Comments are wrapped in DIV tags.
jQuery.fn.comments = function( blnDeep ){
var blnDeep = (blnDeep || false);
var jComments = $( [] );
// Loop over each node to search its children for
// comment nodes and element nodes (if deep search).
this.each(
function( intI, objNode ){
var objChildNode = objNode.firstChild;
var strParentID = $( this ).attr( "id" );
// Keep looping over the top-level children
// while we have a node to examine.
while (objChildNode){
// Check to see if this node is a comment.
if (objChildNode.nodeType === 8){
// We found a comment node. Add it to
// the nodes collection wrapped in a
// DIV (as we may have HTML).
jComments = jComments.add(
"<div rel='" + strParentID + "'>" +
objChildNode.nodeValue +
"</div>"
);
} else if (
blnDeep &&
(objChildNode.nodeType === 1)
) {
// Traverse this node deeply.
jComments = jComments.add(
$( objChildNode ).comments( true )
);
}
// Move to the next sibling.
objChildNode = objChildNode.nextSibling;
}
}
);
// Return the jQuery comments collection.
return( jComments );
}
To test this, I create a very small page that grabs a comment-based template and appends it to an UL element:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Untitled</title>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
<script type="text/javascript" src="jquery.comments.js"></script>
<script type="text/javascript">
$(
function(){
// Find all comments within the given collection.
var jComments = $( "#template" ).comments();
// Add the template text to list.
$( "#list" ).append( jComments.html() );
$( "#list" ).append( jComments.html() );
$( "#list" ).append( jComments.html() );
}
);
</script>
</head>
<body>
<!--- Here is a place-holder list. --->
<ul id="list" />
<div id="template" style="display: none ;">
<!--
<li>This is an LI template.</li>
-->
</div>
</body>
</html>
When we run this code, we get the following output:
* This is an LI template.
* This is an LI template.
* This is an LI template.
It works quite nicely. I think this comment plugin plus a modified Textarea template system would work quite nicely!
Damn that James Padolsey, he's so freakin smart!
Want to use code from this post? Check out the license.
Reader Comments
Nice work Ben - good idea
definitely great idea. about 2 years ago i created something similar to dreamweaver templating using comments and js. i should have it somewhere here...
Cool idea. I haven't thought of a need for it yet, but you could use this method for holding data on the page as well. You could put json in your comments, then do an eval(jComments.html()) to get the object.
@Anthony,
Using it to store JSON is an interesting idea. That could be very cool in the right situation.
I would think that over all, the jQuery.data() would be the best way to store something like JSON.
I'm still really liking the idea of having my html snippet templates in comments rather then a hidden element someplace.
Though maybe another thought, this would also be a great way for the server to leave JS some instructions on certain elements, without having to fudge hiding them in the attributes. I know I'm guilty of picking an obscure attribute on a tag, to tell JS that this is a ..whatever.. and including a short list of actions of what can be done with that object. But now, I call say anything I want to JS in the comments, and not have to worry about the classic "Oh shoot, I ran out of attributes, except the title attribute.. that's going to look really ugly if I put it in there.".
Thanks Ben.
Would it also be possible to use {{ and }} like James to select the comments that contain JS relevant html?
nice work man, I'm thinking about implementing this =)!
thank you,
LuK
this is pretty cool. the last piece to my puzzle is finding a good way to template the html. the catch is that is much conform to POSH html. This solution is 1 of 2 that i found. the other is located here : http://www.west-wind.com/Weblog/posts/509108.aspx
there is actually one more i found that uses <#= variableName #> which would probably work as well.
I'm just not sure this would work well. you see, if a template were used, it could still look correct in a html editor. the designers would have full control over the html and css. then, when the page is displayed, certain classes assigned to elements would trigger an ajax call, and the template would be updated with the returned ajax data.
this is to remove the need to server side scripting by a developer. all info is gathered by api calls to a REST server. the data returned, just needs to up shown to the user. however, the UI still needs to be functional if the user clicks on any elements that require functionality.
Got any solutions Ben?
@Vic,
I am not sure what POSH html is? Can you maybe explain that a bit more? I've seen, created, and tried various different templating systems and they all work reasonably well.
@LuK,
You can definitely use {{ }} notation. I'm using single bracket notation in this other post that uses textarea-based templating:
www.bennadel.com/blog/1393-Creating-jQuery-Templates-Plug-in-Using-Textarea-Elements-Thanks-Kurt-Bonnet-.htm
Same concept, just different implementation.
Thx 4 your answer, would I have to adjust something to make the script recognize the brackets?
i emailed this, but i figure i would post it here as well, in case anyone is interested.
hey Ben,
i just finished a function that will update a template dynamically.
ok so POSH html, is short for "Plain Old Semantic HTML".
* Validate your HTML
* Stop using tables for layout
* Use semantic elements and attributes for their intended purpose
* Use semantic class names and id values
* Use as little HTML as will get the job done
so it completes separates presentation from functionality. all styles are done through css. as usual. The example shown on
http://www.west-wind.com/Weblog/ShowPost.aspx?id=509108 are pretty good. the first one especially, but thats personal choice. because it follows this POSH html idea. sometimes, some templating engines have control structures in them, like if statements and loops, kinda like how smarty has. well, i might as well just use PHP in it. so this totally separates the presentation from the business logic. theres lots of documentation about it as well.
so, the project i am working on now, the deisgner, can work on the html for the template part, the and and js can always udpate template, regardless of how its structured. I am now getting into API's for all my apps as well. So i have written a REST server that accepts REST requests, and sends back the data in json ( or any format requested ), so now, any developers just need to worry about the front end part. All the back end, db stuff for example is already done via API calls. So, the developer just needs to worry about presentation. thats aside from the POSH html stuff. I just wanted to give you a background on how I am developing this project and a few lined up. So, now, I just wrote a 3 line function that will update a template dynamically.
If you want to see what I am talking about, if you have time. check out :
http://victor.hostopia.com/posh/index.html
just look at the source. The js part would be in its own file, this is just a testing page. You will see a function called updateTemplate() that updates a template one field at a time, like how it does on that example site, but updateTemplateDynamically() updates one dynamically.
So anyways, i've been looking for a templating system, and i think i can use the first one from the example site, plus a few modictications and it should be alright.
man jquery is totally awesome. Thanks for your reply. I never talk to any programmers outside of my work place. its cool to talk with you. take care man.
@Vic,
Ahh, I gotcha. I like that abbreviation - POSH. It looks like you are on the right track with what you're doing. I'm always up for a good conversation, so if you get stuck or wanna talk it out, just let us know.
@LuK,
Right now, that script is set up to recognize one bracket instead of two. Do you need to use two nested? Or would one be sufficient?
=)...Doesn't really matter, one is also good, thx anyways for the script and also for your nice jQuery indept tutorial (found this yesterday)!! Very nice (I'm only half way through but it's a very good collection of jQuerys features!)
THIS IS GREAT SITE
You can also use
<script type="text/template">
Your template
</div>
This will be also ignored by browsers, and I think by screenreaders too. You can also easier access it.
@Oleg,
Yeah, that is a good technique and one that I have definitely started to use much more lately. It feels right and, there are no problems with ever having nested elements.
Hey Ben,
I would like to make one recommendation for your jQuery comments script. It is always a good idea to wrap your jQuery plugins in a closure. Simple wrap the code your have above with:
(function($) {
... your function here
})(jQuery);
This will help isolate the code from anything else in the DOM and helps when compressing files together.
Best,
James
@James,
Much agreed. I have started to implement that practice. I've also started to use Script tags to store this kind of information as well.
This is very useful. I've just found myself needing it just two days ago but don't know how to write jQuery for it.
Thanks a lot.
This is awesome! Thanks alot.
@Joe,
Glad you like this. You might want to take a look at my JTML (jQuery Template Markup Language) project which uses a script-based templating approach with easy-to-use tag implementations:
www.bennadel.com/blog/1908-The-jQuery-Template-Markup-Language-JTML-Project.htm
Cheers.
Can i use it for realtime update of comments list? I don't undestend how it may work, but i need it %(
@Bigbad,
I am not sure I understand what you are asking.
FYI: If you try to add markup containing comment nodes to a document, i.e. via innerHTML, IE8 will silently discard the comments.
@Wizzszz,
Good to know, thanks. I've grown to like Script tags as a way to keep template markup, which seem to play nicely with the browsers.
I really like the idea. I came across this requirement, when I was crawling a website. The HTML was a mess (full of nested tables, with inline style, and no id, no name, etc.). The only way to extract data was to use regular expressions, and finding some specific elements, one of which was HTML comments.
This code is fine. Thanks.
I know this is a little old, but it showed up pretty high on a Google search for reading comments in jquery. I made a few small changes, since I had td tags in my comments that were getting stripped away. I modified the parameters so that they could take a jquery object representing the new container, as opposed to always using a div:
I also changed the part where it adds to the jquery array so that it doesn't use text to render, as that was removing my td tags:
@All,
I know this is a really old post; but, I recently rewrote this query to allow the comment nodes to be search by text value or by "pseudo attribute" value:
www.bennadel.com/blog/2606-Querying-The-DOM-For-Comment-Nodes-Based-On-Value-And-Pseudo-Attributes.htm
I have some ideas on how I want to leverage this in AngularJS.