halilibrahimkalkan.com

personal and technical blog of Halil İbrahim Kalkan

I finished and published the article Introduction to ASP.NET Boilerplate. Vote it if you like :)


Hi,

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

I want to extend current functionallity 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 overrided method. The last one was the biggest problem.

So, I searched it, 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 font-size of an element. It is definetely not needed to write such a plug-in since it is just a line of code, but this is only demonstration of the technique :) Here is my widget:

/* '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 textFormatter widget:

<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 functionalitty. So, I want to add an option defaultColor, a method changeColor and override current _refreshText method to refresh text color in addition to font size. So, I must override _refreshText method. I will also override _create method (constructor of the widget). Here is the color extension:

/* '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 override them. So, I first defined an object named base to do that.

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

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

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

<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 functionallity to my widget: changing font-wight (to make a text bold, normal... etc.). Here the extension:

/* '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 plugin):

<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, initial screen:

When I click all of 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 codes and sample page.


Other pages

Month List