I'm creating a jQuery plugin using the following boilerplate:
https://github.com/jquery-boilerplate/jquery-boilerplate/blob/master/src/jquery.boilerplate.js
My question is, if I have additional public methods in my plugin, how do I access the plugin's settings (and other variables) from within that public method?
;(function ( $, window, document, undefined ) {
// Create the defaults once
var pluginName = "myplugin",
defaults = {
propertyName: "value"
};
// The actual plugin constructor
function Plugin ( element, options ) {
this.element = element;
this.settings = $.extend( {}, defaults, options );
this._defaults = defaults;
this._name = pluginName;
this.init();
}
var body = $('body');
Plugin.prototype = {
init: function () {
// Init code here
},
yourOtherFunction: function () {
// This is a private method
}
};
// Toggle menu opening
$.fn.doSomething = function(){
// How do I access the plugin's settings here? <<<<<<< Here is the issue
};
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[ pluginName ] = function ( options ) {
this.each(function() {
if ( !$.data( this, "plugin_" + pluginName ) ) {
$.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
}
});
// chain jQuery functions
return this;
};
})( jQuery, window, document );
So, after instaniating my plugin using $('.myelement').myplugin(); later I can do $('.myelement').doSomething(); and in that method, I need to be able to access the plugin's settings. How do I do that? this.settings didn't appear to work.
Is there a better alternative plugin boilerplate or is this one pretty standard?
That's a bad design. You are creating a completely new plugin. And there is no connection between myplugin and doSomething.
I would suggest you to do this:
$.fn[ pluginName ] = function ( options ) {
if(options == 'doSomething'){
// do something here or call some predefined function here
}
else{
this.each(function() {
if ( !$.data( this, "plugin_" + pluginName ) ) {
$.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
}
});
}
// chain jQuery functions
return this;
};
So, if you use the above construct, after calling $('.myelement').myplugin();, you can do:
$('.myelement').myplugin('doSomething'); //this is cool!
Related
I am currently in the process of writing a plugin using the JQuery Boilerplate. However I am having issues calling functions that are not called within the init() function. The boilerplate comments state that functions can be called via this.functionname() from within the init function but I'm unsure how to do this if they're called from elsewhere.
From looking at other questions on here it looks like I can use JQuery Proxy for this but can see no example of how to apply this to the boilerplate. Could someone show me, for instance how I could call this.createCarousel() or this.settings from within onEnterMobile please:
;(function ( $, window, document, undefined ) {
// Create the defaults once
var pluginName = "dcResponsiveCarousel",
defaults = {
itemsPerPageOnMobile: 1,
itemsPerPageOnTablet: 2,
itemsPerPageOnDesktop: 3
};
// The actual plugin constructor
function Plugin( element, options ) {
this.element = element;
$element = $(element);
this.settings = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
this.init();
}
Plugin.prototype = {
init: function() {
enquire
.register("screen and (max-width:767px)", this.onEnterMobile)
.register("screen and (min-width:768px) and (max-width:991px)", this.onEnterTablet)
.register("screen and (min-width:992px)", this.onEnterDesktop)
},
onEnterMobile: function (){
var deviceType = "mobile";
console.log(deviceType);
//this.createCarousel($element, itemsPerPage, deviceType);
}
}
};
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, "plugin_" + pluginName)) {
$.data(this, "plugin_" + pluginName,
new Plugin( this, options ));
}
});
};
})( jQuery, window, document );
OK after seeking some offline help on this issue I thought I'd post up an example of how JQuery Proxy can be used within JQuery Boilerplate:
init: function() {
var that = this;
enquire
.register("screen and (max-width:767px)", $.proxy(this.onEnterMobile,this))
.register("screen and (min-width:768px) and (max-width:991px)", $.proxy(this.onEnterTablet,this))
.register("screen and (min-width:992px)", $.proxy(this.onEnterDesktop,this))
},
So rather than just calling my callback function this.onEnterMobile I'm calling that function from within another "proxy" function which then allows me to pass through the value of the "this" keyword. This allows me to access my settings from any function in the plugin using this.settings or call any other function using this.functionName
I've been looking at the plugin boiler plate for jQuery plugins, I find it ok but there is one major flaw in the design or maybe just something I can't figure out.
When I author plugins at the moment, it is easy for me to define publicly exposed methods and private methods that only the plugin has access to.
When I tried to do something similar in the boiler plate I was thwarted.
;(function ( $, window, document, undefined ) {
// Create the defaults once
var
pluginName = "defaultPluginName",
defaults = {
propertyName: "value"
};
// The actual plugin constructor
function Plugin ( element, options ) {
this.element = element;
this.settings = $.extend( {}, defaults, options );
this.defaults = defaults;
this.name = pluginName;
this.init();
}
Plugin.prototype.init = function() {
console.log('init')
console.log(this)
this.yourOtherFunction();
}
Plugin.prototype.yourOtherFunction = function () {
console.log('yourOtherFunction')
console.log(this)
this.yourOtherFunction2();
}
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction().bind(this)
}
var privateFunction = function() {
console.log('private')
console.log(this)
}
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[ pluginName ] = function ( options ) {
return this.each(function() {
if ( !$.data( this, "plugin_" + pluginName ) ) {
$.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
}
});
};
})( jQuery, window, document );
$(document).defaultPluginName()
Anyway you can see the function 'privateFunction' it's scope is to the window object, but what I want to be able to do is scope it to the Plugin instance, or basically 'this' from the prototype methods.
What I don't want to do, is pass the scope into each private function as a function argument!
So how can I bind the scope?
Console output
init
Plugin { element=document, settings={...}, defaults={...}, more...}
yourOtherFunction
Plugin { element=document, settings={...}, defaults={...}, more...}
private
Window index.html <-- I want Plugin, not window
You are calling privateFunction and then binding this as scope for its result. So use (as said by #Khanh_TO):
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction.apply(this,arguments);
}
Instead of:
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction().bind(this)
}
More details:
bind returns a copy of the function on which is called (the result of privateFunction in your case) after applying the scope you've passed in (this in your case). What bind does is something like:
Function.prototype.bind = function(scope) {
var _function = this;
var _args = [];
for (var i = 0, len = arguments.length-1; i < len; i++){ _args[i] = arguments[i+1]; }
return function() {
// returns the same function on which is called (not the same Function object, but
// another with same properties) with 'this' equal to the first parameter and
// the remaining specified parameters as parameters of the function returned
return _function.apply(scope, _args);
}
}
eg. myFunction.bind(newScope, param1, param2, ...) -> returns an anonymous function which in turns returns the function myFunction(param1, param2,....) with set this = newScope.
So, as a proof of concept, also this code would have worked:
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction.bind(this)();
}
but you should use the first one since the last one does the same thing with extra passages.
Replace:
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction().bind(this)
}
With
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction.apply(this,arguments);
}
I hope that you understand my english.
I try use Jquery pattern "Lighrweight" (http://coding.smashingmagazine.com/2011/10/11/essential-jquery-plugin-patterns/)
But I have a problem for call method and the scope of this.
I bind plugin on table :
$("#tableSurface0").flexTab();
$("#tableSurface1").flexTab();
My Jquery plugin :
;(function ( $, window, document, undefined ) {
var pluginName = 'flexTab',
defaults = {
wrapOverflowHeight : true
};
// The actual plugin constructor
function Plugin( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options) ;
this.init();
}
Plugin.prototype.init = function () {
$("th:not([noflex])", this.element).on("mousedown", menuContextuel);
};
menuContextuel = function(event)
{
//BUG
console.log( ?? ); // show this.element of constructor
}
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName,
new Plugin( this, options ));
}
});
}
})( jQuery, window, document );
So I can not call this.element in menuContextuel function without add data in event handler :
Plugin.prototype.init = function () {
$("th:not([noflex])", this.element).on("mousedown", { table:this.element }, menuContextuel);
};
...
menuContextuel = function(event)
{
console.log( event.data.table );
}
There-he has another solution ?
Thank you
It is because the function is being called and the "this" is the element that is clicked on. Use can use jQuery's proxy so maintain the scope of "this" that you are after.
$("th:not([noflex])", this.element).on("mousedown", $.proxy(menuContextuel,this));
I've been using the below boilerplate for jQuery Plugin development which has been working great. But I'm not sure of the best way to make a "method" public.
;(function ( $, window, undefined ) {
var pluginName = 'defaultPluginName',
document = window.document,
defaults = {
propertyName: "value"
};
function Plugin( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
this.init();
}
Plugin.prototype.init = function () {
};
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
}
});
}
}(jQuery, window));
Thanks
Only functions can be private / local, not a jQuery Plugin.
If you are using jQuery and trying to add your own (custom) function in jQuery, there are a best way jQuery.fn.yourFunctionname = function(){}, since jQuery is already in public scope so jQuery.fn.yourFunctionname is a public scope (no matter where you define your Plugin , will available for global use).
window.jQuery = jQuery;
$.fn.pluginName = function ( options ) {
return this.each(function () {
// code goes here
});
}
I am trying to write a good jQuery Plugin structure. I am trying to follow "best practices" from jQuery.com and others.
But I am little bit confused about prototype.
Should I use it or not? And Is the actual structure looks good or terrible?
Thanks !
(function( $ ){
var defaults = { /* ... */ },
publicMethods = {
add: function(options){
var $this = $(this);
// ...
return $this;
}
},
privateMethods = {
init: function(options) {
var $this = $(this);
return $this;
},
click: function() {
//...
}
};
$.fn.tooltip = function(method) {
var args = arguments;
$(this).each(function() {
if ( publicMethods[method] ) {
return publicMethods[ method ].apply( this, Array.prototype.slice.call( args, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return privateMethods.init.apply( this, args );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
}
});
};
})( jQuery );
With respect to using the .prototype of the tooltip function, that would be useful only if the tooltip function is going to be invoked as a constructor.
Typically as a jQuery plugin, you're only interested in using the object created from the jQuery constructor, rather than creating your own objects from the plugin function.
There may be occasion to use a constructor somewhere internally in your plugin code, but it typically wouldn't be the actual plugin function itself.