views:

60

answers:

2

What I would like to do is be able to dynamically a block of self-contained HTML that has additional properties.

I'm not sure what to call it, as "widget" now implies it is something like flair or a badge to put on your blog. I am talking about a reusable, customizable, "composite" JS/HTML(/CSS) object.

For example, a simple date picker.

document.body.appendChild(new DatePicker());

Would generate

<div class="datepicker" name="...">
    <select class="datepicker_monthSelect">
        <option value="0">January</option>
        ....
    </select>
    <select class="datepicker_daySelect">
        <option value="1">1</option>
        ....
    </select>
    <select class="datepicker_yearSelect">
        <option value="2010">2010</option>
        ....
    </select>
</div>

And would have properties like

var d = new DatePicker();
d.value = "2010-06-21";
d.name = "the_datepicker";
d.month = 5;
d.month = "June";
d.day = 21;
d.year = 2010;

How can this be (easily) accomplished? What are the best practices for this situation?

+1  A: 

There are a couple of different options with this.

You could 'subclass' the element object but that would be extremely inefficient as Javascript pretend to be as little Object-Oriented as possible. (Plus, extending host objects is yuck.)

However, the approach that I would take would be to define a to_html function on the DatePicker 'class'.

This method would theoretically take the attributes of the DatePicker and use them to create the HTML.

And, if you keep this HTML stored in the DatePicker, you would have an easy way to update it and access its value as needed further in the code.

huntaub
+1  A: 

I like the MooTools approach of doing this. Any JavaScript object can define a toElement method and you can append this object to the DOM using MooTools' wrappers. The wrappers will query the toElement property on the object, and if it exists, then the return value of calling toElement() will be appended to the DOM. For this to work, you would have to use the wrappers for appending to DOM instead of using appendChild directly.

function DatePicker(date) {
    this.date = date;
}

DatePicker.prototype.toElement = function() {
    // return a DOM representation of this object
};

var picker = new DatePicker();

Here are some ways to go about using this:

// retrieve the DOM node directly
document.body.appendChild(picker.toElement());

// use a wrapper to append, that queries toElement first
// DOM is a function like jQuery, that wraps the given DOM
// object with additional methods like append for querying toElement.
DOM(document.body).append(picker);

Of course, you can always overwrite the native appendChild method, however, I would not recommend this.

For the sake of completeness, this is how you would do it though. Use a closure to hold on to the original appendChild method, and replace it with a new method that first checks if toElement is defined, and retrieves the element representation of that object if it does. Otherwise, proceed with adding the passed-in object as it is.

(function(original) {
    Element.prototype.appendChild = function(child) {
        if('toElement' in child) {
            original.call(this, child.toElement());
        }
        else {
            original.call(this, child);
        }
    }
})(Element.prototype.appendChild);

Add the object to DOM as in your example:

document.body.appendChild(new DatePicker());
Anurag