Dependency Injection for jQuery Plugins

I've been using jQuery heavily for the past 5 years or so but the more I used the more it seemed that the way most plugins are written is very limiting to the way the can be used and built upon.

The Problem

Let's look at a couple of options for the popular bxslider plugin:

$('.demo').bxslider({
    slideMargin: 0,
    captions: true
});

It looks fine at first sight — I can specify spacing for the slides and if I need to I can get some captions from the image title attribute. But what if I want to have different margin for each slide, like make them progressively smaller as we go towards the end? Or may be store captions in the alt attribute instead. I just can't do that with scalar options.

Functions as Dependencies

Traditional OOP solution for this problem is to have another object injected into constructed one as a dependency at the initialization time (hence term "dependency injection"). You can refer to this article for more details. In a nutshell you have to define an interface one way or the other and say that your new object will expect to get an object that supports taht interface at construction time. This is very powreful but also very verbose thing to implement.

This concept becomes a lot more easier to use with JavaScript functions since they are implemented as first-class objects. Among other things it means that you can pass them around just like you would any other object.

If you use functions for dependencies the only things you need to define as an interface are input parameters (execution context in this case is considered as one of the input parameters) that would be passed upon calling by the host object and type (and may be boundaries) of the return value. This may sound complicated but it isn't really.

Let's define such interface for the slideMargin option I showed earlier. There's no default way to formally define function parameters in JavaScript so I will use JSDoc.

/**
 * Function should return appropriate margin for provided $slide.
 * @param  {jQuery} $slide
 * @param  {number} index
 * @param  {number} count
 * @return {number}
 */

And here's how we would implement progressive reduction of slide margin using this definition:

$('.demo').bxslider({
    slideMargin: function($slide, index, count) {
        return 50 * (index / count);
    },
    captions: true
});

As you can see it's very simple to implement such a dependency but it also provides unparalleled amount of flexibility compared to scalar values. To allow for even crazier customization you can opt in to allowing asynchronous behavior inside dependencies. Let's imagine that title option allows this by providing a callback as described in it's definition:

/**
 * Function should return appropriate title for provided $slide.
 * @param  {jQuery} $slide
 * @param  {number} index
 * @param  {Function} callback
 */

Now he can use AJAX requests to get our titles:

$('.demo').bxslider({
    slideMargin: function($slide, index, count) {
        return 50 * (index / count);
    },
    captions: function($slide, index, callback) {
        $.get('/getTitleForSlide/' + index, callback);
    }
});

Conclusion

I've been using this technique every day for about a year now on a large high-load website and it really changed not only the way I approach writing jQuery plugins but the way I think and implement dependencies in JavaScript code in general. By allowing functional dependencies in my code I can reuse it a lot easier and in a lot more situations. This in turn results in smaller code base with very little hard dependencies between objects.

If you want to see some more examples of this approach in action you can take a look at my Brute Select Plugin or explore Twitter Bootstrap JavaScript Components as they also partially support this behavior. For example their tooltip plugin supports dynamic placement and title text via functional dependency.

Social comments Cackle