As I worked on the GameEngine improvements, I realized that it would be nice to have a menu and several other DOM elements. Since the engine will be standalone, it is necessary to dynamically create the DOM elements required to run the game. For this task, I have use a method "document.createTag()", which I often refactor for various projects. In its most basic form, it looks like the following:
Example 1: document.createTag
YAHOO.lang.augmentObject(document, { /** * Creates and returns an html element and adds attributes from the hash. * * @method createTag * @param tagName {String} Required. Tag name to create. * @param h {Object} Optional. The hashtable of attributes, styles, and classes; defaults is empty object. * @return {Element} The newly created element; returns null otherwise. * @static */ createTag: function(tagName, h) { var Dom = YAHOO.util.Dom, node = document.createElement(tagName), hash = isType(h,object) ? h : {}; // iterate through the possible attributes for (var k in hash) { var v = hash[k]; k = k.toLowerCase(); if (isType(v,string) || (style=== k && isType(v,object))) { switch (k) { caseclassname: caseklass: caseclass: casecls: Dom.addClass(node, v); break; casecellpadding: node.cellPadding = v; break; casecellspacing: node.cellSpacing = v; break; casecolspan: node.colSpan = v; break; casechecked: casedisabled: node[k] = v; break; caserowspan: node.rowSpan = v; break; casestyle: // iterate through the style object for (var t in v) { var s = v[t]; if (isType(s,string) || isType(s,number)) { Dom.setStyle(node, t, s); } }; break; caseinnerhtml: casetext: if (isType(v,string) && ! v.match(/< .*?>/) && ! v.match(/&.*?;/)) { node.appendChild(document.createTextNode(v)); } else { node.innerHTML = v; } break; default: node.setAttribute(k, v); break; } } }; return node || null; } });
To use this method, simply include it in your JavaScript source files, then call "document.createTag" passing in two parameters: 1st parameter is the tag name you wish to create, and the 2nd is an Object where the keys are tag attribute names and the values are the attribute values. Using a for … in
loop we iterate on each key in the Object, evaluating only those values that are strings and numbers, or an object in the case of the style attribute. The switch statement then, normalizes the keys, so the key checked
does the same thing as cHeCkEd
, because capitalization does matter.
Over the years I have added each case of the switch statement based on issues experienced in various browsers. Most attributes seem to be applied just fine, when you use the setAttribute
method, so this is the default case. Some attributes such as checked
and disabled
do not work in all browsers with setAttribute
, so they are applied only with dot notation. Some attributes such as cellPadding
and cellSpacing
are case-sensitive in certain browsers (and capitalizing them doesnt break the browsers that lower-case worked in), and/or can only be applied via dot notation, so we special-case each of them. The word
class is a reserved word in at least IE, so you cannot do "object.class", instead you need to use "object.className", and this seems to be supported by all browsers; I also support various ways of storing the classname as a key, because different projects have different naming conventions. Style is a special-case that must contain an object, where the keys are style names, and the values are what you want to set the style to (strings and integers are acceptable). Lastly, the
text or
innerhtml key will attempt to append the provided value as a text node, unless the value: isn
t a string, contains HTML elements, or contains HTML special characters; in those cases it uses innerHTML, because it will render HTML and special characters correctly, while a text node will simply print the text as is.
Now, this method probably isnt perfect, but it works with all the browsers that I have ever needed to support. Feel free to chime in, if you know of any other special-case situations or browsers that you believe this method won
t work with.
This solution is homegrown, and there may exist better DOM node creation methods, with more exposure and support. I know the Framework Ext.js has a DOM node creation method, and I wouldn&rsquot;t be surprised if Dojo or jQuery have a plugin solution as well.