A framework is something that usually forces a certain way
of implementing a solution, whereas jQuery is just a tool to make implementing
what you want to do easier.
a library - a collection of functions which are useful when writing web
apps. Your
code is in charge and it calls into the library when it sees fit.
E.g., jQuery.
frameworks - a particular implementation of a web application, where
your code fills in the details.
The
framework is in charge and it calls into your code when it needs something app
specific.
E.g., durandal,
ember, etc.
The purpose of jQuery is to make it much
easier to use JavaScript on your website.
jQuery takes a lot of common tasks that
require many lines of JavaScript code to accomplish, and wraps them into
methods that you can call with a single line of code.
jQuery also simplifies a lot of the
complicated things from JavaScript, like AJAX calls and DOM manipulation.
The jQuery library contains the following
features:
HTML/DOM manipulation
CSS manipulation
HTML event methods
Effects and animations
AJAX
Utilities
The advantages of jQuery
The main advantage of jQuery is that it is
much easier than its competitors. You can add plugins easily, translating this into a
substantial saving of time and effort. In fact, one of the main reasons why Resig and his team
created jQuery was to buy time (in the web development world, time matters a lot).
The open source license of jQuery allows the
library to always have constant and fast support, constantly publishing updates. The jQuery
community is active and extremely hardworking.
Another advantage of jQuery over its
competitors such as Flash and pure CSS is its excellent integration with AJAX.
In summary:
- jQuery is flexible and fast for web
development
- It comes with an MIT license and is Open
Source
- It has an excellent support community
- It has Plugins
- Bugs are resolved quickly
- Excellent integration with AJAX
The disadvantages of jQuery
One of the main disadvantages of jQuery is
the large number of published versions in the short time. It does not
matter if you are running the latest version of jQuery, you will have to host
the library yourself (and update it constantly), or download the library from Google (attractive,
but can bring incompatibility problems with the code).
In addition to the problem of the versions,
other disadvantages that we can mention:
jQuery is easy to install and learn,
initially. But
it’s not that
easy if we compare it with CSS
If jQuery is improperly implemented as a
Framework, the development environment can get out of control.
jQuery Selectors
jQuery selectors allow you to select and
manipulate HTML element(s).
jQuery selectors are used to "find" (or
select) HTML
elements based on their name, id, classes, types, attributes, values of
attributes and much more. It's based on the existing CSS Selectors, and in addition,
it has some own custom selectors.
All selectors in jQuery start with the dollar
sign and parentheses: $().
The element Selector
The jQuery element selector selects elements
based on the element name.
You can select all <p> elements on a
page like this:
$("p")
Example
When a user clicks on a button, all <p>
elements will be hidden:
Example
$(document).ready(function(){
$("button").click(function(){
$("p").hide();
});
});
The #id Selector
The jQuery #id selector uses the id attribute
of an HTML tag to find the specific element.
An id should be unique within a page, so you
should use the #id selector when you want to find a single, unique element.
To find an element with a specific id, write
a hash character, followed by the id of the HTML element:
$("#test")
Example
When a user clicks on a button, the element
with id="test" will
be hidden:
Example
$(document).ready(function(){
$("button").click(function(){
$("#test").hide();
});
});
The .class Selector
The jQuery .class selector finds elements with a specific
class.
To find elements with a specific class, write
a period character, followed by the name of the class:
$(".test")
Example
When a user clicks on a button, the elements
with class="test" will
be hidden:
Example
$(document).ready(function(){
$("button").click(function(){
$(".test").hide();
});
});
Common Selectors Overview
Example
|
Classification
|
Explanation
|
h1
|
Type Selector
|
Selects an element
by its type
|
.tagline
|
Class Selector
|
Selects an element
by the class attribute value, which may be reused multiple times per page
|
#intro
|
ID Selector
|
Selects an element by the ID attribute
value, which is unique and to only be used once per page
|
Child Selectors Overview
Example
|
Classification
|
Explanation
|
article h2
|
Descendant Selector
|
Selects an element
that resides anywhere within an identified ancestor element
|
article > p
|
Direct Child
Selector
|
Selects an element that resides immediately
inside an identified parent element
|
Sibling Selectors Overview
Example
|
Classification
|
Explanation
|
h2 ~ p
|
General Sibling
Selector
|
Selects an element
that follows anywhere after the prior element, in which both elements share
the same parent
|
h2 + p
|
Adjacent Sibling
Selector
|
Selects an element
that follows directly after the prior element, in which both elements share
the same parent
|
Attribute Selectors
Overview
Example
|
Classification
|
Explanation
|
a[target]
|
Attribute Present
Selector
|
Selects an element
if the given attribute is present
|
a[href="http://google.com/"]
|
Attribute Equals
Selector
|
Selects an element
if the given attribute value exactly matches the value stated
|
a[href*="login"]
|
Attribute Contains
Selector
|
Selects an element
if the given attribute value contains at least once instance of the value
stated
|
a[href^="https://"]
|
Attribute Begins
With Selector
|
Selects an element
if the given attribute value begins with the value stated
|
a[href$=".pdf"]
|
Attribute Ends With
Selector
|
Selects an element
if the given attribute value ends with the value stated
|
a[rel~="tag"]
|
Attribute Spaced
Selector
|
Selects an element
if the given attribute value is whitespace-separated with one word being exactly as
stated
|
a[lang|="en"]
|
Attribute Hyphenated
Selector
|
Selects an element
if the given attribute value is hyphen-separated and begins with the word stated
|
The Document
Object Model (DOM) is a
programming API for HTML and XML documents. It defines the logical structure of documents
and the way a document is accessed and manipulated. In the DOM specification, the term "document" is
used in the broad sense - increasingly, XML is being used as a way of representing
many different kinds of information that may be stored in diverse systems, and
much of this would traditionally be seen as data rather than as documents. Nevertheless,
XML presents this data as documents, and the DOM may be used to manage this
data.
The Document
Object Model originated as a specification to allow JavaScript scripts and Java
programs to be portable among web browsers. Dynamic HTML was the immediate ancestor of
the Document Object Model, and it was originally thought of largely in terms of
browsers. However, when
the Document Object Model Working Group was formed, it was also joined by
vendors in other domains, including HTML or XML editors and document
repositories. Several of
these vendors had worked with SGML before XML was developed; as a result, the
Document Object Model has been influenced by SGML Groves and the HyTime
standard. Some of these
vendors had also developed their own object models for documents in order to
provide programming APIs for SGML/XML editors or document repositories, and these object
models have also influenced the Document Object Model.
Benefits of
using jQuery
* Search
Engine Optimized – While
search engines are getting better at being able to read content within some
Flash, everything within jQuery is setup as text. This means it is completely readable to all
the search engines, exposing all your keyword rich content.
* Save
Time – Five lines of
jQuery are equivalent to 25 lines of conventional JavaScript code. This means
smaller files and faster loading web pages.
* Plug-ins – There are an
abundance of plug-ins
on the web that make creating special effects simple and fast for web
developers.
* Help?
– With an
abundance of plug-ins
comes with an abundance of help. There is a large helpful support community on the web to
help you quickly remedy any bug issues.
* That
was easy! – jQuery has
easy implementation for web developers in comparison to other applications.
* Cross
Browser Friendly – jQuery
is currently the most popular JavaScript library and works in all browsers.
* FREE! – free, open
source software.
* Mobile
Devices – jQuery is
supported by any mobile device whose web browser supports JavaScript. A lot of
mobile devices like iPads and iPhones don’t run Flash at all.
Simplifies AJAX
* Wow
Factor – Web
developers use jQuery to make web pages more exciting, interactive, cleaner, and
more user friendly. Make
your users go WOW!
Many JavaScript libraries use $ as a function or
variable name, just as jQuery does. In jQuery's case, $ is just an alias for jQuery,
so all functionality is available without using $. If you need to use another JavaScript library
alongside jQuery, return control of $ back to the other library with
a call to $.noConflict(). Old
references of $ are saved during jQuery initialization; noConflict() simply
restores them.
If for some reason two versions of jQuery are loaded (which is not
recommended),
calling $.noConflict( true ) from
the second version will return the globally scoped jQuery variables to those of
the first version.
1
2
3
4
5
6
|
<script src="other_lib.js"></script>
<script src="jquery.js"></script>
<script>
$.noConflict();
// Code that uses other library's
$ can follow here.
</script>
|
This technique is especially effective in conjunction with
the .ready() method's
ability to alias the jQuery object, as within callback passed to .ready() you can
use $ if you wish without fear of conflicts later:
1
2
3
4
5
6
7
8
9
|
<script src="other_lib.js"></script>
<script src="jquery.js"></script>
<script>
$.noConflict();
jQuery( document ).ready(function( $ ) {
// Code that uses jQuery's $ can
follow here.
});
// Code that uses other library's
$ can follow here.
</script>
|
If necessary, you can free up the jQuery name as
well by passing true as an argument to the method. This is
rarely necessary, and if you must do this (for example, if you need to use multiple
versions of the jQuery library on the same page), you need to consider that most plug-ins rely on
the presence of the jQuery variable and may not operate correctly in
this situation.
Examples:
Map the original object that was referenced by $ back to $.
1
2
3
4
5
|
jQuery.noConflict();
// Do something with jQuery
jQuery( "div p" ).hide();
// Do something with another
library's $()
$( "content" ).style.display = "none";
|
Revert the $ alias and then create and execute a function
to provide the $ as a jQuery alias inside the function's scope. Inside the
function the original $ object is not available. This works well for most plugins that don't
rely on any other library.
1
2
3
4
5
6
7
8
|
jQuery.noConflict();
(function( $ ) {
$(function() {
// More code using $ as alias to
jQuery
});
})(jQuery);
// Other code using $ as an alias
to the other library
|
Create a different alias instead of jQuery to use in the
rest of the script.
1
2
3
4
5
6
7
|
var j = jQuery.noConflict();
// Do something with jQuery
j( "div p" ).hide();
// Do something with another
library's $()
$( "content" ).style.display = "none";
|
Completely move jQuery to a new namespace in another object.
1
2
|
var dom = {};
dom.query = jQuery.noConflict( true );
|
Result:
1
2
3
4
5
6
7
8
|
// Do something with the new
jQuery
dom.query( "div p" ).hide();
// Do something with another
library's $()
$( "content" ).style.display = "none";
// Do something with another
version of jQuery
jQuery( "div > p" ).hide();
|
Load two versions of jQuery (not recommended). Then, restore jQuery's globally scoped
variables to the first loaded jQuery.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery.noConflict demo</title>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<div id="log">
<h3>Before $.noConflict(true)</h3>
</div>
<script src="https://code.jquery.com/jquery-1.6.2.js"></script>
<script>
var $log = $( "#log" );
$log.append( "2nd loaded jQuery version ($): " + $.fn.jquery + "<br>" );
// Restore globally scoped jQuery
variables to the first version loaded
// (the newer version)
jq162 = jQuery.noConflict( true );
$log.append( "<h3>After $.noConflict(true)</h3>" );
$log.append( "1st loaded jQuery version ($): " + $.fn.jquery + "<br>" );
$log.append( "2nd loaded jQuery version (jq162):
" + jq162.fn.jquery + "<br>" );
</script>
</body>
</html>
|
Advanced Plugin Concepts
linkProvide
Public Access to Default Plugin Settings
An improvement we can, and should, make to the code above
is to expose the default plugin settings. This is important because it makes it very
easy for plugin users to override/customize the plugin with minimal code. And this is
where we begin to take advantage of the function object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// Plugin definition.
$.fn.hilight = function( options ) {
// Extend our default options
with those provided.
// Note that the first argument
to extend is an empty
// object – this is to keep from
overriding our "defaults" object.
var opts = $.extend( {},
$.fn.hilight.defaults, options );
// Our plugin implementation code
goes here.
};
// Plugin defaults – added as a property on our
plugin function.
$.fn.hilight.defaults = {
foreground: "red",
background: "yellow"
};
|
Now users can include a line like this in their scripts:
1
2
3
|
// This needs only be called once
and does not
// have to be called from within
a "ready" block
$.fn.hilight.defaults.foreground = "blue";
|
And now we can call the plugin method like this and it will
use a blue foreground color:
1
|
$( "#myDiv" ).hilight();
|
As you can see, we've allowed the user to write a single
line of code to alter the default foreground color of the plugin. And users can
still selectively override this new default value when they want:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// Override plugin default
foreground color.
$.fn.hilight.defaults.foreground = "blue";
// ...
// Invoke plugin using new
defaults.
$( ".hilightDiv" ).hilight();
// ...
// Override default by passing
options to plugin method.
$( "#green" ).hilight({
foreground: "green"
});
|
Provide
Public Access to Secondary Functions as Applicable
This item goes hand-in-hand with the previous item and is an interesting way to
extend your plugin (and
to let others extend your plugin). For example, the implementation of our plugin may define a
function called "format" which
formats the hilight text. Our plugin may now look like this, with the default
implementation of the format method defined below the hilight function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// Plugin definition.
$.fn.hilight = function( options ) {
// Iterate and reformat each
matched element.
return this.each(function() {
var elem = $( this );
// ...
var markup = elem.html();
// Call our format function.
markup = $.fn.hilight.format( markup );
elem.html( markup );
});
};
// Define our format function.
$.fn.hilight.format = function( txt ) {
return "<strong>" + txt + "</strong>";
};
|
We could have just as easily supported another property on
the options object that allowed a callback function to be provided to override
the default formatting. That's another excellent way to support customization of
your plugin. The technique
shown here takes this a step further by actually exposing the format function
so that it can be redefined. With this technique it would be possible for others to ship
their own custom overrides of your plugin – in other words, it means others can write
plugins for your plugin.
Considering the trivial example plugin we're building in
this article, you may be wondering when this would ever be useful. One real-world example
is the Cycle Plugin. The Cycle
Plugin is a slideshow plugin which supports a number of built-in transition
effects – scroll,
slide, fade, etc. But
realistically, there is no way to define every single type of effect that one
might wish to apply to a slide transition. And that's where this type of extensibility
is useful. The Cycle
Plugin exposes a "transitions" object
to which users can add their own custom transition definitions. It's defined
in the plugin like this:
1
2
3
4
5
|
$.fn.cycle.transitions
= {
// ...
};
|
This technique makes it possible for others to define and
ship transition definitions that plug-in to the Cycle Plugin.
Keep
Private Functions Private
The technique of exposing part of your plugin to be
overridden can be very powerful. But you need to think carefully about what parts of your
implementation to expose. Once it's exposed, you need to keep in mind that any
changes to the calling arguments or semantics may break backward compatibility. As a general
rule, if you're not sure whether to expose a particular function, then you
probably shouldn't.
So how then do we define more functions without cluttering
the namespace and without exposing the implementation? This is a job for
closures. To
demonstrate, we'll add another function to our plugin called "debug". The
debug function will log the number of selected elements to the console. To create a
closure, we wrap the entire plugin definition in a function (as detailed
in the jQuery Authoring Guidelines).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// Create closure.
(function( $ ) {
// Plugin definition.
$.fn.hilight = function( options ) {
debug( this );
// ...
};
// Private function for debugging.
function debug( obj ) {
if ( window.console &&
window.console.log ) {
window.console.log( "hilight
selection count: " + obj.length );
}
};
// ...
// End of closure.
})( jQuery );
|
Our "debug" method cannot be accessed from outside of the closure and
thus is private to our implementation.
Bob
and Sue
Let's say Bob has created a wicked new gallery plugin (called "superGallery") which
takes a list of images and makes them navigable. Bob's thrown in some animation to make it
more interesting. He's
tried to make the plugin as customizable as possible, and has ended up with
something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
jQuery.fn.superGallery = function( options ) {
// Bob's default settings:
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
delay: "quite long",
getTextFromTitle: true,
getTextFromRel: false,
getTextFromAlt: false,
animateWidth: true,
animateOpacity: true,
animateHeight: true,
animationDuration: 500,
clickImgToGoToNext: true,
clickImgToGoToLast: false,
nextButtonText: "next",
previousButtonText: "previous",
nextButtonTextColor: "red",
previousButtonTextColor: "red"
};
var settings = $.extend( {},
defaults, options );
return this.each(function() {
// Plugin code would go here...
});
};
|
The first thing that probably comes to your mind (OK, maybe not
the first) is the
prospect of how huge this plugin must be to accommodate such a level of
customization. The plugin,
if it weren't fictional, would probably be a lot larger than necessary. There are
only so many kilobytes people will be willing to spend!
Now, our friend Bob thinks this is all fine; in fact, he's
quite impressed with the plugin and its level of customization. He believes
that all the options make for a more versatile solution, one which can be used
in many different situations.
Sue, another friend of ours, has decided to use this new
plugin. She has set
up all of the options required and now has a working solution sitting in front
of her. It's only
five minutes later, after playing with the plugin, that she realizes the
gallery would look much nicer if each image's width were animated at a slower
speed. She hastily
searches through Bob's documentation but finds no animateWidthDuration option!
Do
You See The Problem?
It's not really about how many options your plugin has; but
what options it has!
Bob has gone a little over the top. The level of customization he's offering,
while it may seem high, is actually quite low, especially considering all the
possible things one might want to control when using this plugin. Bob has made
the mistake of offering a lot of ridiculously specific options, rendering his
plugin much more difficult to customize!
A
Better Model
So it's pretty obvious: Bob needs a new customization model, one
which does not relinquish control or abstract away the necessary details.
The reason Bob is so drawn to this high-level
simplicity is that the jQuery framework very much lends itself to this mindset. Offering
a previousButtonTextColor option
is nice and simple, but let's face it, the vast majority of plugin users are
going to want way more control!
Here are a few tips which should help you create a better
set of customizable options for your plugins:
Don't
Create Plugin-specific
Syntax
Developers who use your plugin shouldn't have to learn a
new language or terminology just to get the job done.
Bob thought he was offering maximum customization with
his delay option (look above). He made it so that with his plugin you can
specify four different delays, "quite short," "very short," "quite long," or "very long":
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
var delayDuration = 0;
switch ( settings.delay ) {
case "very short":
delayDuration = 100;
break;
case "quite short":
delayDuration = 200;
break;
case "quite long":
delayDuration = 300;
break;
case "very long":
delayDuration = 400;
break;
default:
delayDuration = 200;
}
|
Not only does this limit the level of control people have,
but it takes up quite a bit of space. Twelve lines of code just to define the delay time is a bit
much, don't you think? A better way to construct this option would be to let
plugin users specify the amount of time (in milliseconds) as a number, so that no processing of the
option needs to take place.
The key here is not to diminish the level of control
through your abstraction. Your abstraction, whatever it is, can be as simplistic as
you want, but make sure that people who use your plugin will still have that
much-sought-after low-level control! (By low-level I mean
non-abstracted.)
Give
Full Control of Elements
If your plugin creates elements to be used within the DOM,
then it's a good idea to offer plugin users some way to access those elements. Sometimes
this means giving certain elements IDs or classes. But note that your plugin shouldn't rely on
these hooks internally:
A bad implementation:
1
2
3
4
|
// Plugin code
$( "<div class='gallery-wrapper' />" ).appendTo( "body" );
$( ".gallery-wrapper" ).append( "..." );
|
To allow users to access and even manipulate that
information, you can store it in a variable containing the settings of your
plugin. A better
implementation of the previous code is shown below:
1
2
3
4
5
6
7
|
// Retain an internal reference:
var wrapper = $( "<div
/>" )
.attr( settings.wrapperAttrs )
.appendTo( settings.container );
// Easy to reference later...
wrapper.append( "..." );
|
Notice that we've created a reference to the injected
wrapper and we're also calling the .attr() method to add any specified attributes to the element. So, in our
settings it might be handled like this:
1
2
3
4
5
6
7
8
9
10
|
var defaults = {
wrapperAttrs : {
class: "gallery-wrapper"
},
// ... rest of settings ...
};
// We can use the extend method
to merge options/settings as usual:
// But with the added first
parameter of TRUE to signify a DEEP COPY:
var settings = $.extend( true,
{}, defaults, options );
|
The $.extend() method
will now recurse through all nested objects to give us a merged version of both
the defaults and the passed options, giving the passed options precedence.
The plugin user now has the power to specify any attribute
of that wrapper element so if they require that there be a hook for any CSS
styles then they can quite easily add a class or change the name of the ID
without having to go digging around in plugin source.
The same model can be used to let the user define CSS
styles:
1
2
3
4
5
6
7
8
9
10
|
var defaults = {
wrapperCSS: {},
// ... rest of settings ...
};
// Later on in the plugin where
we define the wrapper:
var wrapper = $( "<div
/>" )
.attr( settings.wrapperAttrs )
.css( settings.wrapperCSS ) // ** Set CSS!
.appendTo( settings.container );
|
Your plugin may have an associated stylesheet where
developers can add CSS styles. Even in this situation it's a good idea to offer some
convenient way of setting styles in JavaScript, without having to use a
selector to get at the elements.
Provide
Callback Capabilities
What is a callback? – A callback is
essentially a function to be called later, normally triggered by an event. It's passed
as an argument, usually to the initiating call of a component, in this case, a
jQuery plugin.
If your plugin is driven by events then it might be a good
idea to provide a callback capability for each event. Plus, you can create your own custom events
and then provide callbacks for those. In this gallery plugin it might make sense to add an "onImageShow" callback.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
var defaults = {
// We define an empty anonymous
function so that
// we don't need to check its
existence before calling it.
onImageShow : function() {},
// ... rest of settings ...
};
// Later on in the plugin:
nextButton.on( "click", showNextImage );
function showNextImage() {
// Returns reference to the next
image node
var image = getNextImage();
// Stuff to show the image here...
// Here's the callback:
settings.onImageShow.call( image );
}
|
Instead of initiating the callback via traditional means (adding
parenthesis) we're calling
it in the context of image which will be a reference to the image
node. This means that
you have access to the actual image node through the this keyword
within the callback:
1
2
3
4
5
6
7
|
$( "ul.imgs li" ).superGallery({
onImageShow: function() {
$( this ).after( "<span>" + $( this ).attr( "longdesc" ) + "</span>" );
},
// ... other options ...
});
|
Similarly you could add an "onImageHide" callback and
numerous other ones. The
point of callbacks is to give plugin users an easy way to add additional
functionality without digging around in the source.