Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Extensible jQueryUI Widget Development

5.00/5 (3 votes)
27 Nov 2011CPOL2 min read 17K  
Extensible jQueryUI widget development

Today, I spent a lot of time investigating a way to extend a jQueryUI widget.

I want to extend the current functionality of a widget without changing the main widget code. Also, I don't want to inherit it and create a new widget. I just want to add new options, private fields, and methods. I also want to override some existing methods, but I want to call the base method in the overridden method. The last one was the biggest problem.

So, I searched for some solutions; some solutions were good but there was not a complete solution. Then, I combined some solutions and added my own technique and reached a good solution.

Here, I'll explain how I did it.

First, I created a simple widget. It is used to change the font-size of an element. It is definitely not needed to write such a plug-in since it is just a line of code, but this is only a demonstration of the technique. :) Here is my widget:

JavaScript
/* 'textFormatter' widget (just changes 'font-size') */
(function ($) {

    //Core text formatter object
    var textFormatter = {

        //Default values for options
        options: {
            defaultFontSize: '12px'
        },

        //Private fields
        _currentFontSize: null,

        //Constructor
        _create: function () {
            this._currentFontSize = this.options.defaultFontSize;
        },

        //Initializer
        _init: function () {
            this._refreshText();
        },

        //Public functions
        changeSize: function (size) {
            this._currentFontSize = size;
            this._refreshText();
        },

        //Private functions
        _refreshText: function () {
            $(this.element).css('font-size', this._currentFontSize);
        }
    };

    //Create the widget
    $.widget('hik.textFormatter', textFormatter);

})(jQuery);

Let's see a sample usage of the textFormatter widget:

HTML
<div id="TestDiv">A text for test</div>
<script type="text/javascript">

    $('#TestDiv').textFormatter({
        defaultFontSize: '14px'
    });

    $('#TestDiv').textFormatter('changeSize', '20px');

</script>

It is simple. Now, I want to add color changing functionality. I want to add an option defaultColor, a method changeColor and override the current _refreshText method to refresh the text color in addition to the font size. So, I must override the _refreshText method. I will also override the _create method (constructor of the widget). Here is the color extension:

JavaScript
/* 'color' extension for 'textFormatter' widget */
(function ($) {

    //References to base methods
    var base = {
        _create: $.hik.textFormatter.prototype._create,
        _refreshText: $.hik.textFormatter.prototype._refreshText
    };

    //Extend the 'textFormatter' widget with 'color'
    $.extend(true, $.hik.textFormatter.prototype, {

        //Default values for options
        options: {
            defaultColor: 'green'
        },

        //Private fields
        _currentColor: null,

        //Constructor (overrides base)
        _create: function () {
            base._create.apply(this, arguments); //call base
            this._currentColor = this.options.defaultColor;
        },

        //Public functions
        changeColor: function (color) {
            this._currentColor = color;
            this._refreshText();
        },

        //Private functions (overrides base)
        _refreshText: function () {
            base._refreshText.apply(this, arguments); //call base
            $(this.element).css('color', this._currentColor);
        }
    });

})(jQuery);

I must store references to base methods before overriding them. So, I first defined an object named base to do that.

Then I called the jQuery.extend method with deep=true (first parameter). Thus, it will merge options with the base widget (not overwrite). It also adds all defined methods to the textFormatter widget.

As you know, the jQuery.extend method overwrites the same named members (such as the _refreshText and _create methods). That's why I created the base object. See the _refreshText method. I called the base method as base._refreshText.apply(this, arguments);. It's very simple and that's all!

Now, I can call the new changeColor method and the defaultColor option as shown below:

JavaScript
<div id="TestDiv">A text for test</div>
<script type="text/javascript">

    $('#TestDiv').textFormatter({
        defaultFontSize: '14px',
        defaultColor: 'blue'
    });

    $('#TestDiv').textFormatter('changeSize', '20px');
    $('#TestDiv').textFormatter('changeColor', 'red');

</script>

It was clear and simple. Now, I want to add a new functionality to my widget: changing font-weight (to make a text bold, normal, etc.). Here's the extension:

JavaScript
/* 'font-weight' extension for 'textFormatter' widget */
(function($) {

    //References to base methods
    var base = {
        _create: $.hik.textFormatter.prototype._create,
        _refreshText: $.hik.textFormatter.prototype._refreshText
    };

    //extension object
    $.extend(true, $.hik.textFormatter.prototype, {

        //Default values for options
        options: {
            defaultFontWeight: 'normal'
        },

        //Private fields
        _currentFontWeight: null,

        //Constructor (overrides base)
        _create: function() {
            base._create.apply(this, arguments); //call base
            this._currentFontWeight = this.options.defaultFontWeight;
        },

        //Public functions
        changeFontWeight: function(fontWeight) {
            this._currentFontWeight = fontWeight;
            this._refreshText();
        },

        //Private functions (overrides base)
        _refreshText: function() {
            base._refreshText.apply(this, arguments); //call base
            $(this.element).css('font-weight', this._currentFontWeight);
        }
    });

})(jQuery);

And here is a complete usage of my two times extended widget (jQueryUI plug-in):

XML
<div id="TestDiv">A text for test</div>

<div>
    <div>
        Enter new font size:
        <input type="text" id="NewFontSize" value="20px" />
        <button id="ChangeFontSize">Change font size</button>
    </div>
    <div>
        Enter new color:
        <input type="text" id="NewColor" value="red" />
        <button id="ChangeColor">Change font color</button></div>
    <div>
        Enter new font weight:
        <input type="text" id="NewFontWeight" value="bold" />
        <button id="ChangeFontWeight">Change font weight</button>
    </div>
</div>

<script type="text/javascript">

    $('#TestDiv').textFormatter({
        defaultFontSize: '14px',
        defaultColor: 'blue',
        defaultFontWeight: 'regular'
    });

    $('#ChangeFontSize').click(function () {
        $('#TestDiv').textFormatter('changeSize', $('#NewFontSize').val());
    });

    $('#ChangeColor').click(function () {
        $('#TestDiv').textFormatter('changeColor', $('#NewColor').val());
    });

    $('#ChangeFontWeight').click(function () {
        $('#TestDiv').textFormatter('changeFontWeight', $('#NewFontWeight').val());
    });
    
</script>

Here is the initial screen:

When I click all of the buttons, the text will be red, bold, and larger as shown below:

I hope it was useful for you. I'll use this pattern in my jTable project.

Click here to download the code and see the sample page.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)