How I write JavaScript Widgets
Improving the structure of the code I write is an ongoing concern. Over the last while, I feel I've settled in to how I write HTML/CSS/JavaScript widgets and find the pattern repeating itself over and over again.
The code example below is a simple little logger widget. It appends messages to a list and has a clear link to delete all the recorded messages.
// Wrap code with module pattern.
(function() {
var global = this;
// helper functions
var escapeHTML = function(msg) {
return (String(msg)).replace('&', '&').replace('<', '<').replace('>', '>');
};
// widget constructor function
global.MY_makeLogger = function() {
// private instance methods
var clear = function() {
LIB_emptyElement(logEl);
};
var append = function(msg, className) {
LIB_insertBottom(logEl,
'<dt class="'+className+'">' + (new Date()) + '</dt>' +
'<dd class="'+className+'">' + escapeHTML(msg) + '</dd>');
};
// create widget DOM fragment
var parser = document.createElement('div');
LIB_upateElement(parser,
'<div class="Logger">' +
'<p><a href="#" class="clearLink">clear</a></p>' +
'<dl class="log"></dl>' +
'</div>');
// find pieces and enliven DOM fragment
var rootEl = LIB_find('.Logger', parser)[0];
parser = null; // enable garbage collection of parser div
var logEl = LIB_find('.log', rootEl)[0];
LIB_on(LIB_find('.clearLink', rootEl), 'click', function(e) {
LIB_preventDefault(e);
clear();
});
// public instance methods
return {
getRootEl: function() {return rootEl;},
log : function(msg) {append(msg, 'log' );},
warn : function(msg) {append(msg, 'warn' );},
error : function(msg) {append(msg, 'error');}
};
};
})();
I use the module pattern around each widget constructor function. I write the module pattern as the following. Having the explicit global
variable makes it clear the MY_makeLogger
function is global. When real-world code grows larger than a small example, it is some welcome clarity.
(function() {
var global = this;
// ...
})();
Several library functions are prefixed with LIB_
to avoid collisions. I've written about namespacing before and don't buy into the popular use of objects as namespaces.
The library functions are all common components of most libraries. LIB_on(elements, type, handler)
adds an event handler to one or more elements. LIB_preventDefault(e)
stops default browser event behavior like following a link. LIB_find(selector, rootEl)
finds elements matching a CSS selector under the optional element rootEl
. LIB_updateElement
, LIB_insertBottom
, and LIB_emptyElement
use the non-standard innerHTML
property of elements to manipulate the DOM. The innerHTML
property may be non-standard but I've found it is more reliable than the standard DOM manipulation methods, especially when generating elements.
At the top of the module, before the constructor function, are helper functions which do not need to capture in their closures' the widget's state variables. The escapeHTML
function, for example, is a pure function. (Its implementation may be too naive for a real escape function but that is not the point here.) If these helper functions could be reused by other widgets they could be extracted to a library.
The constructor function MY_makeLogger
is the last element inside the module. The constructor is the only symbol added to the global object. It is namespaced by the MY_
prefix to avoid collisions.
The constructor function starts with the private instance methods (as they would be called in Java.) These functions capture in their closures' the widget's state variables (i.e. rootEl
and logEl
).
The widget's DOM fragment is created next. The use of a parser element allows the entire widget HTML to be expressed declaratively in HTML. This is compact, reliable and saves many uses of el.appendChild()
and all of that bulky, imperative nuisance.
Now that the widget's DOM is created, we find bits of it and store references to them in state variables and enliven others with event listeners. Note that all elements are found using class names. First instincts may be to find elements by id attributes but using class names allows the widget to appear on the same page twice. If id attributes were used then either the same id would repeat on the page (illegal) or you would have to use some sort of global gensym
-like function when generating the id attributes. Class names is easier.
The final part of the constructor is returning the public instance methods. I only return function-valued properties (i.e. methods). When ECMAScript 3.1 is available I will freeze the returned object and each of its properties' functions.
The return statement always contains an object literal. I don't want any of my other code in the constructor calling these functions as properties of the returned object.
Note this widget does not attach itself to the page's DOM. Instead the caller can use the getRootEl
function to attach the widget to the DOM.
This widget offers only four public instance methods. Widgets should not have very many public instance methods. If the number of methods grows too larger perhaps your widget is doing too much.
There is no use of this
or prototype
in this widget. I don't like these words, in general.
This style of OOP has no inheritance. I consider that a benefit for maintainability. In general, the style of OOP in the example seems to be direction the ECMAScript Harmony class
proposal is going.
I've omitted the necessary feature testing that would be required if this widget was destined for the unrestricted web.
So that's my general pattern for widgets. It has been working out well for me and I think it is conceptually quite simple compared alternate approaches I've tried and seen.
Comments
Have something to write? Comment on this article.
kangax,
By adding function identifier either in function declarations or function expressions, the functions become available to the rest of the code inside the constructor. I particularly don't want that in almost all cases. The public methods are for external use not internal use.
Worries about memory consumption are sometimes important but usually not for widgets. I haven't found myself putting thousands of widgets on a page.
Yes there was a typo. Thanks.
Thanks for posting the method to making the Widgets in Java is going to be a huge time saver for me down the road!
Stupid question from a newbie:
How then does this get used in the HTML ?
One additional trick I have found useful in the "module" pattern (where you end with a "return {...};" statement, is to first save the value in a local/private variable of what you are going to return (I always name it "publicAPI"), and then return it.
The value here is that in the "public API" of your module, you can have functions and variables. Basically, the module can update the value of a public variable by changing the publicAPI.xxx variable, and automatically, the calling code can see that value because of the shared reference.
Similarly, the calling code can set the value of a public variable, and the module can immediately see this value and use it, if desired. You can even emulate "read-only" by simply having the module overwrite the value to that of the internally kept ("safe") value, regardless of what the calling code changes it to.
I find that for some cases, letting calling code set values on certain configuration variables directly, rather than by function call, is more stylistically/structurally pleasing to code with, as this is how other "native" Javascript objects work.
One note to take care of: I also always create an internal "destructor", and attach a "onunload" event handler to it, so that I can unset references like "publicAPI", to prevent unintended memory leaks.
Steve,
Sometime after the window.onload
event you can execute the following to add a logger widget to the page.
var logger = MY_makeLogger();
var fooEl = document.getElementById('foo');
fooEl.appendChild(logger.getRootEl());
logger.error('PANIC!');
Kyle,
I don't like calling code setting values of my widgets directly. It breaks encapsulation. If I need to allow setting, I will provide a setter method. That way if the abstraction inside the widget changes, I can use the setter as an adapter. Also, in the future, there may be other things that need doing when the value is set. It is harder to retrofit existing code if the calling code can set values directly so I prefer to start with setters from the beginning.
I do have a destroy
method on some of my widgets that do actually need a clean-up. I didn't think of it as the general case but it is a good idea. At least an empty destroy
method could be provided on all widgets.
Nice.
I like the Module pattern. Gives an easy way to declare private/public variables and methods.
Thanks for the tip on not using Objects for namespacing.
Great tips!
Quite a good lesson for me to learn.
@Peter-
Question... why do you suppose the designers of the Javascript language "broke encapsulation" by having many/most of the native objects deal with variable getting/setting rather than function calling?
I have one guess... because it's less overhead to set a variable's value than to call a single function call just to do the same thing. It's also less overhead to read a value from a variable than to call a function to get that value.
You see, the theory of OO (encapsulation) is fine, but in practice (ESPECIALLY with interpreted scripting languages), sometimes the tradeoffs have to be weighed.
I also don't see how my suggestion is less "future-proof" to refactoring than is a function getter/setter interface? If the calling code has to use the value from a function (or from a value), you can't change the format of that value without having the calling code change, regardless of how they get that value (variable access or function call).
But, you can quite easily change the internal module's functions which SET the value of that variable (like how it figures out how to set the value). That kind of internal refactoring is still quite possible, just like it is with the one you suggest.
But neither your approach nor mine can get around the fact that if you refactor the publicly facing information/API/values, then calling code will have to change.
Both strategies accomplish the same thing, but respectfully speaking, I think my strategy not only fits more naturally with the rest of the Javascript langauge constructs/objects, but also is in some cases more efficient than always needing a function call which has only one line of code in it. That's an awful lot of overhead that might be avoided.
Kyle,
The creators of JavaScript and DOM APIs did not break encapsulation. What they are able to do when they define an object is more than what we are able to do when we define an object. When you write
el.style.background = "#333333";
You are actually calling a setter function that can do anything the designers of the style
object want it to do. If you start playing around with defining host objects in a JavaScript implementation like Spidermonkey or Rhino you will see what I mean.
The only way to get equivalent power in the code we are writing for browsers is to use an actually setter function in the JavaScript like I have suggested.
You are right that the internal functions of an object could set a public API value to match legacy values. A bit backwards to the way I think about things but I suppose it could work. It cannot do everything a setter function can do...
Using setter functions have other advantages. If you want to validate the value when it is set and throw an error if it is bad, then you need a setter function. If you want other side-effects to occur when a value is set, like log a message, then the setter function allows this to be done.
In ECMAScript 3.1, I believe, there will be setter and getter functions that allow the syntax of property access and assignment. Some ECMAScript implementations already have that. I'm not really excited about that because I don't mind calling a function but it might be right up your alley.
@Peter-
Fair enough, I agree that having the ability to immediately trigger an error from validation rules is something my approach does not provide, nor can an automatic log event happen. However, I try to structure the functionality of my modules such that the calling code sets a variable, and then then or later fires off some action on the module by calling a function. When that function runs, the first thing it does is check to see if there are new values in the "publicAPI", and if there are, it validates and then uses them. So, theoretically, in that scenario, the same type of logging/validation-error handling could happen at that time.
Yes, I could force all those variable settings into the single function call, but then function calls with 5 or 10 parameters get messy. And even though I like the practice, using an object literal to "name" your parameters feels a little less than natural compared to how most of native Javascript is interacted with (though the frameworks love to do it!)
For almost all of modules that I would be creating in Javascript, just setting a variable to a certain value wouldn't necessarily (need to) imply firing of a action (like it does with your "style" object example, where a "redraw" of the page/element is triggered). In fact, I think far fewer of the native Javascript objects actually fire off visible/functional events when a user sets a variable-most of the time it appears to the calling code as a passive event.
Instead, my modules usually do something in response to either a function call, a timer event, an asynchronous event response, or an event handler (mouse, keyboard, etc). In all those cases, there's an active execution thread fired off against the module, and again, the first thing it does is ask the publicAPI for any configuration variable changes that it should consult for use during its next action.
For instance, I have an object that emulates the native XHR object, right down to its property configuration (onreadystate, status, etc). Setting/reading of those properties doesn't cause the native XHR object to do anything special (as it appears to the calling code, anyway), nor does there seem even to be any kind of validation that happens with it. Instead, it doesn't seem to check those values until either you call a function to trigger it to do something, or it gets an async response back from a server.
So, going along that mindset, and wanting to emulate (not exactly mimic) that same behavior, this is where I came up with this approach.
Moreover, if I just create any new module, one that's not just a replacement of an existing native one, and I use the same functionality, I personally feel like it makes my module "feel" more natural to coders who already use core Javascript, and fit in better.
Think about this: generally speaking, you can call obj.SetAttribute(xxx,'...'), or you can just say obj.xxx = '...'. I would say that though both are valid and even common, the second syntax is much more common and "natural" for a lot of javascript'ers. And there are very few places where Javascript forces calling code to use a function instead of just setting a property.
Think about this: generally speaking, you can call obj.SetAttribute(xxx,'...'), or you can just say obj.xxx = '...'. I would say that though both are valid and even common, the second syntax is much more common and "natural" for a lot of javascript'ers. And there are very few places where Javascript forces calling code to use a function instead of just setting a property.
JavaScript is not a beautiful language. Scheme wins that competition, hands down. With JavaScript, I try to plug my nose and just live with the syntax.
In the end, it is semantics that matter to me, not syntax. The semantics I want is a setter function. For now, the only way to do that is with an actually function call.
I'm using a very similar structure for widgets (and other objects, especially singletons), but I think that having all of the methods defined inside the maker function is only advisable if there won't be a lot of objects created from it. For something like, say, a chess figure object, or a thumbnail object, I'd probably still use this/prototype.
Do you have any thoughts about memory consumption with this pattern?
Conrad,
I agree with you. If there were many objects to be created, and performance using the above technique was observed to be unsatisfactory, then I would consider trading encapsulation for decreased memory usage and increased speed by using prototype
and this
. I think that a chess board worth of pieces would not be enough to warrant abandoning the technique in this article. I would estimate at least hundreds of objects would need to be created before there was a noticeable need to change things. Most browser apps are DOM-bound and the JavaScript execution takes very little of the total execution time.
Have something to write? Comment on this article.
Interesting.
I would consider adding function identifiers to methods returned from a "module" as part of a "public" interface (i.e. use function declarations rather than expressions); Makes it easier to introspect and debug:
Or perhaps just do it "inline" (all-famous IE bug should not matter much here, since the snippet is contained within a separate scope):
As far as `this` and `prototype`, I have always considered prototype chain to be a valuable tool when it comes to an effective memory use. Has this ever been a concern for you?
P.S. I believe there is a small typo (since `parser` references a plain element and shouldn't really have a `0` property):
-
kangax