Writing jQuery Plugins Jon Hartmann Most content shamelessly stolen from and
What is a Widget? Plugin Stateless Implemented by assigning to $.fn No extensibility – Need to modify it? Make a new plugin Manual Setup – Options – Methods – Callbacks Widget Stateful Special implementation code Extensibility pattern Automatic Setup – Options – Methods – Callbacks
Simple Widget $.widget('thd.progressbar', { _create: function() { var progress = this.options.value + '%'; this.element.addClass('progressbar').text(progress); } }); $(' ').appendTo('body').progressbar({ value: 20 }); Its called like this:
Widget Dissected $.widget('thd.progressbar', { _create: function() { var progress = this.options.value + '%'; this.element.addClass('progressbar').text(progress); } }); $.widget Constructor Widget name with namespace Widget configuration object this also has an options object for all options passed in through when the widget is invoked _create is standard invoked on widget call. this has access to the target element, but IS NOT the target element
Options Default Widget $.widget('thd.progressbar', { // Default Values options: { value: 0 }, _create: function() { var progress = this.options.value + '%'; this.element.addClass('progressbar').text(progress); } });
Adding methods $.widget('thd.progressbar', { //... // Create a public method. Calld as $('*').progressbar('value') value: function( value ) { //... }, // Create a private method. Called as this._constrain() _constrain: function( value ) { //... } }); Adding public methods easy as adding them to configuration object Private methods easy as using an underscore
Adding Callbacks $.widget('thd.progressbar', { //... _update: function() { var progress = this.options.value + '%'; this.element.text( progress ); if ( this.options.value == 100 ) { this._trigger('complete', null, { value: 100 } ); } }); Callbacks passed and set like options, code can trigger them even if not specified. Easily add callbacks where necessary. Always emits "custom" events!!
Widgets Bind Directly to DOM Useful because you can access the widget methods by the DOM – No need to call the $() selector engine again – Accessed via the.data() Also means you have a hook to override a specific instance's methods var bar = $( " ").appendTo( "body" ).progressbar().data( "progressbar" ); bar.option( "value", 50 ); alert( bar.options.value ); // Override bar.open = function () { console.log('Overridden!'); }; bar.open();
Widgets can be… DESTROY'ED! Automatically called when DOM element removed Helps clean up memory by removing references Cleans up HTML if you just want to remove the widget from the DOM element (no dangling classes) $.widget('thd.progressbar', { //... destroy: function() { this.element.removeClass( "progressbar" ).text( "" ); // Call the base destroy function. $.Widget.prototype.destroy.call( this ); } });
Widgets Have a Prototype You can extend the widget dynamically You can extend the object like any other object. – Is that the best way? – No. There is another Skywalker… err method. $.thd.progressbar.prototype.reset = function() { this._setOption( "value", 0 ); };
Widget can Extend Widgets BOOM! – Did you catch that? Built in inheritance $.widget('thd.awesomebar', $.thd.progressbar, { dazzle: function(onOff) { this.options.dazzleOn = onOff; this._update(); } }); $(' ').appendTo('body').awesomebar({ value: 20 });
Widgets can Override or Extend Methods Widgets implement "_super" to access parent class. $.widget('thd.awesomebar', $.thd.progressbar, { open: function() { console.log( "open" ); } }); $.widget('thd.awesomebar', $.thd.progressbar, { open: function () { console.log( "open" ); // Invoke the parent widget's open(). return this._super(); } });
Access to _super _super() accepts argument list like.call() _supperApply() accepts an array, like.apply() $.widget('thd.awesomebar', $.thd.progressbar, { _someFunction : function () { this._super( key, value ); // like.call() this._superApply( arguments ); // like.apply() } });
Cool Trick: Redefining Widget jQuery 1.9 lets you redefine a widget, which means this is valid: $.widget('thd.progressbar', $.thd.progressbar, { someMethod : function () { // Do something } }); Note that the name matches the original object definition.