scope this in Jquery pattern "Lightweight" - javascript

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));

Related

How to access jQuery plugin settings in public plugin method?

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!

How to use JQuery Proxy with the JQuery Boilerplate

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

jQuery plugin boiler plate - private method with bound scope?

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);
}

Extending a JQuery Plugin and JQuery at the same time

I'm building a Jquery Plugin. My skeleton code is as below:
(function (window, document, $, undefined) {
var methods = {
init : function(options){
},
func_1: function(){
},
func_2: function(){
}
};
$.fn.myplugin = function(args){
if ( methods[args] )
{
return methods[ args ].apply( this, Array.prototype.slice.call( arguments, 1 ));
}
else if ( typeof args === 'object' || ! args )
{
var opts = $.extend({}, $.fn.myplugin.defaults, args);
var new_args = new Array(opts);
return methods.init.apply( this, new_args );
}
else
{
$.error( 'Method ' + args + ' does not exist' );
}
};
$.fn.myplugin.defaults = {
func_1: function(){},
func_2: function(){}
};
}(window, document, jQuery));
I'm looking to extend this plugin so that I may add additional functions to JQuery. So, I want to have these functions called as below:
$.myplugin.new_func();
How do I do this? I know I probably have to use $.extend but not sure how to go about it.
Thanks in advance.
The difference between $.fn.myplugin and $.myplugin is that the latter doesn't have any context. So you can define the latter using the following code. In order to use chaining on the result of $.myplugin you just need to return an object that you would like to use the .new_func() method on, for example a certain jQuery object.
$.myplugin = function () {
...
return $('body');
};
Ok, after going through some older JQuery plugins (FancyBox 2 especially), I managed to figure out a way to do this. Below is the entire skeleton code:
(function (window, document, $, undefined) {
var methods = {
init : function(options){
},
func_1: function(){
},
func_2: function(){
}
};
$.fn.myplugin = function(args){
if ( methods[args] )
{
return methods[ args ].apply( this, Array.prototype.slice.call( arguments, 1 ));
}
else if ( typeof args === 'object' || ! args )
{
var opts = $.extend({}, $.fn.myplugin.defaults, args);
var new_args = new Array(opts);
return methods.init.apply( this, new_args );
}
else
{
$.error( 'Method ' + args + ' does not exist' );
}
};
$.fn.myplugin.defaults = {
func_1: function(){},
func_2: function(){}
};
//below is the code I added to get the desired functionality
var D = $.myplugin = function(){};
$.extend(D, {
new_func: function(){
//add functionality here
}
});
}(window, document, jQuery));
It is also possible to define a function (called new_func or whatever) inside methods object, and then call it down below from your new function using methods.new_func().
Cheers.

How to make a public method in jQuery Plugin

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
});
}

Categories

Resources