Namespacing jQuery plugins - javascript

Let's assume that we have following jQuery plugins (each of them in separate files):
$.fn.foo
$.fn.foo.bar
$.fn.foo.baz
I use standard jQuery plugin pattern. The first one is actually a proxy or "facade" to the rest plugins of it's namespace.
For example, when I call $('#el').foo(), under the hood I also call:
var context = this; // context is equal to $('#el')
$(context).foo['bar'].apply(context);
$(context).foo['baz'].apply(context);
There are two problem when I want to call only (without $.fn.foo) $('#el').foo.bar(). The first problem is that there are no $.fn.foo namespace but I can create it, so this is actually no problem. The second problem is that this inside $.fn.foo.bar is equal to document object but I want to be equal to $('#el'). How can I do that?
So, making a long story short, both should work:
$('#el').foo(); // This also calls foo.bar and foo.baz under the hood
$('#el').foo.bar(); // I'm calling foo.bar explicitly

I think you might just need to follow a different plugin pattern. Here is a nice repository of jQuery plugin patterns, but the one you might want to look at would be the namespace pattern - you'll see how the namespace gets defined initially if it doesn't already exist, and this allows you to more easily extend from a single namespace across multiple scripts.
Update: Hmm, I'm still learning so I wouldn't say I'm an expert at this, but trying to get this $('#el').aaa.bbb.ccc() to work got really messy for me. Maybe it would be better to not use that format, but instead do this (demo):
$.aaa.bbb.ccc( $("#el") );
because then it is relatively easy to set up:
(function() {
if (!$.aaa) {
$.aaa = {
bbb : {
ccc: function(el, options){
alert(el[0].id);
}
}
};
};
})(jQuery);
$(function() {
$.aaa.bbb.ccc( $("#el") ); // alerts "el"
});

Related

Make a javascript "function object" inherit from another

In Javascript, it's possible to have some kind of inheritance, by using function contructors and their "prototype" attribute (i.e the "parent of future instances"), or more recently Object.create(), to get new objects.
However recently I needed to "override" the Jquery object ("$") for one of my projects, that is to say, provide a similar function, which also has all the fn/extend/... [inherited] attributes of the original "jQuery" factory function.
The goal was to have a "$()" function which always received "window.parent" as the second parameter by default, so that the whole program modified the parent frame and not the iframe in which the program was loaded.
Typically I expected to be able to do something like this:
var oldJQ = $;
$ = function (selector) {
return oldJQ(selector, window.parent);
}
$.__proto__ = oldJQ .__proto__;
$.extend({...}); // must work
Despite numerous attempts, I couldn't get it to work, I always got simple objects instead of real functions when using the "new" keyword, or the inheritance didn't work. I found alternative ways (for example, by copy-pasting all attributes from $ into my new function object), but it seems to me like a horrible way of doing stuffs.
Is there any way to have a new function object which has, as a parent/prototype, the original $ object ?
While functions are objects in javascript, they aren't regular objects. Specifically, they're objects for which inheritance is broken. Functions are not the only such objects in the language, DOM elements are similarly handicapped. That's one reason jQuery is implemented as wrapper around DOM rather than extending the DOM's features via inheritance.
Fortunately, while OOP doesn't work in this case, we can take inspiration from jQuery itself and use FP to do what we need. In other words, while you can't inherit functions, you can wrap it in another function.
For example, if you need a special console.log function for your app that sends logs back to your server you can override it by redefining it:
var log_orig = console.log;
console.log = function () {
ajaxlog([].slice.call(arguments,0).map(function(x){
return JSON.parse(x);
}).join(',');
log_orig.apply(console,arguments);
}
Or if you want a special version of $ that does something extra:
function $$ () {
/*
* do extra stuff
*/
return $.apply(null,arguments);
}
Or if you want to override $ instead if wrapping:
var old$ = $;
function $ () {
/*
* do extra stuff
*/
return old$.apply(null,arguments);
}

Binding objects parsed from JSON to classes

I want to pull a tree structured set of objects from a web service represented with JSON
When I unpack that, I'll wind up with a structure which uses vanilla Javascript objects. What I'd like to be able to do is bind each node to a specific class, so that method calls become available on each node of the tree.
My solution, using jQuery .extend()
Here's a simplified example which illustrates the approach.
I might define a simple class using jQuery .extend() as follows...
MyNode= function() {
this.init();
}
$.extend(MyNode.prototype, {
init: function() {
// do initialization here
},
getName: function() {
return this.nodeName;
}
});
Now given a simple object like this
var tree={
nodeName:'frumious',
nodeType:'MyNode'
}
I can make the object appear to be an instance of the desired nodeType with
$.extend(tree, eval(tree.nodeType+'.prototype'));
Note that I want the object to declare the class name, so I've used eval to locate the appropriate prototype for that class. (Thanks to Rob W for suggesting window[tree.nodeType].prototype as a better alternative)
Now I can do things like alert(tree.getName());
Better ways?
I write StackOverflow questions and find the act of describing it in enough detail to avoid a downvote is enough for me to solve it myself. This was no exception, but I'd be interested to hear of any more elegant approaches to this problem. This solution gets the job done, but I can't help but feel there must be other approaches...
I'd get rid off eval, and use:
$.extend(tree, window[tree.nodeType].prototype);
If MyNode is a local, but known variable, add it to an object, for reference. Eg:
var collection = {};
collection['MyNode'] = MyNode;
$.extend(tree, collection[tree.nodeType].prototype);
Alternatively, if the structure of the JSON is solid, I recommend a custom JSON parser, which also allows you to validate properties prior addition.

How to create a catch all function on a jQuery object?

I was previously using the jQuery autosave plugin but have recently removed it. Some code is still trying to use this function, so how can I temporarily extend the jQuery object so there is an empty function to prevent errors until I can get through all of the code that is trying to use the function?
The plugin is called in this way:
jQuery().autosave.restore();
jQuery().autosave.save();
I think those are the only two functions that exist, so it would be OK to create two empty functions, but is there a way to create a catch-all function on this type of object?
Note
This is a temporary solution until I can go through a lot of code. I do believe the question is a valid coding question even if you think this workaround is not ideal (it isn't).
There is a way to do this. You can create a dummy plugin (check out jQuery's documentation for creating plugins):
(function( $ ){
$.fn.autosave = {
restore: function() {};
save: function() {};
};
})( jQuery );
I would highly recommend against doing this, however. Instead, you should look at those errors and fix them, i.e., stop your code from using them. Otherwise you're simply hiding the problem.
Nope. Standard JavaScript does not support "catch-all" methods.

What is this design pattern known as in JavaScript/jQuery?

I was looking over the JavaScript source code for SlickGrid.
I've noticed that slick.grid.js has the following structure:
(function($) {
// Slick.Grid
$.extend(true, window, {
Slick: {
Grid: SlickGrid
}
});
var scrollbarDimensions; // shared across all grids on this page
////////////////////////////////////////////////////////////////////////////
// SlickGrid class implementation (available as Slick.Grid)
/**
* #param {Node} container Container node to create the grid in.
* #param {Array,Object} data An array of objects for databinding.
* #param {Array} columns An array of column definitions.
* #param {Object} options Grid options.
**/
function SlickGrid(container,data,columns,options) {
/// <summary>
/// Create and manage virtual grid in the specified $container,
/// connecting it to the specified data source. Data is presented
/// as a grid with the specified columns and data.length rows.
/// Options alter behaviour of the grid.
/// </summary>
// settings
var defaults = {
...
};
...
// private
var $container;
...
////////////////////////////////////////////////////////////////////////
// Initialization
function init() {
/// <summary>
/// Initialize 'this' (self) instance of a SlickGrid.
/// This function is called by the constructor.
/// </summary>
$container = $(container);
...
}
////////////////////////////////////////////////////////////////////////
// Private & Public Functions, Getters/Setters, Interactivity, etc.
function measureScrollbar() {
...
}
////////////////////////////////////////////////////////////////////////
// Public API
$.extend(this, {
"slickGridVersion": "2.0a1",
// Events
"onScroll": new Slick.Event(),
...
// Methods
"registerPlugin": registerPlugin,
...
});
init();
}
}(jQuery));
Some code has been omitted for brevity and clarity, but this should give you the general idea.
What is the purpose of the the following: (function($) { // code }(jQuery)); Is this the module pattern that I've seen talked about? Is this meant to keep the global namespace clean?
What do the $.extend() lines mean?: The top $.extend(true, window, { // code }); looks like it has to do with a "namespace"; what's a namespace? It looks like the bottom $.extend(this, { // code }); is used to exposed 'public' members and functions? How would you define a private function or variable?
How would you initialize multiple "SlickGrids" if you wanted to? How much would they share and how would they interact? Note the "constructor" function: function SlickGrid(...) { // code }.
What are some books, links, and other resources on coding in this style? Who invented it?
Thanks! ♥
This is a jQuery plugin.
(function($) { // code }(jQuery)); gives you a new function scope so your names are not dumped into the global scope. Passing jQuery as $ lets you use the $ shorthand even if other Javascript libraries use $.
$.extend is a jQuery method to copy properties from one object to another. The first argument true means it should be a deep rather than a shallow copy. By extending window, new global properties are created, in this case, Slick.
The $.extend(this,...) at the bottom is in a capitalized function SlickGrid. SlickGrid is meant to be used as a constructor, in which case this will be the newly-created object, so this extend is adding properties to the object. They are effectively public members. In this code sample, measureScrollbar is private: it is only visible to the code defined in this function, not outside it.
You can create a number of grids with:
var grid1 = new Slick.Grid(blah, blah);
var grid2 = new Slick.Grid(blah, blah);
In the code you've shown, the only thing these two instances will share is the scrollBarDimensions variable.
(function($) { // code }(jQuery))
This is a closure. It basically keeps everything inside it "// code" safe from things outside it. You pass in jQuery and the $, but someone with more knowledge will have to explain why that is necessary.
$.extend()
This is a jQuery function that will take two objects and merge them together. Replacing the first object "window" with the second object {Slick: {Grid: SlickGrid}}. This means if there is a window object with Grid:Null it would now equal Grid:SlickGrid.
Adding true as a first parameter means it will replace nested objects as well:
var firstObj = { myObj:{
first:this,
second: {
new: obj
}
}}
$.extend(true, firstObj, {myObj:{second:{new:newer}}});
This is useful if you are using a lot of objects to store information.
Not sure what you mean by #3, but look at http://960.gs for a good grid system.
JavaScript the Good Parts is a great book. Pro JavaScript by John Resig is also a good book to take you beyond the basics.
Simply put, guy in your example just wrote jQuery plug-in of sorts... Check PLUGINS section of jquery.com for more references to sources on how to code plugins. They are simple, straightforward and fun to explore.
For the first question, this construct is a way to allow jQuery to coexist with other libraries which may use the $ function, but still use $ to reference jQuery within a code block.
The entire package is wrapped in a function call with $ as a parameter. The only "main" activity when this is run is to call that function with jQuery as an argument, this giving a reference to the well-known global jQuery to the local parameter $, which masks any global value that $ may have.
1) That function is invoked immediately when the JS file is loaded. It receives the jquery instance as parameter and makes it available internally as "$". All code is encapsulated in that function so (unless you forget a var in front a yet undeclared variable) nothing pollutes the global namespace.
2) All properties of the 2nd object are copied to the 1st object, here "window", which also is the global namespace object in a web browser. Therefore this code does not make much sense. It pretends to encapsulate but does the opposite. Neither does the second invocation of $.extend make all that much sense. It's not wrong, I just think the code "pretends".
4) I very highly recommend you check out the videos from Douglas Crockford at http://developer.yahoo.com/yui/theater/
Crockford is a JS god, very famous (among serious JS programmers) - and in addition great fun to listen to :)

Javascript plugins design pattern like jQuery

Could someone write down a very simple basic example in javascript to conceptualize (and hopefully make me understand) how the jQuery plugin design pattern is done and how it works?
I'm not interested in how creating plugin for jQuery (so no jQuery code here at all).
I'm interested in a simple explanation (maybe with a bit of Javascript code) to explain how it is done the plugin concept.
Plz do not reply me to go and read jQuery code, I tried, but I it's too complex, otherwise I would have not post a question here.
Thanks!
jQuery has a library of functions stored in an internal object named fn. These are the ones that you can call on every jQuery object.
When you do $("div.someClass") you get a jQuery object containing all <div> elements of that class. Now you can do $("div.someClass").each( someFunction ) to apply someFunction to each of them. This means, that each() is one of the functions stored in fn (a built-in one in this case).
If you extend (add to) the internal fn object, then you automatically make available your custom function to the same syntax. Lets assume you have a function that logs all elements to the console, called log(). You could append this function to $.fn, and then use it as $("div.someClass").log().
Every function appended to the fn object will be called in such a way that inside the function body, the this keyword will point to the jQuery object you've used.
Common practice is to return this at the end of the custom function, so that method chaining does not break: $("div.someClass").log().each( someFunction ).
There are several ways to append functions to the $.fn object, some safer than others. A pretty safe one is to do:
jQuery.fn.extend({
foo: function() {
this.each( function() { console.log(this.tagName); } );
return this;
}
})
Tomalak already posted almost everything You need to know.
There is one last thing that helps jQuery do the trick with the this keyword.
it's amethod called apply()
var somefunction=function(){
alert(this.text);
}
var anObject={text:"hello"};
somefunction.apply(anObject);
//alert "hello" will happen
It really helps in creating abstractions so that framework/plugin users would just use this as intuition tells them, whatever there is inside Your code
It works, as many other js frameworks, using javascript prototype orientation.
For instance you can declare a simple function
var alertHelloWorld = function() {
alert('hello world');
}
And then tie it to an existing object (including DOM nodes)
document.doMyAlert = alertHelloWorld;
If you do this
document.doMyAlert();
The alertHelloWorld function will be executed
You can read more about javascript object prototyping here

Categories