Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are best practices for designing JavaScript classes that represent UI objects in the DOM?

I am very familiar with JavaScript and OOP but am very unfamiliar with designing JS classes that are used for programming the UI (HTML). I have searched around a bit but have not come across any prevalent patterns.

Let's say I wanted to dynamically create panel-like objects (similar to Microsoft Windows panels). Each object needs a container, a header area that could include a text label, and a close button that has a click event handler. In addition to a DOM representation (HTML), the object would also have a JavaScript object representation (variables and methods). Here is one way I have tried:

//
// Window class
//
var Window = function(params) {
    this.testMethod = function() {
        console.log('test');  // just an example
    }

    this.windowDiv = document.createElement('div');
    this.windowDiv.style.width = params.width + 'px';
    this.windowDiv.style.height = params.height + 'px';
    this.windowDiv.style.position = 'absolute';
    this.windowDiv.style.top = '30px';
    this.windowDiv.style.left = '30px';
    this.windowDiv.style.border = '1px solid #000';

    this.headerDiv = document.createElement('div');
    this.headerDiv.style.width = '100%';
    this.headerDiv.style.height = '30px';
    this.headerDiv.style.background = '#bbb';
    this.headerDiv.style.borderBottom = '1px solid #000';
    this.headerDiv.innerHTML = params.title;

    this.buttonDiv = document.createElement('div');
    this.buttonDiv.style.width = '30px';
    this.buttonDiv.style.height = '18px';
    this.buttonDiv.style.position = 'absolute';
    this.buttonDiv.style.top = '0px';
    this.buttonDiv.style.right = '5px';
    this.buttonDiv.style.textAlign = 'center';
    this.buttonDiv.style.background = 'red';
    this.buttonDiv.style.border = '0px 1px 1px 1px solid #000';
    this.buttonDiv.innerHTML = 'x';
    this.buttonDiv.addEventListener('click', function(e) {
        document.body.removeChild(e.target.parentNode.parentNode);
    }, false);

    this.headerDiv.appendChild(this.buttonDiv);
    this.windowDiv.appendChild(this.headerDiv);
    document.body.appendChild(this.windowDiv);
}

// Initialize
var myWindow = new Window({
    width: 400,
    height: 200,
    title: 'My Window'
});
myWindow.testMethod();

This doesn't feel "right." Another approach might be to have a render() method and use it to separate out the HTML generation code. It seems awkward that the JS object contains multiple nested HTML objects inside of it. Should I just pass in the nested tags via innerHTML?

So I am curious... what is the best approach here? What is the best way to design classes that have DOM code (HTML objects and styling) and traditional variables and methods? Is there a way to use Prototypes for these HTML objects? You can even imagine in this example that there is also a WindowManager class that contains instantiated Window objects, helps manage them, and creates new ones. And it, too, could have an HTML representation.

Some of the above could be handled with static CSS and applying IDs/classes to clean up some of the code, but let's just say that positioning and size must be accomplished programmatically (to handle resizing, drag-drop, etc.).

I am not interested in framework-level solutions that could be provided by ExtJS, jQuery, etc. I want to know what the best practices are for home-grown code. In fact, I'd be interested in knowing how the engineers of said frameworks design their UI classes.

like image 763
skyline3000 Avatar asked Jul 05 '11 05:07

skyline3000


1 Answers

For the HTML part of the object, you could use a template to define the HTML structure of the component, plugging in whatever data is necessary when the component is initialized. There are many ways to implement the template function, take a look at underscore.js for an example.

For the styles, I would put the default values in the stylesheet. It doesn't matter if they need to be modified later via javascript, that will still work even if they are defined in css. The only information that should be in the component is the default behavior of the various events that that component fires.

You could also look into creating a simple prototype chain for putting common properties inside base "classes" (understanding that javascript has no real classes), again refer to underscore.js for an example of the extend function that does this, among other implementations that can be googled.

In the end, all that matters is your code is easy to use and understand, and performs reasonably well. You can also go back and improve parts that don't work quite so well, or reorganize the code into modules.

like image 161
Jason Miesionczek Avatar answered Nov 15 '22 00:11

Jason Miesionczek