jQuery - plugin methods call - javascript

I'm trying to create a jQuery plugin following some official best practices
(function($){
var methods = {
init : function( options ) {
this.options = options;
}
, add_that: function (elem) {
this.append(elem);
return (this);
}
, add_this: function (elem) {
return (methods.add_that(elem));
}
};
$.fn.test = function (method) {
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.test' );
}
};
})(jQuery);
I'd like the method add_that to be able to append things to the matched element(s).
Now this method is called from add_this.
$('#test').test('add_this', $('<div />'));
TypeError: this.append is not a function
Why can't I access to the plugin (this) from add_that ?

Because the scope has changed when you've called it from add_this. Notice that the original call used Function.apply to scope the call to this.
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
}
So you can presumably get around the issue by using apply again in your method:
add_this: function (elem) {
return methods.add_that.apply(this,[elem]);
}
Live example: http://jsfiddle.net/XaUHV/

In the context you're using it "this" refers to methods.add_that
Since methods.add_that is a function there is no method on it called "append" by default.

Related

How does jquery proxy work

I am more curious than anything else. How does it pass the context to the function. Does it wrap the function in the object? I am sure there is some simple straightforward code for doing this in js without jquery proxy
function abc(){
console.log(this.name);
}
var obj={name:"Something"};
$.proxy(abc,obj);
How can I do this without jquery proxy?
Without jQuery you may use bind :
var newFunction = abc.bind(obj);
And if you want to be compatible with IE8, you may do
var newFunction = function(){ abc.call(obj) };
Here's how jQuery does it :
// Bind a function to a context, optionally partially applying any
// arguments.
proxy: function( fn, context ) {
var args, proxy, tmp;
if ( typeof context === "string" ) {
tmp = fn[ context ];
context = fn;
fn = tmp;
}
// Quick check to determine if target is callable, in the spec
// this throws a TypeError, but we will just return undefined.
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// Simulated bind
args = core_slice.call( arguments, 2 );
proxy = function() {
return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
};
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
return proxy;
},

Cannot extend my javascript object with Prototype

I have this code:
var Flippable = function( options, element ) {
this.$el = $( element );
this._init( options );
};
Flippable.prototype = {
_init : function( options ) {
this.options = $.extend( true, {}, $.Flips.defaults, options );
this.$pages = this.$el.children( 'div.f-page' );
this.pagesCount = this.$pages.length;
this.History = window.History;
this.currentPage = this.options.current;
this._validateOpts();
this._getWinSize();
this._getState();
this._layout();
this._initTouchSwipe();
this._loadEvents();
this._goto();
},
foo: function(){alert("foo");}
}
But when I call Flipppable.foo(), I get undefined. Any ideas where my syntax is off?
Thanks!
Chris
** Update **
I"m creating a plugin like so:
$.fn.flippable = function( method ) {
// Method calling logic
if ( Flippable[method] ) {
return Flippable[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return Flippable._init.apply( this, arguments );
Uncaught TypeError: Cannot call method 'apply' of undefined
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.flippable' );
}
};
It borks on line:
return Flippable._init.apply( this, arguments );
_init is undefined.
Prototype functions are accessible only through the object of the class. And not the class itself.
This would work.
var oFlip = new Flippable();
oFlip.foo();
The way you are trying to call foo using .apply you will have to do this.
Flippable.prototype.foo.apply( this, arguments );
The object which has a foo property is prototype on Flippable and not Flippable itself.

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 create a Jquery Plugin with Plugin Methods and Maintaining Chainability?

I am trying to create a Jquery plugin that maintains chainability and has public methods as specified in Jquery Plugins/Authoring . The complexity is that it is trying to maintain certain vars that I want the public methods to use.
This is my jsfiddle : http://jsfiddle.net/badmash69/9cqcj/2/
javascript code :
(function($){
var methods = {
init : function( options ) {
this.options = options;
}
, add_that: function (elem) {
$(this).append(elem);
return (this);
}
, show_parent: function(){
// this is a simple test to see if the plugin vars are accessible
alert("parent id=" + $(this).parentId)
}
, add_this: function (elem) {
return methods.add_that.apply(this,elem);
}
};
$.fn.test = function (method) {
var args = method;
var argss = Array.prototype.slice.call(args, 1);
return this.each(function(){
var $this = $(this);
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.test' );
}
var element = $(this);
var parentId= element.parent().attr("id")
});
};
})(jQuery);
$('#test').test('add_this',$('<div>Hello World d</div>'));
$('#test').test('show_parent');
​
Html Code
<div id="holder">
<div id="test"></div>
</div>
I cant figure out what I am doping wrong here .
How can I make it work ? I would deeply appreciate any help .
the way that I do this is using the $.data, you can have specific object local vars, "public"/"private" methods, etc. here goes an small example in how I will do it
(function($){
var myTestMethods = function() {
// local variables
var last_added;
// local "private" methods
var init=function(options) {
this.options = options;
last_added = null;
return this;
};
var add_that=function(elem) {
last_added = elem;
this.append(elem);
return this;
};
var show_parent=function() {
alert("parent id=" + this.parent().attr('id'));
}
return { // this are your obj "public" methods
// notice we are not listing add_that method, therefore this method will be a "private" method
init : init,
show_parent: show_parent, // you can publish a private method
get_last_added: function(){
return last_added; // you can access local variables
},
add_this: function (elem) {
return add_that.apply(this, elem); // you can also run local methods
}
}
};
$.fn.test = function (method) {
var obj_data = this.data('myTestData');
if (typeof(obj_data) != "undefined") {
if ( obj_data[method] ) {
return obj_data[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
}else {
$.error( 'Method ' + method + ' does not exist on jQuery.test' );
}
} else {
if (typeof(method) === 'object' || ! method) {
obj_data = myTestMethods();
this.data('myTestData', obj_data);
return obj_data.init.apply(this, arguments);
}
}
};
})(jQuery);
$('#test').test(); //init
$('#test').test('add_this',$('<div>Hello World d</div>'));
$('#test').test('show_parent');
this code has small tests so there may be small bugs, but this will show you the basic idea in how to do what you want.
Take a look at this demo: http://jsfiddle.net/9cqcj/11/
As they suggest, to keep data you should better use .data:
return this.each(function(){
var $this = $(this);
$this.data("parentId",$this.parent().attr("id"));
....
(assuming that you need parentId of each element in set)
Also, you have a problem with calling your methods:
return this.each(function(){
var $this = $(this);
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
Last line, arguments - arguments of function passed to .each is used. In order to get original arguments save them into variable before calling a method:
$.fn.test = function (method) {
var args = arguments;
return this.each(function(){
var $this = $(this);
$this.data("parentId",$this.parent().attr("id"));
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( args , 1 ));
See arguments replaced with args in last line.
Also, when you are using .apply, second parameter should be an array:
return methods.add_that.apply(this, [elem]);
In case like this:
return methods.add_that.apply(this, elem);
You can get unexpected problems. For instance, try to replace elem with simple string "test" and see what you will get in console. Or if you will pass jQuery object, you will get DOM object in called method

Best jQuery Plugin Structure?

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.

Categories

Resources