Converting jQuery Code to a Plugin

Demo Download

When it comes to efficiently organizing jQuery code, one of the best options is turning certain parts of it into a plugin. There are many benefits to this - your code becomes easier to modify and follow, and repetitive tasks are handled naturally. This also improves the speed with which you develop, as plugin organization promotes code reuse.

This is why today we are going to demonstrate the process of converting code to a plugin. We are going to take the code from our jQuery & CSS3 Select Replacement tutorial, and turn it into a ready for use jQuery plugin.

The Idea

Writing a jQuery plugin is not at all difficult. You need to extend the $.fn object with your own function. What is more difficult, however, is finding a way to properly structure your code so that your plugin is easy to embed and use, without dependencies.

Here are several problems that we need to solve when converting the tutorial code into a jQuery plugin:

  1. We need to give users the ability to control what markup is generated for the dropdown. For example the tutorial code relies heavily on the presence of data- attributes, which contain HTML markup. This is too specific to be included into a plugin, so we need to leave it out of the implementation;
  2. Because of the way plugins are called, we need to rewrite the code so it uses the "this" object that is passed to the plugin, instead of hard-coding a selector. This will also make it possible to convert more than one select element at once;
  3. We need to extract the JavaScript and CSS code of the plugin into separate files, so it is easy to embed and redistribute.

The Code

As you remember from the tutorial, our jQuery code scans the select's option elements and builds an unordered list. In the process it also looks for a number of data- attributes in the options that contain an image URL and a description to use in the list.

This is, however, too specific for a plugin. We need to give users the ability to override this functionality. To solve the problem, we can allow users to pass a function as a parameter to the plugin, which will generate the markup instead. If such a parameter is not passed, we will use fall back to a default one, which basically takes the text of the option element and turn it directly into a list item.

Lets put this into code:

(function($){

    $.fn.tzSelect = function(options){
        options = $.extend({
            render : function(option){
                return $('<li>',{
                    html : option.text()
                });
            },
            className : ''
        },options);

    // More code will be added here.

    }
})(jQuery);

The render function takes an option element (the kind that is contained in a select), and returns a li element, which is directly included in the drop down list by the plugin. This solves problem #1 outlined above.

jquery-css3-select-replacement-plugin.jpg

Before we move on with solving problem #2, lets see how our plugin is going to be called:

$(document).ready(function(){
    $('select').tzSelect();
});

In the example code above, you can see that we are applying the plugin to every select element on the page. We can access these elements by traversing the "this" object that is passed to the plugin:

return this.each(function(){

            // The "this" points to the current select element:

            var select = $(this);

            var selectBoxContainer = $('<div>',{
                width       : select.outerWidth(),
                className   : 'tzSelect',
                html        : '<div class="selectBox"></div>'
            });

            var dropDown = $('<ul>',{className:'dropDown'});
            var selectBox = selectBoxContainer.find('.selectBox');

            // Looping though the options of the original select element

            if(options.className){
                dropDown.addClass(options.className);
            }

            select.find('option').each(function(i){
                var option = $(this);

                if(i==select.attr('selectedIndex')){
                    selectBox.html(option.text());
                }

                // As of jQuery 1.4.3 we can access HTML5
                // data attributes with the data() method.

                if(option.data('skip')){
                    return true;
                }

                // Creating a dropdown item according to the
                // data-icon and data-html-text HTML5 attributes:

                var li = options.render(option);

                li.click(function(){

                    selectBox.html(option.text());
                    dropDown.trigger('hide');

                    // When a click occurs, we are also reflecting
                    // the change on the original select element:
                    select.val(option.val());

                    return false;
                });

                dropDown.append(li);
            });

            selectBoxContainer.append(dropDown.hide());
            select.hide().after(selectBoxContainer);

            // Binding custom show and hide events on the dropDown:

            dropDown.bind('show',function(){

                if(dropDown.is(':animated')){
                    return false;
                }

                selectBox.addClass('expanded');
                dropDown.slideDown();

            }).bind('hide',function(){

                if(dropDown.is(':animated')){
                    return false;
                }

                selectBox.removeClass('expanded');
                dropDown.slideUp();

            }).bind('toggle',function(){
                if(selectBox.hasClass('expanded')){
                    dropDown.trigger('hide');
                }
                else dropDown.trigger('show');
            });

            selectBox.click(function(){
                dropDown.trigger('toggle');
                return false;
            });

            // If we click anywhere on the page, while the
            // dropdown is shown, it is going to be hidden:

            $(document).click(function(){
                dropDown.trigger('hide');
            });

        });

The fragment above almost identical with the tutorial code we are converting today. One notable change is that we assign $(this) to the select variable (line 5), which used to be $('select.makeMeFancy') (a hardcoded selector), which significantly limited the scope of the code.

The other change is that instead of directly generating the drop down list, we are calling the render function that was passed as a parameter (line 51).

When we combine the above, we get the complete source code of the plugin:

tzSelect/jquery.tzSelect.js

(function($){

    $.fn.tzSelect = function(options){
        options = $.extend({
            render : function(option){
                return $('<li>',{
                    html : option.text()
                });
            },
            className : ''
        },options);

        return this.each(function(){

            // The "this" points to the current select element:

            var select = $(this);

            var selectBoxContainer = $('<div>',{
                width       : select.outerWidth(),
                className   : 'tzSelect',
                html        : '<div class="selectBox"></div>'
            });

            var dropDown = $('<ul>',{className:'dropDown'});
            var selectBox = selectBoxContainer.find('.selectBox');

            // Looping though the options of the original select element

            if(options.className){
                dropDown.addClass(options.className);
            }

            select.find('option').each(function(i){
                var option = $(this);

                if(i==select.attr('selectedIndex')){
                    selectBox.html(option.text());
                }

                // As of jQuery 1.4.3 we can access HTML5
                // data attributes with the data() method.

                if(option.data('skip')){
                    return true;
                }

                // Creating a dropdown item according to the
                // data-icon and data-html-text HTML5 attributes:

                var li = options.render(option);

                li.click(function(){

                    selectBox.html(option.text());
                    dropDown.trigger('hide');

                    // When a click occurs, we are also reflecting
                    // the change on the original select element:
                    select.val(option.val());

                    return false;
                });

                dropDown.append(li);
            });

            selectBoxContainer.append(dropDown.hide());
            select.hide().after(selectBoxContainer);

            // Binding custom show and hide events on the dropDown:

            dropDown.bind('show',function(){

                if(dropDown.is(':animated')){
                    return false;
                }

                selectBox.addClass('expanded');
                dropDown.slideDown();

            }).bind('hide',function(){

                if(dropDown.is(':animated')){
                    return false;
                }

                selectBox.removeClass('expanded');
                dropDown.slideUp();

            }).bind('toggle',function(){
                if(selectBox.hasClass('expanded')){
                    dropDown.trigger('hide');
                }
                else dropDown.trigger('show');
            });

            selectBox.click(function(){
                dropDown.trigger('toggle');
                return false;
            });

            // If we click anywhere on the page, while the
            // dropdown is shown, it is going to be hidden:

            $(document).click(function(){
                dropDown.trigger('hide');
            });

        });
    }

})(jQuery);

Placing this plugin in a separate file solves problem #3. However, as I mentioned previously, we intentionally left out the code that uses the data- attributes in order to make the plugin more portable. To compensate, we need to pass a custom render function when calling the plugin, as you can see below (this is also the code that is used in the demo).

script.js

$(document).ready(function(){

    $('select.makeMeFancy').tzSelect({
        render : function(option){
            return $('<li>',{
                html:   '<img src="'+option.data('icon')+'" /><span>'+
                        option.data('html-text')+'</span>'
            });
        },
        className : 'hasDetails'
    });

    // Calling the default version of the dropdown
    $('select.regularSelect').tzSelect();

});

With this our jQuery plugin is complete!

You can use this plugin by simply dropping the tzSelect folder (available from the download button above) into your project root, and including jquery.tzSelect.css and jquery.tzSelect.js in your HTML documents.

Wrapping Up

Following these simple steps, you can easily turn a mess of jQuery code into a structured and ready for reuse plugin. Sure, it does take a bit of work, but the effort would pay out many times in the long run.

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles