Building DOM elements by hand is mind numbingly boring. It’s more fun to watch paint dry. Traversing DOM is just as boring, which is what prompted my
JQuery post.
If you’re writing a MS AJAX client control or behavior, you have some powerful functionality in the common toolkit library, common.js. See how you need to declare an extender control to have this library available. Btw, applying the [RequiredScript (typeof(CommonToolkitScripts))] attribute is ignored on controls implementing the IScriptControl interface. I guess only bona fide extenders are supported.
The syntax
Luckily, the AjaxControlToolkit.CommonToolkitScripts singleton has a shortcut notation, $common, which is simply a “global” JavaScript variable.
The library has an interesting factory method of a sorts,
createElementFromTemplate, which saves you typing boring code of creating DOM elements, filling in their properties, attaching events, and doing the same for children elements (if any).
The function takes three parameters:
- template (mandatory): an object literal describing the intended element, properties, events, child nodes (if any), etc.
- appendToParent (optional): A DOM element within which to append the newly created element. Note: the parent must be an instance of
DomElement which you can obtain via $get('....').
- nameTable (optional): An object to use as the storage for the element using
template.name as the key, as inline documentation states. I know it sounds confusing. It is. I’ll skip it in this post. Once I understand what it’s used for and how, I’ll write a follow-up post.
Let’s look at some examples.
Example 1
var el = $common.createElementFromTemplate ({nodeName : 'div'});
This creates a bare-bones div. Seems slower than just calling document.createElement('div') because of all kinds of checks inside createElementFromTemplate.
Example 2
Here’s an example from our project:
var el = this.get_element();
var new_id = el.id + '_checkall_';
var footer = $common.createElementFromTemplate ({
nodeName : 'div',
cssClasses : ['callout'],
children : [
{
nodeName : 'input',
properties : {
type : 'checkbox',
id : new_id
},
events : this._checkAll$delegate
/*
Declared as:
this._checkAll$delegate = {
click : Function.createDelegate (this, this._toggle)
}
*/
},
{
nodeName : 'label',
properties : {
htmlFor : new_id
},
content : document.createTextNode ('*blah*')
}]
});
I’m emulating the CheckBox server control, i.e. a checkbox and a label when rendered. This sample creates a footer div with a CSS class of 'callout', and I’m putting two child nodes inside: a checkbox (nodeName : 'input') and a label (nodeName : 'label').
Pay attention to how both child nodes are initialized, especially the htmlFor property of the label (since you can’t use 'for' as an attribute).
Also note the appendToParent and nameTable are not set here. I’m passing only one parameter, template, in the object literal notation. If template has a children field, as shown above, the children are simply fed recursively to
createElementFromTemplate.
Example 3
Once you create an element, you need to attach it. It’s not attached for you by default! There are two ways to do it as the following sample shows:
$common.createElementFromTemplate ({
parent : pager,
nodeName : 'span',
properties : { innerHTML: '*blah*' }
});
Here I’m creating a span with some arbitrary content and attaching it to an element called pager. You can do the same thing by passing pager in the appendToParent
parameter:
$common.createElementFromTemplate ({
nodeName : 'span',
properties : { innerHTML: '*blah*' }
}, pager);
Example 4:
Adding one or more CSS classes is fine and dandy, but how about setting CSS rules explicitly? You can throw an object literal in the
properties field:
properties : {
style : {
height : '100px',
width : '100px',
backgroundColor : 'white'
}
}
Syntax recap
So what fields may the template parameter hold?
- nodeName: a DOM element to create.
- parent: a container to append the element to.
- properties: an object literal with properties to
assign to the element.
- cssClasses: an array (!) of CSS classes to assign to the element.
- events: an object literal of event handlers, same notation as for
$addHandlers.
For example (taken from inline documentation):
events : {
click : function() { alert('foo'); },
mouseover : function() { elt.backgroundColor = '#999'; },
mouseout : function() { elt.backgroundColor = 'white'; }
}
- visible: hide or show the element upon creation.
- opacity: self-explanatory, right? Must be in the 0…1
range (ex: 0.3).
- children: an array (!) of objects for each node in the same literal notation as
template itself.
- content: content to stick inside the element.
- name and contentPresenter: work in tandem with the
nameTable parameter which I won’t discuss here.
Passing up on nameTable
I don’t fully understand what the last parameter,
nameTable is for. Neither can I find a reasonable
explanation anywhere.