$ = jQuery = window.jQuery = window.$
There are other libraries that use $ as a global designator, and jQuery can release it for the global scope.
The $ shortcut is convent, but you must be careful how you use it, especially when writing a plugin.
For the basic jQuery
<script type="text/javascript" src="/libs/jquery-1.7.1.min.js"></script>
Or hotlink to one of the many CDN hosts, like google
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"> </script>
Back in the old days, before jQuery, the only way to run code once the page was fully loaded was with onload
<script type="text/javascript"> window.onload = function(){ alert("Hello World!"); }
</script>
But this isn't called until the entire page has downloaded, including images and the page has rendered for the first time.
jQuery gives us the document ready function, which is called once the main content has been downloaded but before images have finished or the initial page render.
<script type="text/javascript">$(document).ready(function(){ $(document).ready(function(){
alert("Hello World!"); }); </script>
Selectors are the core of jQuery's coolness.
Use standard CSS style selectors to easily filter content to just what you need.
Additional selectors for even more power.
All jQuery selectors return a wrapper, an instance of the jQuery object that contains a reference to all the native elements found by the selection.
HTML Snippet
<div>
<h1>Headline</h1>
<p>Paragraph 1</p>
<p>Paragraph 1</p>
</div>
JavaScript
$('p');
Selects all of the <p> tags (in this case two items)
The wrapper object automatically applies commands to everything in the set.
HTML Snippet
<div class="inactive">
<h1>Headline</h1>
<p>Paragraph 1</p>
<p>Paragraph 1</p>
</div>
jQuery
$('.inactive p').addClass('dim');
Adds the class dim to all of the paragraphs
Almost all jQuery functions return either
a) the original set of elements or
b) a modified set of elements
within a jQuery wrapper unless explicitly documented otherwise.
Save space and time by chaining one function onto another.
HTML Snippet
<ul class="first">
<li class="foo">list item 1</li>
<li>list item 2</li>
<li class="bar">list item 3</li>
</ul>
<ul class="second">
<li class="foo">list item 1</li>
<li>list item 2</li>
<li class="bar">list item 3</li>
</ul>
jQuery
$('ul.first').find('.foo').css('background-color', 'red')
.end().find('.bar').css('background-color', 'green');
jQuery
$('ul.first').find('.foo').css('background-color', 'red')
.end().find('.bar').css('background-color', 'green');
jQuery
$('ul.first').find('.foo')
.css('background-color', 'red')
.end().find('.bar')
.css('background-color', 'green')
.end();
jQuery has many easy ways to add simple animations.
example
$("#target").fadeOut(); $("#target").fadeIn() $("#target").slideDown();
etc...
A callback is a function that is called when something is finished.
For instance, here is a callback from the fadeout command.
$("#prev").fadeOut( 1000, function(){
$("#next").fadeIn( 1000 ); });
JavaScript can create functions in three ways:
Standard function definition:
function myFunction ( argument1 ){ ... }
Assign the function to a variable
var myFunction = function ( argument1 ){ ... }
Anonymous function
function ( argument1 ){ ... }
Just remember, curly brackets { } define one block of scope.
function scope1 () {
var outer = 'outer';
var scope2 = function () {
var inner = 'inner'
// outer == 'outer'
}
//inner == undefined
}
And 'this' has a different meaning inside each scope, even if they're right next to each other.
In normal JavaScript this isn't too confusing because there is usually a lot a space around functions blocks, but jQuery's heavy reliance on anonymous functions can lead to confusing situations.
As long as you're aware of waits happening, it's usually pretty easy to figure out what's going on.
We could talk a long time about scope and how this works. But for our purposes, just realize that:
A shorthand way of declaring objects and arrays.
[ ] designates an array (or a list of items stored by number 0 to X.)
[ 'one', 'two', 'three' ]
{ } designates an object with name/values pares separated by a colon.
{ name : 'value', second : 2 }
jQuery makes heavy use of JSON as a means of condensing the overall amount of code.
At it's core, a jQuery plugin is a function added to the jQuery.fn object
jQuery.fn.myPlugin = function() { // Your plugin code goes here };
To avoid issues with conflicting use of the $ shortcut, it's best practice to create a "closure function". This is simply an anonymous function that calls itself and passes a reference to the jQuery object as it's only parameter.
(function( $ ) { $.fn.myPlugin = function() { // Your plugin code goes here }; })( jQuery );
It is then safe to use the $, and also creates a private namespace where you can create private functions hidden from the world.
Inside of a jQuery plugin, sometimes this refers to the HTML object, sometimes the jQuery wrapper.
Simple rule of thumb:
One of the core aspects of jQuery, so wouldn't it be nice to add it to your plugin?
Just target each from your main plugin function (the one defined on jQuery.fn)
(function( $ ) { $.fn.fadedBox = function() { this.each(function() { $(this).css('opacity', .5 ) .css('boarder', '5px' ); }); }; })( jQuery );
to process all elements of the wrapper set.
Maintain chainability by returning a reference to the jQuery object.
Simply return this unless you need to specifically return another value.
(function( $ ) { $.fn.fadedBox = function() { return this.each(function() { $(this).css('opacity', .5 ) .css('boarder', '5px' ); }); }; })( jQuery );
A handy way to combine two or more objects.
Best way to handle default values.
var objectA = { name:'chip', height:6.5, favorateColor:'red' }; var objectB = { quest:'explain jQuery', favorateColor:'blue' }; var objectC = { favorateColor:'green' }; var result = $.extend( {}, objectA, objectB, objectC );
After using $.extend, the result is now a combination of objectA, objectB & objectC, which is equal to:
result = { name : 'chip', height : 6.5, favorateColor : 'green', quest : 'explain jQuery' }
Wrap it up and keep it safe.
It's best practice to wrap all of your functions inside your plugin namespace (the name of your plugin). That way you minimize conflicts with other plugins. To do this, first we move where we define our functionality.
$.extend({
slidePresenter :{
defaults : {
// record default properties
},
init : function( options ) {
return this.each(function(){
// init code goes here
});
},
next : function () {
// code to go to the next goes here
}
}
});
So now our plugin function adds a bit of code that checks for the correct function, and calls it for us.
$.fn.slidePresenter = function( method ) {
var sp = $.slidePresenter;
// Method calling logic
if ( sp[method] ) {
return sp[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return sp.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.slidePresenter' );
}
};
Now we can call our function one of two ways.
As a string to the main plugin:
$('#target').slidePresenter('next');
Or as a method of the plugin:
$('#target').slidePresenter.next();
Notice that we still give a target for the plugin.
Use the .data() method to ensure that you don't create a problem.
This will allow multiple instances of the same plugin on the same page without overriding each other's settings, or creating circular references.
init : function( options ) {
return this.each(function(){
var $slideshow = $(this);
var data = $slideshow.data('slidePresenter');
// If the plugin hasn't been initialized yet
if ( ! data ) {
var settings = $.extend( true, {}, $.slidePresenter.defaults, options); // Init code goes here
// Store Data for this instance of the slideshow
$(this).data('slidePresenter', {
settings : settings
});
}
});
}
(Yes I wrote it just for this presentation.)
Overview of plugin
;( function( $ ){ // this is where most of the info goes $.extend({ slidePresenter :{ ... } }); // Extend jQuery with our function $.fn.slidePresenter = function( method ) { ... }; // Private functions function transition ( $slideshow, newSlide ){ ... } function initHotKeys ( $slideshow ){ ... } function initNavigation ( $slideshow ){ ... } function selectCurrentCrumb ( $slideshow ){ ... } })( jQuery );
Main plugin function
// Extend jQuery with our function $.fn.slidePresenter = function( method ) { var sp = $.slidePresenter; // Method calling logic if ( sp[method] ) { return sp[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); } else if ( typeof method === 'object' || ! method ) { return sp.init.apply( this, arguments ); } else { $.error( 'Method ' + method + ' does not exist on jQuery.slidePresenter' ); } };
Extension object to apply namespace
$.extend({ slidePresenter :{ defaults : { transition : 'none', transitionSpeed : 0, navID : 'slidePresenterNav', showNavigation : true, showBreadcrumbs : true, loop : false, hotkeys : { next : [39,40,32], // right & down arrow, spacebar previous : [37,38] // up & left arrow } }, init : function( options ) { return this.each(function(){ var $slideshow = $(this); var data = $slideshow.data('slidePresenter'); // If the plugin hasn't been initialized yet if ( ! data ) { var settings = $.extend( true, {}, $.slidePresenter.defaults, options); var slides = []; var current = 0; var total = 0; // Init Slides $slideshow.children().hide().each(function(){ slides.push( $(this) ); }); slides[current].show(); $slideshow.addClass('active'); // Store Data for this instance of the slideshow $(this).data('slidePresenter', { settings : settings, slides : slides, current : current, animating : false }); // Init Nav elements initNavigation( $slideshow ); initHotKeys( $slideshow ); } }); }, next : function () { var data = this.data('slidePresenter'); return this.slidePresenter('goTo', data.current+1 ); }, previous : function () { var data = this.data('slidePresenter'); return this.slidePresenter('goTo', data.current-1 ); }, goTo : function ( number ) { var data = this.data('slidePresenter'); var settings = data.settings; var slides = data.slides; // Constrain to valid slides if ( number<0 ){ number = ( settings.loop ? slides.length-1 : 0 ); } else if ( number>=data.slides.length ) { number = ( settings.loop ? 0 : slides.length-1 ); } if ( data.current!=number && data.animating==false ){ transition(this, number); selectCurrentCrumb(this); } return this; } } });
Transition function
function transition ( $slideshow, newSlide ){
var data = $slideshow.data('slidePresenter');
var prev = data.slides[data.current];
var next = data.slides[newSlide];
data.current = newSlide;
var duration = data.settings.transitionSpeed/2;
var simultaneous = false;
var nextCSS = {};
var prevCSS = {};
switch ( data.settings.transition ){
case 'crossfade':
duration *= 2;
simultaneous = true;
case 'fade':
// prep next slide for animation
next.show();
next.css('opacity',0);
// CSS values to transition to
nextCSS = {
opacity: 1
};
prevCSS = {
opacity: 0
};
break;
case 'none':
default:
prev.hide();
next.show();
return $slideshow;
}
data.animating = true;
// define the animation for the next slide
var animateNext = function (){
next.animate(
nextCSS,
duration,
function (){
data.animating = false;
}
);
}
// Transition out the previous slide
prev.animate(
prevCSS,
duration,
function (){
prev.hide();
if (!simultaneous) animateNext();
}
);
if ( simultaneous ) animateNext();
return $slideshow;
}
Hotkey navigation
// setup hotkey functionality for keyboard navigation
function initHotKeys ( $slideshow ){
var data = $slideshow.data('slidePresenter');
var settings = data.settings.hotkeys;
// make a map of keyCodes and actions
var hotKeys = [];
$.each(settings, function( action, keys ){
if ( $.slidePresenter[action] ){
$.each( keys, function( index, keynum ){
hotKeys[keynum] = action;
});
}
});
// register key event for the browser window
$(window).keydown(function (e){
if ( hotKeys[e.keyCode] ){
$slideshow.slidePresenter( hotKeys[e.keyCode] );
e.preventDefault();
}
});
}
Build the navigation
// Create the navigation section
function initNavigation ( $slideshow )
{
var data = $slideshow.data('slidePresenter');
var settings = data.settings;
if ( settings.showNavigation ){
var makeBtn = function ( action, num ){
return $('<div />').attr( 'id' , ( num ? action+'_'+num : action ) )
.append( num ? num : action )
.wrapInner('<a href="#"><span /></a>')
.click( function( e ){
$slideshow.slidePresenter( action, num-1 );
e.preventDefault();
});
}
var $nav = $('<div id="'+settings.navID+'">');
if ( settings.showBreadcrumbs ){
$.each( data.slides, function ( index, value ){
$nav.append(makeBtn('goTo',index+1));
});
}
$nav.prepend(makeBtn('previous'))
.append(makeBtn('next'));
$('body').append( $nav );
selectCurrentCrumb($slideshow);
}
}
Select the breadcrumb
function selectCurrentCrumb ( $slideshow )
{
var data = $slideshow.data('slidePresenter');
$('#'+data.settings.navID)
.children()
.removeClass('selected')
.end()
.children('#goTo_'+(data.current+1))
.addClass('selected');
}
Always seek closure, wrap it up (function ( $) { //stuff })(jQuery);
Return 'this' to maintain the chain if at all possible.
Pass a single argument object for ease of extending
Only one jQuery.fn namespace per plugin
Add namespace to methods, events & data for added protection.