How do you manage namespace for a custom JavaScript library depends on jQuery?
Do you create your own namespace, say foo and add your objects there? e.g. foo.myClass, foo.myFunction
Or do you add your objects to jQuery's namespace? e.g. jQuery.myClass, jQuery.myFunction
Which is the more common practice and why?
This article discusses writing jQuery plugins/libraries in excruciating detail.
What NOT to do:
(function( $ ){
$.fn.tooltip = function( options ) { // THIS };
$.fn.tooltipShow = function( ) { // IS };
$.fn.tooltipHide = function( ) { // BAD };
$.fn.tooltipUpdate = function( content ) { // !!! };
})( jQuery );
What to do:
(function( $ ){
var methods = {
init : function( options ) { // THIS },
show : function( ) { // IS },
hide : function( ) { // GOOD },
update : function( content ) { // !!! }
};
$.fn.tooltip = function( method ) {
// Method calling logic
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.tooltip' );
}
};
})( jQuery );
I also wrote a blog post last year about various methods for namespacing in JavaScript (non-jQuery related).
It would depend on what the library does.
If you're extending the functionality of instances of jQuery objects, you'd use jQuery.fn as was described very nicely by #David Titarenco in his answer.
If you're creating utilities that are meant to be seen as additions to those provided in window.jQuery, then I don't see a problem with using that namespace (as long as you're careful with naming).
If it is really its own separate library that is not meant to be seen as an extension of jQuery, yet relies on functionality from jQuery, then definitely use your own namespace.
Related
I am trying to hook css get/set. However after installing a simple pass thru hook it seems to be not called: (no error messages in Chrome console)
$.cssHooks['padding-left'] = {
get: function( elem, computed, extra ) {
return $.css( elem, $['padding-left'] );
},
set: function( elem, value) {
// Setting breakpoint here in Chrome, this line is not called:
elem.style[ $.support['padding-left'] ] = value;
}
};
// Proof of concept: Should not this statement initiate the hooked setter call?
// This statement is called, confirmed via breakpoint
$('#et-top-navigation').css('padding-left', '0px');
What am I missing?
Use the camel-case javascript property name rather than the hyphenated css style.
$.cssHooks['paddingLeft'] = { ...
You need wrap all of that in a document ready call, because jQuery writes cssHooks at this time and will get rid of your functions if they exist.
Take a look at the skeleton template in the API
https://api.jquery.com/jQuery.cssHooks/
(function( $ ) {
// First, check to see if cssHooks are supported
if ( !$.cssHooks ) {
// If not, output an error message
throw( new Error( "jQuery 1.4.3 or above is required for this plugin to work" ) );
}
// Wrap in a document ready call, because jQuery writes
// cssHooks at this time and will blow away your functions
// if they exist.
$(function () {
$.cssHooks[ "someCSSProp" ] = {
get: function( elem, computed, extra ) {
// Handle getting the CSS property
},
set: function( elem, value ) {
// Handle setting the CSS value
}
};
});
})( jQuery );
I'm noticing that this references something else inside a function that I added as event listener. I read this informative resource and a few questions on stackoverflow but I don't know how to apply it to my case (I'm quite new to the "oop" and the module pattern in javascript so I'm a bit lost).
Here is my little module:
var myModule = myModule || ( function() {
// Adds event listener for all browsers
// see http://stackoverflow.com/a/6348597
function addEvent( element, event, listener ) {
// IE < 9 has only attachElement
// IE >= 9 has addEventListener
if ( element.addEventListener ) {
return element.addEventListener( event, listener, false );
} else if ( element.attachElement ) {
return element.attachElement( "on" + event, listener );
}
}
return {
init: function() {
// Add event listeners
addEvent(
document.getElementById( "myElementId" ),
"click",
this.processMyElement
);
addEvent(
document.getElementById( "myOtherElementId" ),
"click",
this.processMyOtherElement
);
},
hideElementById: function( elementId ) {
document.getElementById( elementId ).style.display = "none";
},
showElementById: function( elementId ) {
document.getElementById( elementId ).style.display = "block";
},
processMyElement: function() {
this.hideElementById( "myElementId" );
this.showElementById( "myOtherElementId" );
},
processMyOtherElement: function() {
// Do something else...
}
};
}() );
The thing is that this which I use to call hideElementById in processMyElement is referencing to the element I added an eventListener to, and not to the current object.
I tried a few things without success:
removing the return in addEvent,
using var that = this as a private property of the module (placed in the module before the addEvent definition) and using that in processMyElement
using apply in the init method but it (obviously) calls processMyElement when adding the listener to the element
Could anyone help me with this? I tried a few things but I cannot see how to do it better...
PS: I try to build testable code, that's why I had those hideElementById and showElementById methods, in order to separate various functionalities (that may be quite clumsy actually but that's where I am ATM...).
There are (more than) a couple of common ways to get the correct this binding. For example, you can use a closure:
var that = this;
addEvent(
document.getElementById( "myOtherElementId" ),
"click",
function () {
that.processMyOtherElement();
}
);
Or you could use bind:
addEvent(
document.getElementById( "myOtherElementId" ),
"click",
this.processMyOtherElement.bind(this)
);
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
Which one you use would depend on other factors.
I am in process of learning more about javascript plugins and found one that is of interest to me. I am willing to get my feet dirty and see how this thing can be modified...
(function( $ ){
var methods = {
init : function( options ) {
return this.each(function(){
var $this = $(this),
data = $this.data('tooltip'),
tooltip = $('<div />', {
text : $this.attr('title')
});
// If the plugin hasn't been initialized yet
if ( ! data ) {
console.log('still working..' );
/*
Do more setup stuff here
*/
$(this).data('tooltip', {
target : $this,
tooltip : tooltip
});
}
});
},
show : function( ) {
console.log('this is the show');
},
hide : function( ) {
// GOOD
},
update : function( content ) {
console.log('this is the update');
// !!!
}
};
$.fn.tooltip = function( method ) {
// Method calling logic
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.tooltip' );
}
};
})( jQuery );
ok I have 4 questions...
1.how do you initialize this plugin? i keep getting 'still working..' with my console log when i try to run a random div element, $('#mtest').tooltip();.
2 the init: is inside the var method, which is private, meaning I can't access init: from outside of this plugin? right? where would i put initializing logic at since it appears to be returning options...?
3.
I am confused about this part of the code...
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.tooltip' );
}
I know its returning all the methods, but...
3a. why write methods[method]// is looks like [method] is an array, and that looks confusing to me because I don't see an array, its a bunch of methods...
3b. what is the else checking for? or why would an error occur?
thanks for any advice on helping me fully understand this plugin!
I don't know what your getting at with the first question. But the other questions can be solved pretty easily.
First, lets go over 3.
The code you have, and what jQuery provides in their docs, is merely a sort of "getter" between you and your methods. Instead of clustering up a namespace with all of your methods, you put your methods into an object title methods (which is instantiated on the second line of your first block of code.
If you look at the jQuery provided code you are asking about, its not returning methods as you've thought. Its calling the method of the key in your methods object. The first if statement says that if you call your plugin (in your case, tooltip) with a string variable, it will look up that index in the methods object and Call the function.
The second else if block says that if you pass a object as a parameter OR no parameter, it will call your init method. This is sort of like a custom built getter/initializer for your plugin.
So now, to answer your second question, the init method can be accessed by either calling your tooltip plugin with..
1) no parameters
2) a object parameter (usually options such as {"someOption":true,"anotherOption":400})
3) the string 'init' as in $('#id').tooltip('init')
This way you can also access your show and hide methods with...
$('#id).tooltip('hide') ... and so forth.
You can read up on this in the jQuery docs for much more detail. This is me merely putting it into layman's terms.
I'm writing a new jQuery plugin. For the guide, I am using their recommendation:
(function( $ ){
var methods = {
init : function( options ) {
return this.each(function(){
var $this = $(this),
data = $this.data('tooltip'),
tooltip = $('<div />', {
text : $this.attr('title')
});
// If the plugin hasn't been initialized yet
if ( ! data ) {
data = {
element : this,
target : $this,
tooltip : tooltip
};
$(this).data('tooltip', data);
}
methods.update.apply(data.element, 'Test');
},
update : function( content ) {
var $this = $(this),
data = $this.data('tooltip');
// check or change something important in the data.
private.test.apply( data.element );
return data.element;
}
};
var private = {
test: function() {
var $this = $(this),
data = $this.data('tooltip');
// again, do some operation with data
}
};
$.fn.tooltip = function( method ) {
// Method calling logic
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.tooltip' );
}
};
})( jQuery );
Its a little different from their version to make it shorter but also to show my differences. Basically, in the init, I am instantiating and creating data object that gets stored in the element. Part of the data object is the element itself:
element : this,
Then, after all of the initialization is done, I call a public method from the init (lets say I do it for functionality reuse purpose). To make the call, I use .apply() and provide the proper context (my element), which would match the context when the function is called externally:
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
This is fine and understandable. However, what I am unsure about is the performance of acquiring the data of the plugin from within a private or a public method. To me, it seems that at the top of every public and private method I have to execute the following lines in order to get the data:
var $this = $(this),
data = $this.data('tooltip');
Of course, I wouldn't execute them when I have no need for whatever is stored in data. However, my plugin performs quite a bit of animations and state tracking and almost all of the functions require access to the data. As such, it seems like accessing .data() in almost every private and public call is a pretty big performance hit.
My question is whether anyone uses this plug-in structure (I'm hoping that yes since jQuery recommends it) and has found a different way of referencing the data without hitting .data() in every function call.
Possible Duplicates:
What does this mean in jquery $('#id', javascript_object);
What does $(''class for the same element', element) mean?
What does the second argument to $() mean?
Anyone know what are:
$('element', $$).function(){...};
(seen here)
And
$('element', this).function(){...};
(seen here)
?
$('.pblabel', this).text(newVal + '%');
Is the same thing as
$(this).find('.pblabel').text(newVal + '%');
In fact, that is the way it is rewritten and run internally. It is called the "context selector".
From the jQuery source:
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
It uses this or $$ as the context, i.e. all elements returned must be its descendants. The default is document.