I encountered with snippet of code in the jQuery menu library. I'm new to jQuery so can somebody explain what $(this) in cssmenu variable means (its a kind of context?).
$.fn.menumaker = function(options) {
var cssmenu = $(this),
settings = $.extend({
title: "Menu",
format: "dropdown",
sticky: false
}, options);
}
That code defines a jQuery plugin, by adding a function to the jQuery's object prototype. In the context of a plugin's initializer function, $(this) is totally unecessary: this in the context of a plugin function already refers to a jQuery object.
Not only that, but the way the plugin is written doesn't follow jQuery principles where a plugin might be called on a jQuery object wrapping over multiple DOM elements.
The plugin's definition also doesn't take in account the fact that jQuery.noConflict() would be used.
A form that would make more sense is:
//Using an IIFE and passing jQuery allow us to use $ safely
(function($) {
$.fn.menumaker = function(options) {
//Apply defaults
options = $.extend({
title: "Menu",
format: "dropdown",
sticky: false
}, options);
//Initialize the plugin for every DOM element in the jQuery oject
//and return the jQuery object to avoid breaking chaining
return this.each(function() {
//In here, this points to a DOM element, that's why we wrap it
//in a DOM object
initPlugin($(this), options);
});
};
function initPlugin($el, options) {
//The plugin would be applied here to $el
}
})(jQuery);
Let's look at the whole thing step by step:
$.fn.menumaker = function(options) {
}
This defines a jQuery plugin called menumaker, taking an options object. It is run as $('mySelector').menumaker({}).
var cssmenu = $(this), settings = $.extend({}, options);
Now, this is going to be the jQuery object $('mySelector') from the above example. Wrapping it in another jQuery constructor ($(this)) is rather redundant.
But in any case, it's saved as a variable named cssmenu. A settings variable is also created, applying the passed in objects to the default settings.
So basically, yes, it's the context. It's also pointless.
Basically $.fn.menumaker means its allows you to extend jquery with your own function, which in turn will be available to all jquery objects.
So the this, is the Jquery equivalent of the DOM object with which you have used plugin function.
Related
I am writing a jQuery plugin which, ideally I would like in it's own namespace.
So far, this seems to work (in terms of namespace nesting)
(function($) {
$.fn.nspace = {
foo: function() {
// Does not work becuase $(this) is not the correct selector.
$(this).show();
}
}
})(jQuery);
So given then example above, I might call my function like so:
$("html, body").nspace.foo();
but $(this) is not [html, body]...How can I solve this?
EDIT: To clarify (based on user comments)...
$("html, body").nspace.foo(); should call foo for [html, body] but, $(this) inside nspace resolves to nspace...so it's trying to call nspace.foo();
You shouldn't do this, but just because I dislike when someone says "You can't" in programming (often untrue, especially in Javascript) - here's how you could do this:
The jQuery object is constructed each time using its prototype.init function, which is aliased to fn.init, so you could overwrite it with a wrapped function that adds your namespace object in a way that doesn't harm any existing usage or libraries, like so:
(function($) {
var baseInit = $.fn.init;
$.fn.init = function(selector, context, rootjQuery) {
// Instantiate jQuery the way it expects
var j = new baseInit(selector, context, rootjQuery);
// Add our extra object/namespace
// Use j inside to refer to the current jQuery object
j.nspace = {
foo: function() {
j.show();
}
};
// Return it all and other libraries are none the wiser
return j;
}
})(jQuery);
http://jsfiddle.net/b9chris/7TPZY/
You should consider using the classic pattern for a jQuery plugin: define only one method: in your case, nspace. Inside this method, you'll take every case into account. Sounds hard, but it's pretty easy once you've looked into that.
(By the way you definitely have to look at that when writing a jQuery plugin)
You can't add an object as a plugin and still get the jQuery object that was used to get the object. You simply have no reference to that jQuery object when you call a method in your object.
Put the function directly as the plugin:
(function($) {
$.fn.nspace = function() {
this.show();
};
})(jQuery);
Usage:
$("html, body").nspace();
(Note that the object is the jQuery instance, not a selector or an element, so you don't need to use $(this)).
I am having a bit of trouble creating an instance of useful things
from another JS file, I would like to change the properties of the
instance and use the methods. Or whatever you suggest if this is not
possible. This extends and this construct are confusing me.
(function($) {
$.extend({
usefulThings: new function() {
this.defaults = {
prop_path:'/la/lala',
//OTHER STUFF
};
this.construct = function(options) {
return this.each(function() {
var setting;
this.settings = {};
settings = $.extend(this.settings, $.usefulThings.defaults, options);
$this = $(this);
//OTHER STUFF
$.data(this, "usefulThings",settings);
// FIRST METHOD CALL
//OTHER STUFF
});
};
//SOME METHODS
}
});
$.fn.extend({
usefulThings: $.usefulThings.construct
});
})(jQuery);
I have seen usefulThings called from script blocks like so:
$("#myDivName").usefulThings({
prop_path: '/la/lala' //these seems to get overwritten in the above
});
First, have a look at MDN's introduction to the this keyword. And in the jQuery docs for .each.
Next, check out how does jquery chaining work? to understand the construct method.
Then, you should notice that the use of the new keyword is absolutely inappropriate in this case. The expression should be replaced by a simple object literal:
{
defaults: {
prop_path:'/la/lala',
// …
},
construct: function(options) {
…
},
//SOME METHODS
}
Now, the jQuery.extend. The relevant sentence from the docs is
If only one argument is supplied to $.extend(), this means the target argument was omitted. In this case, the jQuery object itself is assumed to be the target. By doing this, you can add new functions to the jQuery namespace. This can be useful for plugin authors wishing to add new methods to JQuery.
And it's the same for $.fn.extend (which actually is === $.extend), it extends the jQuery.fn object which is a shortcut for jQuery's prototype object.
I have a question regarding the local variables for my jQuery plugin. I am pretty sure if I declare them outside the main jQuery function register, then every time the plugin is called it will redefine the variables.
Example:
(function($){
jQuery.fn.test = function(options){
if ( options ) {
$.extend( settings, options );
}
this.each(function(i,item){
// do whatever i am going to do
});
};
var settings = {
value1: "hello",
value2: "word"
};
})(jQuery);
Say that $(object).test({value1:'newterm'}); is called multiple times.. Am I correct in thinking that every time that method is called, that it will override the settings with the most recently declared settings?
If i want multiple instances of my plugin, do I need to declare everything within the scope of the main jQuery.fn.test = function(){//here} method?
Yes, that is correct, because $.extend will modify settings which is in the closure scope exposed when the jQuery initialization function sets up .test on the global object jQuery. That closure scope is the same everytime you execute .test; therefore, all objects will retain changes.
It depends on the order you pass objects to $.extend. The first (target) object passed will be modified, in your case the settings object. If you want to keep the defaults:
$.extend(options, settings);
Or to get a brand new object:
var newoptions = $.extend({}, options, settings);
Almost all of the examples in the jQuery tutorials that I've read, usually use one major public function for their selecting plugin. When I say 'selecting' plugin, I mean one that is not simply a static function extended onto jQuery.
For example:
(function($) {
jQuery.fn.actionList = function(options) {
var opts = $.extend({}, $.fn.actionList.defaults, options);
return this.each(function(){
alert(this);
});
};
$.fn.actionList.defaults = {
listHtml: '<div>Set the list html</div>'
};
})(jQuery);
but not:
jQuery.log = function(message) {
if(window.console) {
console.debug(message);
} else {
alert(message);
}
};
This works fine for most things, but what I would like to do is be able to call a second function on the object returned from the first call.
var actionBox = $('actionBox').actionList(options);
//Many light-cycles later
actionBox.refreshData(data);
or maybe even:
$('actionBox').actionList(options);
// laaateerr
$('actionBox').actionList.refreshData(data);
I'm guessing one or both of these is not possible or, at least not advisable, but I'm only now getting into the deepest aspects of jQuery and javascript.
Could someone explain how to do this, or if it's not possible or advisable, why? and what they would do instead?
Thanks for reading!
I'm not quite sure what you're getting at, but you can call a second function on the object returned from the first function - in fact, it is very much encouraged to return a jQuery object from your plugins, and the reason why you can chain commands in jQuery.
Using your examples
var actionBox = $('actionBox').actionList(options);
//Many light-cycles later
actionBox.refreshData(data);
would work fine, so long as both .actionList() and .refreshData(data) commands both return a jQuery object.
And
$('actionBox').actionList.refreshData(data);
would need to be
$('actionBox').actionList().refreshData(data);
EDIT:
Looking at the jQuery source code,
jQuery.fn = jQuery.prototype = {
/*
Load of 'property' functions of jQuery object...
*/
}
so, adding properties (a.k.a plugins) to jQuery.fn extends the prototype of the jQuery object. When you call
$(selector, context);
a new jQuery object is returned, using the init property function of the jQuery object
jQuery = window.jQuery = window.$ = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
I think I've got a plugin that might be very useful for you. It allows you to apply any constructor/object to jQuery as it's own namespace AND you can use 'this' as you would normally with jQuery to refer to the object set. Using this[methodName] will call a method on your object, etc.
http://code.google.com/p/jquery-plugin-dev/source/browse/trunk/jquery.plugin.js
Some code samples are here:
http://groups.google.com/group/jquery-dev/browse_thread/thread/664cb89b43ccb92c/34f74665423f73c9?lnk=gst&q=structure+plugin+authoring#34f74665423f73c9
It's about halfway down the page.
I hope you find it useful!
I created a jQuery plugin that was working great until I started testing it with more than one object on a page. The problem is that the options object passed to each new plugin object is not always the same one associated with that specific object when I access it from inside a function in the plugin. I have a feeling that I am missing something very simple, so the code might make things more clear.
Method used to create the plugin
$.fn.myPlugin = function(options) {
return this.each(function() {
var opts = $.extend({}, $.myPlugin.defaults, options);
new $.myPlugin($(this), opts);
});
}
Function that accesses the options object
$.myPlugin = function($textbox, options) {
function doSomething($textbox) {
alert(options.someProperty);
}
}
No matter what options.someProperty was when I created the plugin. The call to doSomething inside the plugin will always return the someProperty of the first options object in the first plugin object I created for example:
$textbox.focus(function() { doSomething($textbox); } );
This will always return the same someProperty even if different objects with different options objects are focused.
I hope I have made this clear enough. Let me know if you need anymore details.
You're giving the plugin a reference to the same options object for all of the elements.
Try the following:
return this.each(function() {
new $.myPlugin($(this), $.extend({ }, options));
});
This will copy the members of the options object to a new object each time. If your options object has other objects nested within it, you might need a deep copy, like this: $.extend(true, { }, options)
EDIT: You need to extend the options object inside the each method. Otherwise, you'll still have the same object for all of the elements.
Try changing
function doSomething($textbox) {
to
var doSomething = function($textbox) {
to ensure that you are creating separate doSomething methods that use separate closures. (the first version will create a single function the first time it's called, and reuse that function and its closure)