Do Right Digital

DOM Widget Pattern – UI Widgets Made Easy

Creating JavaScript widgets to progressively enhance flat HTML sections requires a certain amount of coupling. Different people approach the coupling in different ways but I'd like to suggest a (new?) pattern for painlessly dealing with this - introducing the DOM Widget Pattern.

The pattern

Note: For this example I'm going to assume you're using jQuery, that's not a requirement for this pattern. Firstly create a jQuery widget:

$.fn.extend({
 myWidget: function () {
  $(this).on('click', 'a', function () {
   alert('you just clicked a link to ' + $(this).attr('href'));
  });
 }
});

Now, to create a markup structure which should have that behaviour you use a data attibute e.g.

<div data-widget="myWidget">
 <a href="http://google.com">Google</a>
</div>

You can then use one load handler to load all widgets:

$(document).ready(function () {
  $('[data-widget="myWidget"]').myWidget();
});

You could then make this more generic to load any named widget (for an implementation of this visit Do Right Digital's Dom Widgets)

$(document).ready(function () {
  $(document).initDomWidgets();
});

The benefits

There are some simple benefits:

  • You don't use arbitrary IDs
  • You don't reuse styling classes
  • You don't create classes just to hook into JS behaviour
  • The dependancy is clear

My favourite benefit (so far) is Ajax loading content - When you're ajax loading all you need to do is call .initDomwidgets() on your new DOM before/after dropping it on the page and it'll behave just like it would if you had put it in at page load.

Things you might be wondering

"But I want to setup some parameters" - generally your config should be configured using data-* attributes.
"But I want to use multiple widgets on one element" - widget names should be space separable like classes.
"How granular should the widgets be?" - as granular as possible… or at least as granular as you find beneficial. There have been times before where I've grouped some behaviours into a widget which in turn calls 2-3 different widgets for child selectors, that's more an example of dealing with legacy code than how this pattern is best applied.