1
\$\begingroup\$

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?

\$\endgroup\$

1 Answer 1

3
+50
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ I can agree that the interface is cleaner with the revealing module pattern, yet if I take the whole plugin, the version with the triggers seems simpler to me. What I really don't like is to call .data('pluginName') to access the methods, but I think it can be avoided with a little refactoring, so I will play with it some more. Bounty went! \$\endgroup\$
    – styu
    Commented Sep 7, 2013 at 11:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.