learning and expanding a jquery plugin - javascript

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.

Related

JQuery where does the data come from?

I have seen many JQuery functions using parameter function parameters. Even though I use them, It feels rather incomplete without knowing how this work in the back end.
as an example lets use the click method :
$("p").click(function(event){
event.preventdefault();
alert("testing 123");
});
In this code, if I used, "this" inside the method, it will give me the "p" element itself.
However, I cannot figure out the "event" parameter get assigned with something.
shouldn't there be some place with a code bit like
var event = blah blah;
so that the event parameter has some values?
I have clicked the actual JQuery file by pressing f12 and it goes to a function like this
jQuery.fn[ name ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
I cannot see any place of that filling or assigning something to the parameter named "event"
I have the same problem with $.ajax requests as well,
$.ajax({
type: "POST",
url: url,
async: false,
success: function (data) { }
});
It is visible that there is someplace loading the "data" in the "data" parameter, How and where does the actual data get filled up in? when do we load actual data.
I have seen a bit of similar problem.
The declaration happens in the function parameters themselves.
Declaring data, event, or whatever you want to call it inside the function parameters (any word will work), is effectively the var data = ... statement.
In the instance of an event handler, event is passed by the browser to any function latching on to that event. In the case of the ajax call, as #Alec said, that is the data returning from the server.
jQuery is basically a wrapper that returns an object with many methods. Most of them are not that straightforward, if you want to understand more deeply, you don't have many choices except using the console and going through the source code which you can find here: https://code.jquery.com/jquery-1.12.4.js. Ideally, use uncompressed version. For some methods, it can be quite long to get to the bottom of it. The way the Click callback works is hidden pretty deep.
You can find it this way:
In the console, enter $("p").click. You'll get:
ƒ ( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
}
Which comes from here in the source code:
jQuery.each( ( "blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu" ).split( " " ),
function( i, name ) {
// Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
} );
So because you have at least an argument, it runs: this.on( name, null, data, fn ), where this is your jQuery object $('p'), name is 'click', and data is your function(event). So onto this.on():
console.log($('p').on);
ƒ ( types, selector, data, fn ) {
return on( this, types, selector, data, fn );
}
Here, function on isn't global, so it's in jQuery's closure, so back to the source code where you can find:
function on( elem, types, selector, data, fn, one ) {
...
where elem is your jQuery object $('p'), types is 'click', selector is null, data is your function(e) and fn is null. This leads to:
elem.each( function() {
jQuery.event.add( this, types, fn, data, selector );
} );
So you can find:
jQuery.event = {
global: {},
add: function( elem, types, handler, data, selector ) {
...
Where you can find an addEventListener :
elem.addEventListener( type, eventHandle, false );
On addEventListener, the callback has the event parameter, which is native javascript. In jQuery, the callback is eventHandle, so let's find this one:
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== "undefined" &&
( !e || jQuery.event.triggered !== e.type ) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
So it returns the function dispatch, so now the callback is this :
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) , where arguments is e (the original addEventListener Event). So find dispatch:
dispatch: function( event ) {
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event );
...
So what is this event.fix:
fix: function( event ) {
if ( event[ jQuery.expando ] ) {
return event;
}
// Create a writable copy of the event object and normalize some properties
var i, prop, copy,
type = event.type,
originalEvent = event,
fixHook = this.fixHooks[ type ];
In here you find
event = new jQuery.Event( originalEvent );
jQuery.Event = function( src, props ) {
...
Where the event that is passed as a parameter of click is defined. You can test it by adding properties on jQuery.Event.prototype. Like this for example:
jQuery.Event.prototype.prop = 'newProp';
So, to sum up, the event in function(event), is an instance of jQuery.Event.
See
console.log($('p').click);
console.log($('p').on);
console.log(jQuery.Event)
jQuery.Event.prototype.prop = 'test';
$('p').click(function(event){console.log(event.prop)});
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<p>test</p>
For Ajax, it's probably a bit more straightforward, but again, if you want to know exactly, not much else you can do but go through the source code.
In reference to your first question, about the event parameter, the event is your click. It will never be explicitly declared like a normal variable. It is just a parameter, and in your example the click is the event.
In reference to your ajax question, the data parameter is what is coming back from your backend after a successful post. For example, I use ajax calls to send some information from my frontend. My backend then uses that information to send back data to frontend inside that success: function(data), like JSON. JSON would be the data parameter.

jQuery cssHooks is not called

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 );

jQuery plug-in data access -- performance hit?

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.

JavaScript Namespace with jQuery

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.

Jquery plugin using methods needs to call itself

I'm following the tutorial here: http://docs.jquery.com/Plugins/Authoring
I just wanted to create a simple plugin for a project we are working on that would add +/- signs next to an input box that could be used to increment the value of the box.
So I'm reading the tutorial and doing the section that talks about having multiple methods and everything is going fine, except for one small hitch.
So here is the code:
(function( $ ) {
var methods = {
init: function(options) {
if (options) {
$.extend(settings, options);
}
this.css('float', 'left');
this.after('<div class="increment-buttonset"><div class="increment-button" style="float: left;">+</div><div class="decrement-button">-</div></div><br style="clear: both" />');
$('.increment-button').click(function() {
$.increment('change');
});
return this;
},
change: function() {
// Increment Decrement code would go here obviously
return this;
}
};
$.fn.increment = function(method) {
var settings = {
'step': 1
};
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);
}
};
})( jQuery );
The issue is the $.increment('change');
I want to bind the +/- buttons on click to call the increment('change'). I get errors when I do that.
Uncaught TypeError: Object function (a,b){return new c.fn.init(a,b)} has no method 'increment'
I've tried it without the $. but that just tells me increment isn't defined yet. Am I messing some syntax up here or just going about this completely wrong?
The solution is pretty simple, you were calling the method the wrong way. It should be
$('.increment-button').click(function() {
$(this).increment('change');
});
That is, because function added to $.fn.functionName can only be called on a jQuery elements like $(selector).functionName.

Categories