What is a way to make JQuery plugin extendible? - javascript

I try to move some common application specific actions to jQuery plug-in by:
$.fn.extpoint = function() {...}
But I don't want to declare several extension points:
$.fn.extpoint1 = function() {...}
$.fn.extpoint2 = function() {...}
...
Instead I would like to use syntax sugar like:
$("#id").extpoint.func1().extpoint.func2()
With definition:
$.fn.extpoint = {}
$.fn.extpoint.func1 = function() {
this.val();
this.data("ip");
...
return this;
}
and call:
$("#id").extpoint.func1(...)
this point to $.fn.extpoint (dictionary with func1, func2, ... elements) instead of original jQuery object, when func1 evaluated.
Is it possible to make jQuery plug-in extendible?
PS. It is possible to pass function name as first argument to $.fn.extpoint and implement $.fn.extpoint('extend', func) call to extend (save to internal dictionary association between names and implementations) extension point. In that case use-cases look like:
$("#id").extpoint('func1', ...).extpoint('func2', ...)
but I look for way to make in more syntactic sugar...

The task I ask is hard to implement.
Official docs say:
Under no circumstance should a single plugin ever claim more than one namespace in the jQuery.fn object:
(function( $ ){
$.fn.tooltip = function( options ) {
// THIS
};
$.fn.tooltipShow = function( ) {
// IS
};
$.fn.tooltipHide = function( ) {
// BAD
};
})( jQuery );
This is a discouraged because it clutters up the $.fn namespace. To remedy this, you should collect all of your plugin's methods in an object literal and call them by passing the string name of the method to the plugin.
Another approach is maintain link to this as in http://code.google.com/p/jquery-plugin-dev/source/browse/trunk/jquery.plugin.js
So your calls looks like:
$.fn.addPlugin('test2', {
__construct : function(alertText) { alert(alertText); },
alertAttr : function(attr) { alert($(this).attr(attr)); return this; },
alertText : function() { alert($(this).text()); return this; }
});
$('#test2').bind('click', function() {
var btn = $(this);
btn.test2('constructing...').alertAttr('id').alertText().jQuery.text('clicked!');
setTimeout(function() {
btn.text('test2');
}, 1000);
});
Some related links:
http://milan.adamovsky.com/2010/02/how-to-write-advanced-jquery-plugins.html
http://milan.adamovsky.com/2010/09/jquery-plugin-pattern-20.html
http://ludw.se/blog/articles/19/patching-milans-jquery-plugin-pattern-for-jquery-16
http://code.google.com/p/jquery-plugin-dev/source/browse/trunk/jquery.plugin.js
Old style plug-in extention:
http://docs.jquery.com/Plugins/Authoring
jQuery Plugin Authoring and Namespacing
http://coding.smashingmagazine.com/2011/10/11/essential-jquery-plugin-patterns/
http://mahtonu.wordpress.com/2010/09/20/jquery-plugin-authoring-step-by-step/
http://www.capricasoftware.co.uk/corp/template.php

Here is an overview of creating a plugin. I believe what you are asking about is called "chaining". It is what makes jQuery so easy to use, and it's good that you want to make sure that you are implementing it correctly.
The key thing to remember while developing your plugin in regards to chaining is to always return this; from your methods. That is what will allow you to keep the chain going.

Related

Can you use a JS object as the interface for a JQuery plugin?

I've had a look at this thread: How to create a jQuery plugin with methods?, and while there are plenty of solutions regarding multi-function plugins, I'm interested if it's possible to use an object so that you don't have ugly constructions like $("selector").plugin().dostuff(), and instead can use it more like this: $("selector").plugin.dostuff(), ie 'plugin' is an object not a function.
I can write the plugin like this:
$.fn.plugin = {
dostuff: function() {},
domorestuff: function(){}
};
But then the inner functions won't have access to the JQuery object that's calling it in the first place, as this returns the plugin object. Is there any way to structure a plugin based around an object that would work with JQuery?
No, and for the reason you gave: You lose this, and this is hugely important when writing a plugin. To get this, your plugin must be a function.
You basically have three ways of implementing methods:
Using string arguments, e.g.:
$("selector").plugin("dostuff");
...where you dispatch to your handler methods within the plugin function. This is by far the most popular way. It's the way used by jQuery UI, Bootstrap, and others. Full example of this below.
Using the "ugly construction" as you put it, where your plugin is a function and it returns an object (a new object each time, to preserve this) with functions you can call.
Adding several plugin methods to jQuery rather than just one, using a prefix of some kind, so it ends up being $("selector").pluginDoThis(); and $("selector").pluginDoThat(); and $("selector").pluginDoTheOther(); The key is to use a prefix to avoid name conflicts. I've only seen this done once, a long time ago, and I haven't seen that plugin in a while. Not really a popular choice.
I just recently posted this other answer showing the full pattern for #1 (in their case, the methods were callThis and destroy), e.g.:
// Create the plugin
(function ($) {
var methods = {
init: function(options) {
// Determine options
var opts = $.extend({
opacity: 0.5
}, options);
// Remember them
this.data("pluginname", opts);
// Initial stuff
this.css("opacity", opts.opacity);
},
callThis: function(opts) {
// Use 'opts' if relevant
this.css("display","none");
},
destroy: function(opts) {
this.removeData("pluginame");
this.css("display", "").css("opacity", "");
}
};
jQuery.fn.pluginname = function (options) {
var method, args;
// Method?
if (typeof options === "string") {
// Yes, grab the name
method = options;
// And arguments (we copy the arguments, then
// replace the first with our options)
args = Array.prototype.slice.call(arguments, 0);
// Get our options from setup call
args[0] = this.data("pluginname");
if (!args[0]) {
// There was no setup call, do setup with defaults
methods.init.call(this);
args[0] = this.data("pluginname");
}
}
else {
// Not a method call, use init
method = "init";
args = [options];
}
// Do the call
methods[method].apply(this, args);
};
})(jQuery);
// Example usage
function doInit() {
$("#target").pluginname();
setTimeout(doCallThis, 700);
}
function doCallThis() {
$("#target").pluginname("callThis");
setTimeout(doDestroy, 700);
}
function doDestroy() {
$("#target").pluginname("destroy");
setTimeout(doInit, 700);
}
setTimeout(doInit, 700);
<div id="target">This is the target</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

JS $('x').myfunction is not a function

I am ciruious to know what are the reasons that could cause a function "not being" one.
For example, I have the following:
$.fn.myfunction = function(options){
alert("test");
};
$("#hello").myfunction();
alert($.fn.myfunction instanceof Function);
Why would FireBug, log that it is not a function?
Thanks in advance.
Edit:
I would like a list of all the possibilities that could be the reason for the error.
This isn't an error I got, but I just want to widen my perspective and be aware of more possibilities.
Setting $.fn.myfunction makes the myfunction function available to the object returned by $(selector), not to $ itself.
Thus, $("#hello").myfunction is a function but $.myfunction is not. If you really want $.myfunction to be a function for some reason (e.g., it's a utility function that doesn't need a jQuery object list to operate), just set it explicitly, without using $.fn:
$.myfunction = function() { .... }
What is $ in the context? jQuery perhaps? If it´s jQuery you´re using, please tag your question as such.
(function( $ ) {
$.fn.myPlugin = function() {
return this.each(function() { // Maintaining chainability
var $this = $(this);
// Do your awesome plugin stuff here
console.log($this); // TEMP
});
};
})( jQuery );
Usage: $('#hello').myPlugin();
See more information about jQuery plugin authoring.
adding parentheses seemed to work:
alert($().myfunction instanceof Function);
returns true.

jQuery plugin namespace for specific objects

I'm am trying to create a jQuery plugin that will add new namespace functions to the context object(s), while maintaining full chain-ability. I'm not sure if it's possible, but here's an example of what I have so far:
(function ($) {
var loadScreen = $('<div />').text('sup lol');
$.fn.someplugin = function (args) {
var args = args || {},
$this = this;
$this.append(loadScreen);
return {
'caption' : function (text) {
loadScreen.text(text);
return $this;
}
};
}
})(jQuery);
This works fine if I do $(document.body).someplugin().caption('hey how\'s it going?').css('background-color', '#000');
However I also need the ability to do $(document.body).someplugin().css('background-color', '#000').caption('hey how\'s it going?');
Since .someplugin() returns it's own object, rather than a jQuery object, it does not work as expected. I also need to be able to later on access .caption() by $(document.body). So for example if a variable is not set for the initial $(document.body).someplugin(). This means that somehow how .caption() is going to be set through $.fn.caption = function () ... just for the document.body object. This is the part which I'm not quite sure is possible. If not, then I guess I'll have to settle for requiring that a variable to be set, to maintain plugin functions chain-ability.
Here's an example code of what I expect:
$(document.body).someplugin().css('background-color', '#000');
$('.non-initialized').caption(); // Error, jQuery doesn't know what caption is
$(document.body).caption('done loading...');
Here's what I'm willing to settle for if that is not possible, or just very inefficient:
var $body = $(document.body).someplugin().css('background-color', '#000');
$('.non-initialized').caption(); // Error, jQuery doesn't know what caption is
$body.caption('done loading...');
The be jquery-chainable, a jQuery method MUST return a jQuery object or an object that supports all jQuery methods. You simply have to decide whether you want your plugin to be chainable for other jQuery methods or whether you want it to return your own data. You can't have both. Pick one.
In your code examples, you could just define more than one plugin method, .someplugin() and .caption(). jQuery does not have a means of implementing a jQuery plugin method that applies to one specific DOM object only. But, there is no harm in making the method available on all jQuery objects and you can only use it for the ones that it makes sense for.
I think you could use this:
(function ($) {
var loadScreen = $('<div />').text('sup lol');
$.fn.someplugin = function (args) {
var args = args || {},
$this = this;
$this.append(loadScreen);
return(this);
}
$.fn.caption = function (text) {
loadScreen.text(text);
return this;
}
})(jQuery);
$(document.body).someplugin().css('background-color', '#000');
$('.non-initialized').caption('whatever');
$(document.body).caption('done loading...');
If there's supposed to be some connection between the two .caption() calls, please explain that further because I don't follow that from your question.

jQuery plugin patterns: something more object-oriented?

I'm working on a jQuery plugin, following the pattern detailed in the Authoring guide. Basically:
(function($) {
// Private
var doSomething = function($element, settings) { ... }
var doSomethingElse = function($element, settings) { ... }
// Public
var methods = {
init: function(options) { ... },
show: function() { ... },
hide: function() { ... },
update: function(content) { ... }
};
$.fn.myPlugin = 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.myPlugin');
}
};
})(jQuery);
Here's my dislike: I have to pass the same "instance" variables to all of the private functions. I'm still working on becoming a JS pro — so pardon my incorrect term usage — but if I were doing this same thing in Ruby or PHP, I'd create a class with all of these public and private members and methods, and each instance of the class would correspond to an $element. Then I could do something like this (in JS):
var firstElement = new MyPlugin($element, settings);
firstElement.doSomething();
Rather than passing $element and settings to doSomething(), it already has access to those via this.$element and this.settings.
Get where I'm going with this? I'm looking for a more object-oriented approach, I guess. Now, I totally understand that JS doesn't have classes like Ruby or PHP. But between constructor functions, the module pattern, and regular object notation (like methods above), I'm not sure which is the best option for a jQuery plugin.
Can someone help point me in the right direction? Maybe some examples of existing jQuery plugins that do this well? Thanks!
The jQuery UI Widget Factory might be a good solution. It's useful for creating any kind of stateful jQuery plugins and can be used entirely separate from the rest of the jQuery UI suit.
Some useful links:
http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/
http://wiki.jqueryui.com/w/page/12138135/Widget-factory
http://ajpiano.com/widgetfactory/ (presentation)
If you want a more bare bone solution I'd go with either a regular Constructor + prototype setup to do things "properly" or use the Revealing Module Pattern to create a function that takes the element and any options as arguments and returns the public methods.
An example using the Revealing Module Pattern:
function myPlugin (element, options) {
var privateVar;
function privateFunc () {}
function publicMethod () {}
return {
publicMethodName: publicMethod
};
}
This pattern is a bit more tidy than a traditional prototypal set up, but does not take advantage of the prototype chain.
Edit: To clarify, when using any of these patterns you are supposed to create a new instance for each element/use.
It isn't necessarily a good idea to store any kind of stateful information in the plugin itself since it would be shared by all instances. One option is to store that data elsewhere, outside of the plugin.
The Plugins/Authoring page has a Data section which describes how to store information for use by your plugin on a per-element basis using the data() function.
Using data helps you keep track of variables and state across method
calls from your plugin. Namespacing your data into one object literal
makes it easy to access all of your plugin's properties from one
central location, as well as reducing the data namespace which allows
for easy removal if need be.
The example provided on the page uses the plugin pattern described in your post, but allows "instance" variables to be stored with the element they're associated with.
One key thing to remember when doing this is:
Always namespace your methods, events and data.
Edit:
It should be noted too, that in your example some of your functions expect $element as a parameter, but this isn't necessary since this will refer to the right thing when those functions are called through the plugin (because apply() is being called and setting the context to the correct this).

jquery plugin with multiple functions

According to the developer documentation jquery plugins are supposed to have only one namespace for all functions they make available. Which is straight forward as long as you only expose a single function per context (static/element).
(function($){
var
state_a = 0,
$.myplugin = function(in_options) {
// static
return this;
}
$.fn.myplugin = function(in_options) {
// element
return this;
}
})(jQuery);
This makes calls like this possible:
$("elem").myplugin(options);
jQuery.myplugin(options);
What's the best approach if you have more than one function and need to share state? I would like to call into my plugin like this:
$("elem").myplugin.start(options);
$("elem").myplugin.stop();
jQuery.myplugin.start(options);
jQuery.myplugin.stop();
I've used arguments.callee before:
(function ($) {
$.fn.pagination = function (options) {
var defaults = {
};
options = $.extend(defaults, options);
var object = $(this);
arguments.callee.updatePaging = function () {
//do stuff
};
});
Then, on your page:
var pagination = $("#pagination");
pagination.pagination();
pagination.pagination.updatePaging();
jQuery plugins tend to use a string parameter for different functions:
$(...).myplugin('start', options);
$(...).myplugin('stop');
I can't think of an easy way to use the syntax you would like to use, since having an extra property inbetween will make this point to something else than the jQuery object with the selected elements. Personally I find using a string parameter to completely change what the function does somewhat ugly too. $(...).myplugin().function() would be doable, but likely not as efficiently as just using that string parameter.
This is explained well in the docs: http://docs.jquery.com/Plugins/Authoring#Namespacing

Categories

Resources