DOMtastic’s $.baseClass

Introduction

Three weeks ago, I’ve received an email from a DOMtastic user. He was checking out the library for use as a component baseline, and asked me if I considered exposing the DOMtastic wrapper as an ES6 class. He also included some example code:

import $ from 'domtastic';
class BaseComponent extends $ {
constructor(selector) {
super(selector)
}
}
let div = BaseComponent('div');
console.log(div.attr('bar'));

I hadn’t considered this at all, and I think it’s a great idea.

jQuery Plugins

While working the idea into DOMtastic, I made the comparison with jQuery (or DOMtastic) plugins. This mechanism basically works by extending the $.fn object. Then all instances of $(‘.selector’) have access to the $.fn API on their own prototype. Let’s see a very basic example:

$.fn.hilight = function(options) {
this.css({ "backgroundColor": options.backgroundColor });
return this;
}
$('.paragraph').hilight({backgroundColor: 'green'});

One thing with jQuery plugins is that $.fn is like the global window object for jQuery itself and the plugin eco-system: they all share the same scope. Methods can overwrite each other, and events are on the same bus.

Another point is that jQuery plugins aren’t really extensible. You can’t grab a plugin and change its behavior. Many plugins allow to throw in an endless options object to cater for all possible needs, but this is an endless exercise.

Extending (view) classes

Now, what if we turn things around?

It would be great if we started out with a base class inheriting all the $ goodness. Then we extend from that, say a simple custom dropdown. This in turn could be extended further. In ES6 we now have a real class system, so let’s take advantage of it.

From now on, I’m going to simply call it a “class” here, what some might refer to as a UI component, web component, a jQuery plugin, or a view class.

Let’s look at an example “Highlight” class hooking into the base constructor of the new $.BaseClass:

class Highlight extends $.BaseClass {
constructor(selector, options) {
super(selector);
this.setBackground(options);
this.on('mouseover', this.underLineText.bind(this));
}
setBackground() {
return this.css('background-color', options.bgColor);
}
}
let component = new Highlight('.paragraph', {bgColor: 'green'});

Or, a separate initialize method to be invoked manually:

class Highlight extends $.BaseClass {
init(options) {
return this.setBackground(options);
}
}
let component = new Highlight('.paragraph');
component.init({bgColor: 'green'});

Now if someone likes the plugin, but wants to change its behavior, the class be easily extended:

class ExtraHighlighter extends Highlight {
init(options) {
super.init(options);
return this.setFontColor(options);
}
}
let component = new ExtraHighlighter('.paragraph');
component.init({bgColor: 'green', color: 'red'});

Simple & Elegant

This is why I liked the “component baseline” idea for DOMtastic so much. It is a simple and elegant approach to UI components:

  • You can implement components any way you like.
  • You extend from $.fn, instead of extending $.fn.
  • You get the DOM and event methods for free (there’s no performance penalty).
  • You could use this with a smaller, custom build of DOMtastic. For instance, just the eventing or selector module(s).
  • You can start with base classes, inherit from those and build more complex UI components.
  • You can use it with ES5 or ES6. Using ES6 classes means you get a lot of other ES6 goodness with it for free.

ES5

In “plain JavaScript” (i.e. not ES6), we can use Object.create() to achieve the same inheritance:

var $ = require('domtastic');

function Highlight() {
$.BaseClass.apply(this, arguments);
}

Highlight.prototype = Object.create($.BaseClass.prototype);

Highlight.prototype.init = function(options) {
return this.setBackground(options);
};
Highlight.prototype.setBackground = function(options) {
return this.css('background-color', options.bgColor)
};

Wrapping Up

Maybe it helps to think of DOMtastic’s $.baseClass as having some similarities with a Backbone.View (this.$el) or a Twitter Flight Component.

I’m really interested to see how people would use it. Do you know of interesting use cases, or similar efforts? I’d love to hear about them!