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:
(function ($) {
var textFormatter = {
options: {
defaultFontSize: '12px'
},
_currentFontSize: null,
_create: function () {
this._currentFontSize = this.options.defaultFontSize;
},
_init: function () {
this._refreshText();
},
changeSize: function (size) {
this._currentFontSize = size;
this._refreshText();
},
_refreshText: function () {
$(this.element).css('font-size', this._currentFontSize);
}
};
$.widget('hik.textFormatter', textFormatter);
})(jQuery);
Let's see a sample usage of the 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 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:
(function ($) {
var base = {
_create: $.hik.textFormatter.prototype._create,
_refreshText: $.hik.textFormatter.prototype._refreshText
};
$.extend(true, $.hik.textFormatter.prototype, {
options: {
defaultColor: 'green'
},
_currentColor: null,
_create: function () {
base._create.apply(this, arguments);
this._currentColor = this.options.defaultColor;
},
changeColor: function (color) {
this._currentColor = color;
this._refreshText();
},
_refreshText: function () {
base._refreshText.apply(this, arguments);
$(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:
<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:
(function($) {
var base = {
_create: $.hik.textFormatter.prototype._create,
_refreshText: $.hik.textFormatter.prototype._refreshText
};
$.extend(true, $.hik.textFormatter.prototype, {
options: {
defaultFontWeight: 'normal'
},
_currentFontWeight: null,
_create: function() {
base._create.apply(this, arguments);
this._currentFontWeight = this.options.defaultFontWeight;
},
changeFontWeight: function(fontWeight) {
this._currentFontWeight = fontWeight;
this._refreshText();
},
_refreshText: function() {
base._refreshText.apply(this, arguments);
$(this.element).css('font-weight', this._currentFontWeight);
}
});
})(jQuery);
And here is a complete usage of my two times extended widget (jQueryUI plug-in):
<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.