jQuery Color Picker Sliders plugin

Posted on

Problem

I want to access some of the methods from outside of the plugins scope, namely showPopup(), hidePopup() and updateColor(newcolor).

Initialization

<span class="cp"></span>

<script>
    var cp = $(".cp").ColorPickerSliders({
        flat: true,
        order: {
            hsl: 1,
            preview: 2
        }
    });
</script>

What I want to do

Something like

cp.updateColor('red');

or

$(".cp").ColorPickerSliders('updateColor', 'red');

Which one is the preferred way, and how to refactor the code to accomplish it?

I achieved it using triggers, but don’t know if it has any downsides or is a good way to do these kind of interaction. So I registered a custom event handler inside the plugin’s code (Line 320, triggerelement is the original element the plugin is called on):

triggerelement.on('jquerycolorpickersliders.updateColor', function(e, newcolor) {
    updateColor(newcolor);
});

And call it using this code (where cp is the element the plugin is called on):

cp.trigger('jquerycolorpickersliders.updateColor', 'red');

jsFiddle.

What do you think of it? Is it an acceptable solution for the problem, or is there a better one?

Solution

Firstly, underneath everything Javascript is an event driven language so I do not think that there is anything wrong with using an event driven design pattern if you are comfortable with it. Events are a great way to be able to interlink different components of your system without exposing the inner workings of them (loose coupling) and they add queuing by default. However they are more complex, they make it hard to trace behavior which in turn can make them hard to debug.

I think that you would get a cleaner interface by developing using something called the revealing module pattern, there is an excellent resource on various design patterns by a guy called Addy Osmani at adyosmanio.com and the revealing module pattern specifically here. This (or a flavor of it) is commonly used in jQuery plugin development which you can see on the jQuery advanced plugin development page (see Keep private functions private). By following these principles you can keep the private stuff private and only expose the functions that you want the calling code to have access to.

You mentioned specifically showPopup(), hidePopup() and updateColor(newColor) so a quick example of how that might look.

;(function($) {
    var ColorPickerSliders = function($element, options) {
         //if you want publicly modifiable configuration options.
         var defaults = {
            "opacity": 0,
            "hsl": 1,
            ...
        };

        function init() {
            ...
        }

        function showPopup()
        {
           ...
        }

        function hidePopup()
        {
           ...
        }

        function updateColor(newColor) {
            //uses private function
            var updatedcolor = tinyColor(newColor);
            ...

        }

        //private
        function tinyColor(newColor) {
            ...
        }

        //private
        function buildHtml()
        {
           ...
        }

        //this is the bit that makes your functions public
        //note that each function returns $element to preserve
        //chaining capabilities.
        return {
            "showPopup": function() {
                showPopup();
                return $element;
            },
            "hidePopup": function() {
                hidePopup();
                return $element;
            },
            "updateColor": function(newColour) {
                updateColor(newColour);
                return $element;
            }
        }
    };

    //the use of the data in here is to prevent the plugin being instantiated multiple times
    $.fn.colorPickerSliders = function(options) {
        return this.each(function() {
            if (!$(this).data('colorPickerSliders'))
                $(this).data('colorPickerSliders', new ColorPickerSliders(this, options));
        });
    };
})(jQuery)

Using the above you can then pull the plugin api from the element using:

$yourElement.data('colorPickerSliders').showPopup();

And you can chain:

$yourElement.data('colorPickerSliders').showPopup().delay(1000).hidePopup();

For reference you can also check this excellent article from Smashing Magazine (check the author) and related GitHub repository.

Leave a Reply

Your email address will not be published. Required fields are marked *