Skip to content

Instantly share code, notes, and snippets.

@joeylin
Last active January 2, 2016 08:49
Show Gist options
  • Save joeylin/8278882 to your computer and use it in GitHub Desktop.
Save joeylin/8278882 to your computer and use it in GitHub Desktop.
a complete jQuery Plugin patterns
Event
1, event trigger flow: init -> ready -> other events(other events shouldn't be triggered before ready event).
2, event type format: plugin::eventType.
3, always pass a plugin instance to event as a param.
Callback
1, callback format: onCallback.
2, 'this' in callback direct to plugin instance.
// initialied plugin
$('.element').pluginName();
$('.element').pluginName({
skin:'your skin',
// config
.....
});
// callback method after initialied
$('.element').pluginName('disable'); // call disable method
$('.element').pluginName('val', 10); // call val method with 10 as a param
$('.element').pluginName('method',para1,para2,para3,...); // pass multi param
// another way to call method
var instance = $('.element').data('pluginName'); // get plugin instance
instance.val(); // excute method on instance directly
// what is return
$('.element').pluginName(); // always returns jquery element which plugin initialied on it
$('.element').data('pluginName'); // always returns plugin instance
// ----------------
// class name guide
// ----------------
namespace_statusClassName // this for status class name
namespace-componentClassName // this for compontent class name
namespace-component1-subcomponent
// here how to extend
// extend services / UI components
(function($, document, window, undefined) {
// Optional, but considered best practice by some
"use strict";
var $doc = $(document);
// this will be triggered everytime when instance is initialied
// Note: if you have multi instances, it will excute corresponding times
$doc.on('pluginName::init', function(event, instance) {
// do some stuff
});
$doc.on('pluginName::ready', function(event, instance) {
// do some stuff
});
})(jQuery, document, window);
(function($, document, window, undefined) {
// Optional, but considered best practice by some
"use strict";
var pluginName = 'defaultPluginName',
defaults = {
namespace: 'yourCustomNamespace',
skin: null,
// callback
onInit: null, // function() {};
onReady: null
};
var Plugin = $[pluginName] = function(element,options) {
this.element = element;
this.$element = $(element);
// get setting from element
this.options = $.extend( {}, defaults, options, this.$element.data()) ;
this._name = pluginName;
this.namespace = this.options.namespace;
this.classes = {
// status
skin: this.namespace + '_' + this.options.skin,
disabled: this.namespace + '_disabled',
// components --for example
wrapper: this.namespace + '-wrapper'
};
this.$element.addClass(this.namespace);
// flag
this.disabled = false;
this.initialed = false;
this.updating = false;
this.trigger('init');
this.init();
}
Plugin.prototype = function () {
constructor: Plugin,
init: function() {
this.createHtml();
this.bindEvent();
// skin : excuting after all elements are generated
if (this.options.skin) {
// add skin to wrapper
this.$element.addClass(this.classes.skin);
}
// set initialed value
// ...
this.initialed = true;
// after init end trigger 'ready'
this.trigger('ready');
},
bindEvent: function() {},
createHtml: function() {},
// some method
// .....
// some method
trigger: function(eventType) {
// event
this.$element.trigger(pluginName + '::' + eventType, this);
// callback
eventType = eventType.replace(/\b\w+\b/g, function(word){
return word.substring(0,1).toUpperCase() + word.substring(1);
});
var onFunction = 'on' + eventType;
var method_arguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : undefined;
if (typeof this.options[onFunction] === 'function') {
this.options[onFunction].apply(this, method_arguments);
}
},
update: function(config) {
this.updating = true;
// do some stuff
this.trigger('update');
this.updating = false;
},
enable: function() {
this.disabled = false;
// which element is up to your requirement
this.$element.removeClass(this.classes.disabled);
// here maybe have some events detached
},
disable: function() {
this.disabled = true;
// whitch element is up to your requirement
this.$element.addClass(this.classes.disabled);
// here maybe have some events attached
},
destory: function() {
// detached events first
// then remove all js generated html
this.$element.data(pluginName, null);
this.trigger('destory');
}
};
Plugin.defaults = defaults;
$.fn[pluginName] = function (options) {
if (typeof options === 'string') {
var method = options;
var method_arguments = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : undefined;
return this.each(function() {
var api = $.data(this, pluginName);
if (typeof api[method] === 'function') {
api[method].apply(api, method_arguments);
}
});
} else {
return this.each(function() {
if (!$.data(this, pluginName)) {
$.data(this, pluginName, new Plugin( this, options )););
}
});
}
};
})(jQuery, document, window);
(function($) {
/*
======== A Handy Little QUnit Reference ========
http://api.qunitjs.com/
Test methods:
module(name, {[setup][ ,teardown]})
test(name, callback)
expect(numberOfAssertions)
stop(increment)
start(decrement)
Test assertions:
ok(value, [message])
equal(actual, expected, [message])
notEqual(actual, expected, [message])
deepEqual(actual, expected, [message])
notDeepEqual(actual, expected, [message])
strictEqual(actual, expected, [message])
notStrictEqual(actual, expected, [message])
throws(block, [expected], [message])
*/
module('plugin#returnValue', {
// This will run before each test in this module.
setup: function() {
this.$elems = $('#qunit-fixture').plugin();
this.instance = this.$elems.data('plugin');
this.instance.test = function(value){
if (value) {
return value;
} else {
return 0;
}
}
}
});
test('is chainable', function() {
var value = 10;
var getValue = this.$elems.plugin('test',value);
equal(getValue, 0, 'should return 0');
var setValue = this.$elems.plugin('test',value);
equal(setValue, value, 'should be set value');
});
module('plugin#event', {
setup: function(){
var self = this;
this.$elems = $('#qunit-fixture').plugin({
onChange: function(value) {
self.mockValue = value;
}
});
this.instance = this.$elems.data('plugin');
}
});
test('change', function() {
this.$elems.on('plugin::change', function(instance) {
ok(1,'change event trigger success !');
equal(instance.value,10,'change event value ok');
});
this.instance.set(10);
equal(this.mockValue,10,'callback trigger success !');
});
test('update', function() {
this.$elems.on('plugin::update', function(instance) {
ok(1,'update event trigger success !');
});
this.$elems.on('plugin::change', function(instance) {
ok(0,'change event should not be triggered !');
});
this.instance.update();
});
}(jQuery));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment