Might be a silly question, but I am confused between .queue() used with .dequeue() and $.queue() OR jquery.queue().
Are they the same, if so why jquery provided them in 2 separate documentations? Can, someone explain their ussage differences along with appropriate examples ?
https://api.jquery.com/queue/
http://api.jquery.com/jquery.queue/
.queue() is used as a method of the jQuery element.
It has only one parameter "queueName"
$("div:first").queue("fx")
Whereas jquery.queue() is a standalone function that will accept DOM element as it's first parameter and second for name of the queue.
jQuery.queue($("div:first")[0], "fx" );
Both works same, just the approach is different.
As said at http://api.jquery.com/jquery.queue/
Note: This is a low-level method, you should probably use .queue() instead.
Internally $(selector).queue() and $(selector).dequeue() use $.queue() and $.dequeue() respectively.
This is the code for $(selector).queue() and $(selector).dequeue() jQuery 2.1.3:
jQuery.fn.extend({
queue: function( type, data ) {
var setter = 2;
if ( typeof type !== "string" ) {
data = type;
type = "fx";
setter--;
}
if ( arguments.length < setter ) {
return jQuery.queue( this[0], type );
}
return data === undefined ?
this :
this.each(function() {
var queue = jQuery.queue( this, type, data ); // <-- HERE!
// Ensure a hooks for this queue
jQuery._queueHooks( this, type );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type ); // <-- HERE!
}
});
},
dequeue: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type ); // <-- HERE!
});
},
....
Related
I am working on some other developer's code and I noticed the following pattern being used in some of the JavaScript files.
var my_team = function() {
var handleTeam = function() {
//do some ajax
// and update selected DOM element
}
return {
//main function to initiate the module
init: function() {
handleTeam();
}
};
}();
jQuery(document).ready(function() {
my_team.init();
});
I am at a beginner level of JS development and learning best practices. I think the method above is called Closures? Is that correct?
What I am trying to achieve is:
<select name="players" id="player">
<option>Mark</option>
<option>Tom</option>
</select>
<select name="coaches" id="coach">
<option>Mark</option>
<option>Tom</option>
</select>
I want to be able to pass HTML id-attributes player and coach to init() to take some actions to manipulate DOM.
One way I know is that I could change init-function to accept two parameters and update handleTeam to take two too and so on.
init: function(param1, param2) {
handleTeam(param1, param2);
}
This doesn't seem to be the best method as I won't be able to pass additional parameters later on unless I change the code above to accept more parameters in the list above.
My main goal is to make this functionality re-usable on other pages where I can choose default values or pass any parameters as required.
How can I make it to have default parameters and override those as required from any page?
I think above method is called Closures? Is that correct?
Yes the pattern from the OPs snippet is a "Closure" and its also an "Immediately Invoked Function Expression (aka "IIFE").
As you asked for best practices, I made some subtle changes to reply to this. So its less important what I have implemented but more important how I did it (see inline comments).
If I get you right you want to achieve something like this (also added some stuff to functions body for illustration purpose):
var myTeam = (function( _sDefault, _oDefault ) { // my_team vs. myTeam? Naming convention for JS is CamelCase!
// underscore prepended or appended to variable names is common use to show that a variable has private access
var _handleTeam = function( sDefault, oDefault ) {
console.log( sDefault );
console.log( oDefault );
// "cannot call"/"don't has access" to init() nor updatePlayer()
}
return { // deploy public methods
init: function( sDefault, oDefault ) {
if ( !sDefault ) sDefault = _sDefault; // devs write: sDefault = _sDefault || sDefault;
if ( !oDefault ) oDefault = _oDefault;
_handleTeam( sDefault, oDefault );
},
updatePlayer: function() {
console.log('updatePlayer');
}
};
})( 'default', {default: true} ); // pass values on IIFE
myTeam.init(); // initiate with default values
myTeam.init( 'custom', {default: false, custom: true} ); // initiate with custom values
myTeam.init(); // initiate again with default values
myTeam.updatePlayer();
It would be totally fine to take the above design pattern if it fits your needs. But I can see at least 2 caveats here.
The private methods don't have access to the public ones deployed by the return value.
Kinda hard to read and therefor harder to maintain.
So here is a pattern that I would prefer over the one above | also Closure and IIFE:
var myTeam = (function( _sDefault, _oDefault ) {
// make sure that _oDefault can not be modified from outer scope
_oDefault = $.extend({}, _oDefault); // *
// declare variables with private access
var _oThis = this, // most devs write "that" instead of "_oThis" like I do, you can see "self" also quite often
_oBackup = {sDefault: _sDefault, oDefault: $.extend({}, _oDefault)}; // *
var _handleTeam = function( sDefault, oDefault ) {
// public methods are now also availabe to private ones
_oThis.log( sDefault );
_oThis.log( oDefault );
return _oThis.updatePlayer();
}
// declare properties with public access
this.setDefaults = function( sDefault, oDefault ) {
if ( typeof sDefault === 'string' )
_sDefault = sDefault;
if ( typeof sDefault === 'boolean' )
_sDefault = _oBackup.sDefault;
if ( typeof oDefault === 'object' )
_oDefault = $.extend({}, oDefault); // *
if ( typeof oDefault === 'boolean' )
_oDefault = $.extend({}, _oBackup.oDefault); // *
return this; // make public methods chainable
}
this.updatePlayer = function() {
return this.log('updatePlayer'); // make public methods chainable
}
this.log = function( sLog ) {
console.log(sLog);
return this; // make public methods chainable
}
this.init = function( sDefault, oDefault ) {
_handleTeam(
sDefault || _sDefault,
oDefault || _oDefault
);
return this; // make public methods chainable
}
return this; // deploy everything that has public access
})( 'default', {default: true} ); // set default parameters on IIFE
// our public methods are chainable now
myTeam.init().log('initiated with default values')
.init( 'custom', {default: false, custom: true} ).log('initiated with custom values')
.setDefaults( false, false ).log('reseted to default values')
.init().log('initiated reseted default values')
.setDefaults( 'new default', {default: true, newDefault: true} ).log('set new default values')
.init().log('initiated with new default values');
// *: if you don't know why I'm using jQuery.extend for objects, feel free to leave a comment and I can explain...
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Another question?
init: function(param1, param2) { handleTeam(param1, param2); }
This doesn't seem to be the best method as I won't be able to pass additional params later on unless I change code above to accept more params in the list above.
You can pass as many parameters/arguments as you like, without declaring them beforehand (use arguments instead):
init: function() {
console.log(arguments);
handleTeam(arguments[0], arguments[1], arguments[2]);
// or you can do it like this as well:
handleTeam.apply(this, arguments); //
}
myTeam.init( 'yep', 'don't worry', 'works' )
When I read your question over and over again I guess the following mockup should be in your direction (or should at least be able to illustrate how things can work together). Working pseudocode | Closure but NO IIFE:
(function( $ ) { // sure this an IIFE again but thats not essentially to the question at this point
var Team = function() {
// private
var _oThis = this,
_oTeam = {},
_privateHelper = function() {
// this function can not be triggered directly from outer scope
console.log('_privateHelper was called');
return _oThis; // use _oThis instead of this here!!!
},
_get = function( sId, sIdSub ) {
return _oTeam[sId] && _oTeam[sId][sIdSub] ? _oTeam[sId][sIdSub] : false;
},
_set = function( sId, sIdSub, val ) {
_oTeam[sId][sIdSub] = val;
return _privateHelper();
};
// public
this.register = function() {
for( var i = 0, iLen = arguments.length, sId; i < iLen; ++i ) {
sId = arguments[i];
_oTeam[ sId ] = {
$: $('#' + sId), // #1 cache jQuery collection
aPerson: [], // #2 cache names of each person
sSelectedPerson: false // #3 cache name of selected person
};
_oTeam[ sId ].$.find('option').each(function( iEach ){
_oTeam[ sId ].aPerson[ iEach ] = $(this).val(); // #2
});
this.updateSelectedPerson( sId ); // #3
}
return this; // for chaining | BTW: this === _oThis
}
this.updateSelectedPerson = function( sId ) {
if ( _oTeam[ sId ] ) {
_set(sId, 'sSelectedPerson', _oTeam[ sId ].$.val());
}
return this;
}
this.getSelectedPerson = function( sId ) {
return _get(sId, 'sSelectedPerson');
}
this.getPersons = function( sId ) {
return _get(sId, 'aPerson');
}
this.update = function( sId ) {
if ( _oTeam[ sId ] ) {
console.log(
'old selected: ' + this.getSelectedPerson( sId ),
'new selected: ' + this.updateSelectedPerson( sId ).getSelectedPerson( sId )
);
}
return this;
}
arguments.length && this.register.apply( this, arguments );
return this; // deploy public properties
};
$(function(){ // document ready
var oTeam = new Team( 'coach', 'player' ); // would be the same as ...
// var oTeam = new Team().register( 'coach', 'player' );
console.log(oTeam.getPersons('coach'));
console.log(oTeam.getPersons('player'));
$('select').on('change.team', function(){
oTeam.update( this.id )
})
});
})( jQuery ) // pass jQuery on IIFE for making save use of "$"
<h1 style="font-size:1em;display:inline">select coach and player: </h1>
<select name="players" id="player">
<option>player Mark</option>
<option>player Tom</option>
</select>
<select name="coaches" id="coach">
<option>coach Mark</option>
<option selected>coach Tom</option>
</select>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Take care of parameters that have type of object
var oO = {prop: 'save???'},
oA = [true],
s = 'save!',
i = 0,
b = true,
fn = function( oO, oA, s, i, b ) {
// every argument will get a new value
// lets have a look if this effects the original variable that was passed
oO.prop = 'nope!';
oA[0] = 'oh oh!';
s = 'yep save';
i = 999;
b = false;
};
fn(oO, oA, s, i, b);
// initial -> inner scope -> outer scope
console.log( oO.prop ); // 'save???' -> 'nope!' -> 'nope!'
console.log( oA[0] ); // true -> 'oh oh!' -> 'oh oh'
console.log( s ); // 'save!' -> 'yep save' -> 'save!'
console.log( i ); // 0 -> 999 -> 0
console.log( b ); // true -> false -> true
Here is the best explanation about the WHY I ever found so far (short, precise, understandable, credits: #newacct):
"Objects" are not values in JavaScript, and cannot be "passed".
All the values that you are dealing with are references (pointers to objects).
Passing or assigning a reference gives another reference that points to the same object. Of course you can modify the same object through that other reference.
https://stackoverflow.com/a/16893072/3931192
So if you now have a closer look at the mockups above - thats exactly why I used jQuery utility method extend for objects that are somehow related with the outer scope (which is the case when passed as parameters - right?).
Keep it in mind - never ever forget it from now on! This can save you hours of headaches if you are a novice!
So how to circumvent this behavior:
var oO = {prop: 'make it save now'},
oA = [true],
fn = function( oO, oA ) {
var o = jQuery.extend({}, oO, oA);
console.log('log#1', o);
o[0] = 'how save is this?';
o.prop = 'so save now :)';
console.log('log#2', o);
};
fn( oO, oA );
console.log('log#3', oO, oA);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
NOTE: Other libraries such as underscore or lodash also provide functions to achieve this.
Sample code with what I want to do is commented:
myObj.doSomething({
callbackFn: function( result ) {
if ( result && result.retval !== null )
{
// I want to assign the values on result.retval to myObj
// but `this` is not equal to myObj.
// is it correct to just do this?
myObj.some_val = result.some_val;
}
}
});
Normally with underscore, I'd use its _.bind() function to do this. The code I'm dealing with doesn't use underscore. What's the best way to do this with vanilla JS or jQuery?
JavaScript itself provides .bind:
myObj.doSomething({
callbackFn: function( result ) {
if ( result && result.retval !== null )
{
this.some_val = result.some_val;
}
}.bind(myObj); // <--
});
This is under the assumption that you cannot change the implementation of doSomething. If you can change it, see #10100111001's answer.
But you might as well use the code you currently have. I don't see a big benefit of using .bind here.
Just for fun, a simple version of.bind is pretty easy to implement yourself, should you find yourself in an environment that doesn't support it:
function bind(f, thisValue) {
return function() {
return f.apply(thisValue, arguments);
};
}
var boundF = bind(f, myObj);
You can also do something like this where you can call the callbackFn in doSomething with the proper this value.
var myObj = {};
myObj.doSomething = function (obj) {
obj.callbackFn.call(this, {some_val: "test"});
}
myObj.doSomething({
callbackFn: function( result ) {
if ( result && result.retval !== null )
{
// I want to assign the values on result.retval to myObj
// but `this` is not equal to myObj.
// is it correct to just do this?
this.some_val = result.some_val;
}
}
});
console.log(myObj);
with block is pretty much :
function( result ) {
if ( result && result.retval !== null )
{
with(myObj){
some_val = result.some_val; // Even,no Need "this"
}
}
}
Even, No need this
I'm trying to make a jQuery plugin for custom checkboxes and radio buttons.
(function($)
{
$.fn.checkboxRadio = function(options)
{
var defaults = some;
...
return this.each(function()
{
var button = $(this);
...
});
}
})(jQuery);
It can be used now by $('input').checkboxRadio(options);
How do I add a method check without changing current scope, to make a possible usage of something like $('input').checkboxRadio('check')?
How to handle a custom method and get its name inside my plugin?
Here is the official jquery plugin guide.
The part about wrapping functions is found here ("Plugin Methods") (the example is a would-be tooltip plugin) :
(function( $ ){
var methods = {
init : function(options) { ... },
show : function() { ... },
hide : function() { ... },
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);
[update] explaining the methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )) line in the guide :
If you call $(selector).tooltip('update', 'hello') from your javascript code, you want to end up calling the update method, passing 'hello' as the content argument, with this set to $(selector) for the duration of the call.
That is what this line takes care of :
if method == 'update', methods[method] is the update method,
arguments will be equal to ['update', 'hello'], you have to drop the first element to get the arguments you want to pass to your method ; this is exactly what Array.prototype.slice.call(arguments, 1) does,
myFunc.apply(obj, argsArray) calls the function myFunc, passing argsArray as the arguments, and setting this to obj for the duration of the call.
So inside your methods, you can call this.each(...) to iterate over all of the selector's items, e.g. :
update: function(content) {
this.each(function(){ $(this).data('tooltip.content', content); });
return this;
}
You can connect plugin methods like this:
(function($) {
$.fn.checkboxRadio = function(options) {
var defaults = {
check: 'check'
}
return this.each(function() {
var o = options;
var _this = $(this);
if( o.check === 'check' ) {
_this.attr('checked','checked');
}else if ( o.check === 'uncheck' ) {
_this.removeAttr('checked');
}
});
}
})(jQuery);
and user document should be like what you want: $('input').checkboxRadio({check:'check'});
I am trying to use jQuery's .data() tool in a jQuery plugin but I'm getting some odd results.
Here is a cut down version of my plugin with the relevant code:
(function( $ ){
var methods = {
init : function( options ) {
// Do some stuff
this.data('name', 'New Component');
return this;
},
getStateData : function () {
// Function returns all data for save
var state = new Object;
state.name = this.data('name');
// snip ... Add some other bits to state
// ...
return state;
},
setStateData: function (args) {
var key = args[0];
var value = args[1];
// snip
this.data(key, value);
}
};
$.fn.component7Segment = 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.designComponent' );
}
};
})( jQuery );
If I call $(instance-of-plugin).component7Segment('getStateData'); the plugin returns the correct 'name' however if I call setStateData to change the name it has not changed the 'name' value when I next call getStateData.
Doing console.log(this); in each function looks slightly different so I started trying:
$.data($('#object-id'), 'name', value);
This still doesn't have the desired effect.
Can anybody see what I am missing?
Assuming you're calling setStateData like this:
$(instance).component7Segment("setStateData", "name", "Foo");
the setStateData function should be changed to look like this:
setStateData: function () {
var key = arguments[0];
var value = arguments[1];
// snip
this.data(key, value);
}
Note the removal of the args parameter and the use of the special arguments array-like object instead.
Example: http://jsfiddle.net/andrewwhitaker/ShFHC/
Your original problem was most likely that you were calling setStateData with parameters name and "foo". args[0] and args[1] was actually accessing the characters in name at that position instead of the arguments passed to the function.
So I'm trying to learn how to implement method collection for a plugin based on this example: http://docs.jquery.com/Plugins/Authoring
What I cannot understand is how options that are extended with defaults for the plugin get sent to the individual methods.
I'm pretty sure any original options get sent to the method here:
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
So how can you extend these arguments with defaults? The example doesn't really define how to do this...
var methods = {
init : function( options ) {
return this.each(function(){
$(window).bind('resize.tooltip', methods.reposition);
});
}
}
Also, here is the code from the example plugin authoring page:
(function( $ ){
var methods = {
init : function( options ) {
return this.each(function(){
$(window).bind('resize.tooltip', methods.reposition);
});
},
destroy : function( ) {
return this.each(function(){
$(window).unbind('.tooltip');
})
},
reposition : function( ) { // ... },
show : function( ) { // ... },
hide : function( ) { // ... },
update : function( content ) { // ...}
};
$.fn.tooltip = 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.tooltip' );
}
};
})( jQuery );
Looks like my previous answer was closer to the mark than I previously thought.
Yes, this line is passing on the arguments:
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
Using .apply(), you can call a method, change its context (this value), and give it a collection of arguments instead of individual ones. Handy if you don't know how may arguments need to be passed on.
So you can break the above line down like this:
// reference the arguments passed to the plugin
var args = arguments;
// eliminate the first one, since we know that's just the name of the method
args = Array.prototype.slice.call( arguments, 1 )
// call the method that was passed,
// passing along the Array of the arguments (minus the first one),
return methods[method].apply( this, args);
// Note that it is being called from the context of "this" which is the jQuery object
So if the plugin was called like this:
$('.someElem').tooltip('destroy', 'someArg', 'anotherArg' );
The code will locate the "destroy" method, slice "destroy" off of the Arguments object, and call "destroy" passing along the Array of remaining arguments.
you use $.extend(existingObject || {} (new object), oneObject, secondObject, nthObject)
var mydefaults = { myDefaultArg : "foo" }
var inputOptions = { myDefaultArg : "Somethin else" }
var options = $.extend({}, mydefaults, inputOptions);
options.myDefaultArg == "Somethin else";
To access data, or to save them,you can use the data method
so if you are in the plugin, "this" is the jquery element element, you can now save data into it.
$(this).data("pluginname.somedataname", data);
and retriev it
var data = $(this).data("pluginname.somedataname");