Easy DOM creation for jQuery and Prototype

Michael Geary | Mon, 2006-02-27 11:07

Here is a jQuery plugin that makes it easy to build up a tree of DOM nodes. It lets you write code like this:

var table =
   $.TABLE({ Class:"MyTable" },
         $.TR({ Class:"MyTableRow" },
            $.TD({ Class:"MyTableCol1" }, 'howdy' ),
            $.TD({ Class:"MyTableCol2" },
               'Link: ',
               $.A({ Class:"MyLink", href:"https://www.example.com" },

Basically, each function such as $.TABLE creates a DOM node and takes the following arguments:

The first argument is an object that list any attributes to be set on the node. You can specify the className attribute in a few different ways depending on your taste:

  className: text
  Class: text
  'class': text

Any additional arguments after the first one represent child nodes to be created and appended. These arguments can be DOM elements themselves (e.g. inline $.FOO calls as above), or they can be numbers or strings which are converted to text nodes, or they can be arrays, in which case each element of the array is handled in this same way.

This interface is inspired by Bob Ippolito’s MochiKit DOM API, although it doesn’t implement all of the features of that one (yet).

The code predefines most of the common tags; you can add additional tags by calling:

$.defineTag( tagName )// e.g. $.defineTag( 'dd' );

Or simply add the tag names to the tags list in the code.

One last definition is $.NBSP which defines a non-breaking space (same as   in HTML).

$._createNode is an internal helper function used by the $.FOO functions. I would have hidden it away as a nested function, but I wanted to avoid any unnecessary closures.

This code doesn’t actually depend on any of the features of jQuery except for the presence of the $ function—and it uses $ only as a way to avoid cluttering the global namespace. I haven’t tested it with Prototype.js, but it should work equally well there. Or the code can be used with no library, by preceding it with:

var $ = {};

Here is the source code, or you can download it:

// DOM element creator for jQuery and Prototype by Michael Geary
// https://mg.to/topics/programming/javascript/jquery
// Inspired by MochiKit.DOM by Bob Ippolito
// Free beer and free speech. Enjoy!

$.defineTag = function( tag ) {
    $[tag.toUpperCase()] = function() {
        return $._createNode( tag, arguments );

(function() {
    var tags = [
        'a', 'br', 'button', 'canvas', 'div', 'fieldset', 'form',
        'h1', 'h2', 'h3', 'hr', 'img', 'input', 'label', 'legend',
        'li', 'ol', 'optgroup', 'option', 'p', 'pre', 'select',
        'span', 'strong', 'table', 'tbody', 'td', 'textarea',
        'tfoot', 'th', 'thead', 'tr', 'tt', 'ul' ];
    for( var i = tags.length - 1;  i >= 0;  i-- ) {
        $.defineTag( tags[i] );

$.NBSP = '\u00a0';

$._createNode = function( tag, args ) {
    var fix = { 'class':'className', 'Class':'className' };
    var e;
    try {
        var attrs = args[0] || {};
        e = document.createElement( tag );
        for( var attr in attrs ) {
            var a = fix[attr] || attr;
            e[a] = attrs[attr];
        for( var i = 1;  i < args.length;  i++ ) {
            var arg = args[i];
            if( arg == null ) continue;
            if( arg.constructor != Array ) append( arg );
            else for( var j = 0;  j < arg.length;  j++ )
                append( arg[j] );
    catch( ex ) {
        alert( 'Cannot create <' + tag + '> element:\n' +
            args.toSource() + '\n' + args );
        e = null;

    function append( arg ) {
        if( arg == null ) return;
        var c = arg.constructor;
        switch( typeof arg ) {
            case 'number': arg = '' + arg;  // fall through
            case 'string': arg = document.createTextNode( arg );
        e.appendChild( arg );

    return e;
jquery-dom.js1.56 KB
Submitted by Olle Jonsson (not verified) on Mon, 2006-02-27 14:59.

I read some funky Lisp articles this summer, and the goal of that article was to make some HTML output in a syntax that was a lot like this, and even more like Why the Lucky Stiff’s “Markaby”.

Go you, Mr Geary. This is most cool. I’ll keep my eyes peeled for more content at this space. Or rather, I’ll instruct my feed-reader to.

Submitted by Klaus Hartl (not verified) on Tue, 2006-04-25 02:57.

Hi Michael,

I wanted to report a problem with the plugin when one wants to create a label element with a for attribute. As this is also a keyword in JavaScript I had to change a bit:

1) Add that to the fix:

var fix = { 'class':'className', 'Class':'className', 'forAttr':'for' };

2) e[a] = attrs[attr]; does not work, so I had to change that to this (ugly, ugly):

if (a == "for") {
    e.setAttribute(a, attrs[attr]);
} else {
    e[a] = attrs[attr];

So now I can create my desired label:

$.LABEL({forAttr: "dyn-username"}, "Username")
Submitted by Visitor (not verified) on Tue, 2006-04-25 08:03.

The following syntax also works with no code fix:

$.LABEL({"for": "dyn-username"}, "Username")
Submitted by Klaus Hartl (not verified) on Wed, 2006-04-26 01:32.

Sorry, this does not work (at least in Firefox). The value of the attribute is null. Can you provide a working example?

Submitted by Gilles (not verified) on Fri, 2006-04-28 23:17.

In firefox (gecko) based browsers, you don’t need the “for” attribute, but the “htmlFor” attribute. Google on it, it is hard to find but i got it working.

Submitted by Michael Geary on Sat, 2006-04-29 15:22.

Thanks, guys, good catch. Gilles, even better, htmlFor isn’t just for Firefox, it is the standard. So what if we try this:

var fix = { 'class':'className', 'Class':'className', 'for':'htmlFor', 'For':'htmlFor' };

Or better yet, simplify it to this:

var fix = { 'class':'className', 'for':'htmlFor' };

and change the assignment that uses fix to:

var a = fix[attr.toLowerCase()] || attr;

I like For better than forAttr—it’s closer to what you’d write in HTML. Klaus, your example would read:

$.LABEL({For: "dyn-username"}, "Username")

Or you could use:

$.LABEL({'for': "dyn-username"}, "Username")

I have a feeling we won’t need the “ugly, ugly” :-) code that uses setAttribute. Because htmlFor is the standard DOM property, my guess is that it will work with the simple e[a] = assignment. Whoever tries it first, let us know if it works, OK? Thanks!

Submitted by Klaus Hartl (not verified) on Sun, 2006-04-30 02:33.

Good news, bad news. Thanks for the hint with htmlFor. In general it works, but in IE assigning the attribute via

e[a] = attrs[attr];

fails completely (The form doesn’t even render). I made the experience, that in IE some attributes must be assigned explicitly via

element.attribute = value;

So now I resorted to this:

eval("e." + a + "= attrs[attr]");

I don’t like eval, but it’s a little less ugly :-) Maybe there is another solution, but I haven’t got the time at the moment to test some more things.

The good news is, that the label element now works as expected in IE . Before I had to do some even more ugly stuff:

// Conditional Compiling - DO NOT REOMVE
// Fix labels in IE somehow not working
$("#np-login label").click(function() {
$("#" + this.getAttribute("for")).get(0).focus();

Thank you guys!

Submitted by Gilles (not verified) on Mon, 2006-05-01 12:32.

From another website: The reason element.class and label.for cannot be used is simply that ‘class’ and ‘for’ are reserved words in JavaScript, and thus they had to find different names for those. They chose className and htmlFor, both of which work consistently cross-browser.

So i decided to modify the script like this:

//e[a] = attrs[attr];
e.setAttribute(a, attrs[attr]);

Haven’t tested it yet, but should work even though it’s pretty messy.. After all we are looking for a cross-browser sollution.

Submitted by Gilles (not verified) on Mon, 2006-05-01 13:06.

On the other hand, why not use the default jQuery way to do it? It has built in support for the “fix” so that can be removed from the code.


        for(var attr in attrs) {
            var a=fix[attr] || attr;


        for(var attr in attrs)
Submitted by Klaus Hartl (not verified) on Wed, 2006-05-03 01:23.

Works fine! Thank you Gilles!

Submitted by Klaus Hartl (not verified) on Wed, 2006-04-26 01:36.

If you need to create simple text nodes, add the following snippet:

$.TEXT = function(s) {
    return document.createTextNode(s);

You can than easily create a single text node as well:

var tn = $.TEXT("text node")
Submitted by Kelvin Luck (not verified) on Sat, 2006-05-06 06:16.

Thanks for this cool script! I was just playing with it and found myself in a situation where I was creating lots of elements that I didn’t need to pass any attributes to… I was ending up with code like:

$.SPAN({}, 'S'), $.SPAN({className:'active'}, 'S'), $.SPAN({}, 'M'), $.SPAN({}, 'T'), $.SPAN({}, 'W'), $.SPAN({}, 'T'), $.SPAN({}, 'F')

So made a little modification to the script. I added this code in (the line before and the line after are included so you can see where it goes):

try {
    if(typeof(args[0]) == 'string') {
        var newArgs = [{}];
        for (i=0; i < args.length; i++) newArgs.push(args[i]);
        args = newArgs;
    var attrs = args[0] || {};

As you can see, if the first argument is a string it just prepends an empty object to the front of the arguments array. This isn’t as clean as it could be because the arguments array isn’t a real array and doesn’t support unshift() but the code seems to work and now I can simplify my code to:

$.SPAN('S'), $.SPAN({className:'active'}, 'S'), $.SPAN('M'), $.SPAN('T'), $.SPAN('W'), $.SPAN('T'), $.SPAN('F')

Not sure if you think this is a useful modification? Seems to work fine on IE / FF WinXP and I don’t think it will have too much of a speed impact in most cases…


Kelvin :)

Submitted by fdcn (not verified) on Sat, 2006-08-26 12:44.
try {
    if(typeof(args[0]) == 'string') {
        args = [{}].concat(args);
    var attrs = args[0] || {}
Submitted by coxy (not verified) on Thu, 2006-09-14 00:36.

Similarly, if the first argument is not an attributes object, but another dom element, e.g.

                $.TH( 'From' ),
                $.TH( 'To' ),
                $.TH( 'Date' )
        $.TBODY( {'id':'tbody'} )

You can do something like:

if(typeof(args[0]) == 'string' || $(args[0]).html() ) 

to test for it.

I’m new to jQuery so hopefully someone could improve on that….but it seems to be working ok for me.

Submitted by Demis Bellot (not verified) on Wed, 2006-10-04 09:38.

This is a fix that allows the attr ({}) to be optional without requiring the first arg to be a string.

        if (typeof(args[0]) == 'string' || args[0].nodeType == 1) {
            var newArgs = [{}];
            for (i=0; i < args.length; i++) newArgs.push(args[i]);
                args = newArgs;
Submitted by Olsow (not verified) on Thu, 2006-06-15 04:19.

I wrote another "Easy DOM creation" method. I think it lets you write more understandable markup, and there is no need for tag definitions in the code:

Here is the jQuery function:

$.create = function() {
   var ret = [], a = arguments, e, o, i=0, j, jq = $();
      a = a[0] instanceof Array ? a[0] : a;
      for (; i<a.length; i++) {
      if (a[i+1] instanceof Object) {
         e = ret[ret.length] = document.createElement(a[i]);
         for (j in a[++i]) { $.attr(e, j, a[i][j]); }
         if (a[i+1] instanceof Array) {
            o = $.create(a[++i]).cur;
            for (j=0; j<o.length; j++) { e.appendChild(o[j]); }
      } else { ret[ret.length] = document.createTextNode(a[i]); }
   jq.cur = ret;
   return jq;

And here is an example:

   'table', { 'class':"MyTable" }, [
      'tbody', {}, [
         'tr', { 'class':"MyTableRow" }, [
            'td', { 'class':"MyTableCol1" }, [ "howdy" ],
            'td', { 'class':"MyTableCol2" }, [
               "Link:", 'a', { 'class':"MyLink", 'href':"https://www.example.com" }, ["example.com"] ] ],
         'tr', { 'class':"MyTableRow2" }, [
            'td', { 'class':"MyTableCol1" }, [ "howdy" ],
            'td', { 'class':"MyTableCol2" }, [
               "Link:", 'a', { 'class':"MyLink", 'href':"https://www.example.com" }, ["example.com"] ] ] ] ]);

Method returns a jQuery object so you can easily manipulate it after creation. For example:

   'table', { 'class':"MyTable" }, [
      'tbody', {}, [
         'tr', {}, [
            'td', { 'class':"MyTableCol1" }, [ "howdy" ],
            'td', { 'class':"MyTableCol2" }, [
               "Link:", 'a', { 'class':"MyLink", 'href':"https://www.example.com" }, ["example.com"] ] ] ] ]

Also I’ve coded a usable JSON template method, which uses the method above:

$.tpl = function(json, tpl) {
   var ret = [], a, jq = $(), i=0, j;
   json = json instanceof Array ? json : [json];

   for (; i<json.length; i++) {
      a = $.create($.apply(json[i], tpl)).cur;
      for (j=0; j<a.length; j++) { ret[ret.length] = a[j]; }

   jq.cur = ret;
   return jq;

The power of this method:

<table id="fill-table" border="1">


var json = [
   {'name' : "John", 'surname' : "Smith"},
   {'name' : "Sarra", 'surname' : "Smith"}

$.tpl(json, function(){
   return [
      'tr', { 'class':"MyTableRow" }, [
         'td', { 'class':"MyTableCol1" }, [ this.name ],
         'td', { 'class':"MyTableCol2" }, [ this.surname ]]
}).appendTo($('#fill-table > tbody').get(0));
Submitted by DaveG (not verified) on Sun, 2006-06-18 08:05.

I think Olsows method is extremely powerful. The json based ‘template’ functionality is particularly neat. Olsow: If the content of the JSON data object contains html (or encoded html for example), the HTML is not parsed.

Submitted by Michael Geary on Tue, 2006-07-04 09:29.

Thanks for the very creative solution, Olsow. It’s interesting to see a whole different approach to the problem.

A couple of thoughts…

The instanceof operator is available only in JavaScript 1.4 and higher. To support older browser versions the code would have to be changed to use other techniques.

One thing about my code was that it’s designed to be independent of jQuery. The idea was to allow for a two-stage page load: First, a very small JavaScript file including the DOM creation code but no jQuery could render the dynamic content. Then, a larger JavaScript file including jQuery, behavioral methods, etc. would be loaded in the background.

As it happens, I took a different approach for my own project anyway. Instead of using DOM creation at all, my first stage page rendering code generates HTML code and uses document.writeln() - yes, document.writeln()! - to render the content. This allows for completely smooth page loading, with the dynamic content appearing as part of the page when it first loads. That is impossible to achieve with DOM creation. More on this in a later post…

Submitted by Ripter (not verified) on Thu, 2006-07-20 07:10.

First, I think Olsow method is great! If they ever incorporate a DOM Creation into jQuery I would want it to work like this.

Now my issue, when using this method to set the class, it adds both “classname” and “class”. When you use .removeClass() it only removes the “class” part. I didn’t know if this problem was due to your method or jQuery.

Submitted by Michael Geary on Thu, 2006-07-20 09:06.

That’s a bug in jQuery itself. I think it’s fixed in the latest development version.

Submitted by Kevin (not verified) on Tue, 2006-09-12 17:07.

I like the way this works, but it stopped working when I switched to jQuery 1.0. So in case anyone cares, I’ve rewritten it slightly to work with current versions. Also, instead of returning jQuery objects (don’t see the point in that), these just return arrays of DOM elements that can be passed to jQuery methods like append()…

$.create = function() {
  var ret = [], a = arguments, i, e;
  a = a[0].constructor == Array ? a[0] : a;
  for(i=0; i<a.length; i++) {
    if(a[i+1] && a[i+1].constructor == Object) { // item is element if attributes follow
      e = document.createElement(a[i]);
      $(e).attr(a[++i]); // apply attributes
      if(a[i+1] && a[i+1].constructor == Array) $(e).append($.create(a[++i])); // optional children
    } else { // item is just a text node
  return ret;

$.tpl = function(json, tpl) {
  var ret = [];
  jQuery.each(json.constructor == Array ? json : [json], function() {
    var o = $.create(tpl.apply(this));
    for(var i=0;i<o.length;i++) ret.push(o[i]);
  return ret;
Submitted by carmen (not verified) on Wed, 2007-03-21 13:54.
Submitted by kajsaw (not verified) on Tue, 2006-07-18 00:26.

Hi, I have difficulties getting $.lNPUT to work in IE (6.0). The fabulous Microsoft Script Debugger tells me “Object doesn’t support this property or method”. I don’t know if it has something to do with the “e[a] = attrs[attr];”, but I tried all the fixes suggested and nothing works. I’m fairly new at DOM scripting, so I’d be most thankful for help..

Submitted by Michael Geary on Tue, 2006-07-18 07:35.

Kajsaw, could you post a code sample? I’d be happy to take a look at it. Kind of hard to guess the problem without seeing the actual code.

Submitted by Visitor on Wed, 2006-07-19 02:04.

Sure! For example, if I want to create a text field, I can write $.INPUT({type:’text’},”) in FF, but in IE i have to use the (much uglier) $.TEXTAREA to get it to work.

Submitted by Michael Geary on Wed, 2006-07-19 07:26.

Sorry if I seem dense, but if you could post an actual test page that I could try out, it would be easier for me to see the problem and experiment with it to try to find a solution.

You could either post a link to a test page, or include the actual HTML and JavaScript code in a comment. Enclose HTML code in a <geshi html4strict> tag, and JavaScript code in a <geshi javascript> tag. Or just post the whole thing inside a <pre> tag and I’ll clean up the formatting.


Submitted by Visitor on Tue, 2006-07-25 03:04.

‘course, i was just lazy..:) Btw, I’m typing on a Mac I can’t handle, and couldn’t find the curly brackets, so I used the square ones instead… I don’t do that normally:) also, your server seemed to think it was suspicious to use scripttags so i’m just printing the javascript. put this inside scripttags in the htmlbody, and nothing else on the page (except the jquery-dom.js include in the head).

    //input = $.BUTTON({}, "");  //works in IE
    input = $.INPUT({type: "button"}, "");   //doesn't work..
Submitted by Michael Geary on Tue, 2006-07-25 07:58.

I took care of the curly braces for you. :-) I’ll take a look at the code later and see if I can find out anything.

Submitted by Corey Jewett (not verified) on Sat, 2006-07-22 09:03.

Some code to add support for inline styles. (They’re only evil most of the time. :D)

Usage (same as before)

var ez_clearer = $.DIV({style: 'clear: left'})

And here’s some code to make the style attribute work. If this doesn’t work quite right, it’s because I ported it from my own DOM creator w/o testing. Probably fat fingered a variable.

for( var attr in attrs ) {
  var a = fix[attr] || attr;

  // minimalist element style parser, no respect for ; or : even if quoted.
  if (a == "style") {
    var props = attrs[a].split(/;/)
    for (i=0; i < props.length; i++) {
      var t = props[i].split(/:\s*/);
      var prop = t[0].replace(/^\s*([^\s]+)\s*/,'$1');
      var val = t[1].replace(/^\s*([^\s]+)\s*/,'$1');
      e.style[prop] = val;
  } else {
    e[a] = attrs[attr];
Submitted by D Wachsstock (not verified) on Tue, 2007-02-13 09:58.

Probably easier to use an object for the style and take advantage of JQuery:

var ez_clearer = $.DIV({style: {clear: 'left'} }) 

with this code (using a variation of the the “ultimate fix” above):

if (attr == "style") {
}else {
  $(e).attr(attr, attrs[attr])

or, even easier (skip the whole for (var attr in attrs) loop:

  if (attrs.style) $(e).css(attrs.style);
Submitted by Louise Dade (not verified) on Sat, 2006-07-29 02:24.

Just to let you know I had to add a tiny fix to allow the use of ‘colspan’ and ‘rowspan’ (it has to be ‘colSpan’ and ‘rowSpan’ in javascript):

var fix = { 'class':'className''for':'htmlFor', 'colspan':'colSpan', 'rowspan':'rowSpan' };
Submitted by DZacharias (not verified) on Thu, 2006-08-31 10:09.

I would love a simple generator app for this, where you paste in HTML and generate JS using this technique. Say you’re generating fieldsets on the fly; sure you only have to create the jquery-dom.js JS code one time, but if it’s like 4 fields with labels and all that it would be a pain.

Okay, back to work where I have to create the jquery-dom.js JS code for generating fieldsets on the fly with like 4 fields with labels and all that!

Submitted by Brandon Aaron (not verified) on Thu, 2006-09-14 14:19.

Thanks for this script. It has been an absolute joy to work with.

I made a simple tweak to return a jQuery object. Then I could benefit from method chaining.

Just change line 57 to this:

e.appendChild( $(arg).get(0) );

and line 60 to this:

return $(e);

Then you can do this:

$.DIV({ id: 'somethingNew'}).appendTo('#somethingOld').click(doSomething);
Submitted by Michael Geary on Sat, 2006-09-16 09:17.

Thanks for the kind words, Brandon.

Returning a jQuery object sounds like a great idea. I didn’t do it in my own code because I wanted to be able to use the DOM creation code standalone as well as with jQuery.

I have a question about the patch. The return $(e); makes sense, but what does the change to line 57 do? Given that arg is a DOM element, then $(arg).get(0) should just return arg. So I would expect that these two statements would be equivalent:

e.appendChild( arg );


e.appendChild( $(arg).get(0) );
Submitted by Brandon Aaron (not verified) on Sat, 2006-09-16 14:21.

You know I had the same question but at the time I was more in a hurry to meet my deadline. After quickly looking back over the script and the patch it would seem that line 57 should not be changed but rather a new lined added after the check to see if arg is null in append (line 51). The new line should say:

arg = arg.get(0);

Honestly I’m still a little confused how arg ends up being a jQuery object but it needs to be a regular dom node before being appended.

Submitted by Brandon Aaron (not verified) on Mon, 2006-09-18 06:56.

So I had to actually change the above line once again so that it would work for none DOM nodes.

if (arg.get) arg = arg.get(0);

The reason this is necessary is because each $.[tagName] returns a jQuery object.

Submitted by s (not verified) on Wed, 2006-11-01 21:48.

Please provide a link to a working example for this.

Submitted by Aleksandar Pavic (not verified) on Tue, 2006-11-21 13:51.

Hi I tried attributes cellspacing and cellpadding and they are not working/visible in DOM.

Submitted by Michael Geary on Tue, 2006-11-21 14:45.

There are some attributes that are capitalized differently in the DOM vs. HTML. If you use cellSpacing and cellPadding it should work OK.

Submitted by Ashish Agrawal (not verified) on Tue, 2006-12-26 05:39.

After looking at JS, I followed bellow given comments. I think there are lots of updates (fix) that should be available in download able JS file. Right now I feel after download JS, I have to follow the thread and update script. Can anybody please post latest script!


Submitted by mac (not verified) on Sun, 2007-02-25 18:21.

Here’s how I’d generate the same html table. It might seems more natural to “certain” people :-)

  ["table", 'class', 'MyTable',
    ["tr", 'class', 'MyTableRow'
     ["td", 'class', 'MyTableCol1' , "howdy"],
     ["td", 'class', 'MyTableCol2' , "Link: ",
      ["a", 'class', 'MyLink', 'href', 'https://www.example.com',


Submitted by Vishakha (not verified) on Thu, 2007-07-05 21:15.

This plugin is not working with IE for creating radio buttons.
It creates radio buttons but we can’t select that radio buttons. Is there any solution to solve this problem?

Submitted by Vishakha (not verified) on Thu, 2007-08-16 04:14.

Hi, I found the work around for this issue. The issue is specific to creating elements in IE. In IE you have to create radio buttons by using document.createElement("<input name='myradio' checked >") as the IE doesn’t allow the name or many other attributes to be set on input elements. So for this I made following change in jquery-dom.js file to run it in IE also.

        // check for IE
        if(document.all) {
            if(tag == 'input'){
                var a;
                var checked = '';
                for( var attr in attrs ) {
                    if(attr == 'name'){
                        a = attrs[attr];
                    if(attr == 'checked'){
                        checked = attrs[attr];
                var input_text = '<input name="' + a + '"' + checked +' >';
                e = document.createElement(input_text);
            } else{
            e = document.createElement( tag );
            e = document.createElement( tag );

Is this correct solution? Please suggest me if you have some better solution for the above issue.


Submitted by Michael Geary on Thu, 2007-08-16 08:13.

Thanks for chasing that down, Vishakha. I assume this code replaces the e = document.createElement( tag ); in the original? I’ll take a look at it and see if there is any way to simplify it, but it does sound like you’re on the right track.

Count on IE to make our lives more complicated! :-)

Submitted by lanzz (not verified) on Tue, 2012-10-23 03:06.

Notably missing is any support for the <EM> tag.