I was recently fussing over how developers don’t tend to separate their DOM logic/interfaces from their JavaScript “business logic”. Fuss as I might, I left the tirade rather open-ended, and that’s because I wasn’t sure that it was really a bad thing.

Either way, since JavaScript and the DOM are bunking together, we may as well make it safe — OOP safe.

For fun, let’s see how this might look:

/*
 * Word 
 * A class that encapsulates a word
 */
function Word(word) {
 
    this.setDOM('<li></li>');
 
    this.text(word).css('color', 'red'); // jQuery methods!!!
 
}
 
Word.prototype = new jQuery.Interface; // Magical

So, we’re sub-classing something called jQuery.Interface, and jQuery.Interface is sub-classed from jQuery itself, meaning that you get all of jQuery’s methods in your object.

This is nothing ground-breaking because we always knew we could do stuff like:

function Planet() {
    // Implement planet logic
}
 
Planet.prototype = jQuery([]);
 
// Planet now inherits jQuery's methods!!

But… inheriting straight from jQuery() won’t give you what you want. Firstly, you’ll need to mess around with populating the DOM references yourself (this[0] = jQuery('<div/>')[0] etc.), and you’d have to manage the length property too, and lastly you wouldn’t get all of the jQuery chainability that you’re used to. Not as you might expect it, at least:

var earth = new Planet;
earth.age = 4540000000;
 
earth.appendTo('body').age; // undefined

It’s undefined because jQuery’s DOM manipulation methods will return new jQuery objects, not simply a reference to this, which is what we’d really like!

So, to make it a little easier, I created a little thing called jQuery.Interface which does these things for you, enabling you to have beautiful jQuery sub classes.

Subclassing jQuery means that your class’ instances can be treated just like jQuery instances. Here’s a more realistic potential application of it:

/**
 * HexColorTooltip
 * Shows a colorised tooltip when hovering over HEX colors
 */
function HexColorTooltip() {
 
    this.offsetTop = 10;
    this.offsetLeft = 10;
 
    this.setDOM('<div/>', {
        css: {
            position: 'absolute',
            border: '1px solid black',
            height: 50,
            width: 50,
            zIndex: 100
        }
    });
 
    $('body').bind('mousemove', $.proxy(function(e){
        this.mouseX = e.pageX;
        this.mouseY = e.pageY;
    }, this));
 
    this.appendTo('body').hide();
 
 
}
 
HexColorTooltip.colorRegex = /^(?:#[A-F0-9]{3}|#[A-F0-9]{6})$/i;
 
HexColorTooltip.prototype = $.extend(new $.Interface, {
    bindTo: function(elem) {
 
        var tooltip = this;
 
        $(elem).each(function(){
 
            var t = $(this),
                text = t.text();
 
            if (HexColorTooltip.colorRegex.test(text)) {
 
                t.mousemove(function(){
                    tooltip.setColor(text).show();
                }).mouseout(function(){
                    tooltip.hide();
                });
 
            }
 
        });
 
    },
    setColor: function(color) {
        return this.color === color ? this : 
            this.css('background', this.color = color);
    },
    show: function() {
 
        /// Overriding jQuery.fn.show
        this.css({
            top: this.mouseY + this.offsetTop,
            left: this.mouseX + this.offsetLeft
        });
 
        // Call super
        return this.fn.show.call(this);
 
    }
});
 
// USAGE
var tooltip = new HexColorTooltip();
tooltip.bindTo('span');

See it working.

I don’t know if others will be interested in something like this. For many developers who use jQuery this might seem completely different to the jQuery you know and love, for others, maybe you’ll see it as a move in the right direction.

jQuery.Interface on Github.

Please note that it’s experimental.

Please also note that this isn’t the same as jQuery.sub:

A = jQuery.sub();
 
var a = new A;
a.foo = 'Foo';
a[0] = document.createElement('div');
a.length = 1;
 
// Chaining not reliable because some methods return a new jQ
a.prependTo('body').foo // => UNDEFINED

Also, jQuery.Interface provides a helpful setDOM method. Fundamentally, jQuery.sub and jQuery.Interface are trying to solve slightly different problems, as far as I can see.

Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!