I was going to do a bit of a series, releasing a jQuery tip every day or week or something, but I think I’m a little too lazy to commit to something like that. So I’ve compiled them all into one post! I’ll probably add to the list at later dates so make sure to bookmark it!
Do you have a tip nobody knows about? – Add it in the comments…
$.fn
is just a shortcut tojQuery.prototype
.- You can test if a jQuery collection contains any elements by trying to access the first element, e.g.
if($(selector)[0]){...}
. - jQuery normalizes the event object across all browsers! Have a look at all the available properties/methods over here: http://docs.jquery.com/Events/jQuery.Event.
- When you create a plugin you have access to the jQuery chain’s previous object:
jQuery.fn.doSomething = function() { this; // => $('a') this.prevObject; // => $('li') // Remember chaining in your plugins: return this; }; jQuery('li').show() .find('a').doSomething(); // You could even create a new 'root' plugin: // (Returns the 'root' of a chain) jQuery.fn.root = function() { // Root is always document so we have to // go back to one before the last: var root = this; while(root.prevObject.prevObject) { root = root.prevObject; } return root; }; $('li').find('a').children().root(); // <= $('li') is returned // Using root() is the same as using end().end() in this situation
- You can namespace events! This is especially useful for plugin development:
jQuery.fn.myPlugin = function() { // Clean up after yourself! jQuery.myPlugin = { cleanUp: function() { // Remove all click handlers binded // as a result of the plugin: jQuery('*').unbind('click.myPlugin'); // ALternatively, remove ALL events: jQuery('*').unbind('.myPlugin'); } }; return this.bind('click.myPlugin', function() { // Do something... }); }; // Note, you can also namespace data: // E.g. $(elem).data('whatever.myPlugin',value);
- You can access all event handlers bound to an element (or any object) through jQuery’s event storage:
// List bound events: console.dir( jQuery('#elem').data('events') ); // Log ALL handlers for ALL events: jQuery.each($('#elem').data('events'), function(i, event){ jQuery.each(event, function(i, handler){ console.log( handler.toString() ); }); }); // You can see the actual functions which will occur // on certain events; great for debugging!
- jQuery natively supports JSONP (‘JSON with padding’) which effectively means you can make cross-domain "Ajax" requests (although not strictly Ajax since it doesn’t use XHR). For this to work the requested domain must have some JSONP API in place (it must be able wrap the JSON with a specified callback function). An example:
function getLatestFlickrPics(tag,callback) { var flickrFeed = 'http://api.flickr.com/services/feeds/photos_public.gne?tags=' + tag + '&tagmode=any&format=json&jsoncallback=?'; jQuery.getJSON(flickrFeed, callback); } // Usage: getLatestFlickrPics('ferrari', function(data){ jQuery.each(data.items, function(i, item){ $("<img/>").attr("src", item.media.m).appendTo('body'); }); });
- You might find it a little messy but jQuery enables us to create an entire DOM structure within a single chain:
// Create and inject in one chain: jQuery('<div/>') .append('<p><a href="#">Foo</a></p>') .find('p a') .click(function(){ // Do something... return false; }) .end() .append('<p><a href="#">Bar</a></p>') .find('p:eq(1) a') .click(function(){ // Do something else... return false; }) .end() .appendTo('body');
- Accessing the DOM elements within a jQuery collection is incredibly easy:
var HTMLCollection = $('div').get(); // Alternatively, if you only want the first element: $('div').get(0); $('div').get()[0]; $('div')[0];
- Not only can you bind events to DOM elements; you can also bind a custom event to ANY object!
function Widget() { // Do something... }; var myPhotoWidget = new Widget('photos'); jQuery(myPhotoWidget).bind('photoAdd', function() { // Custom event handling... }); // Trigger event: jQuery(myPhotoWidget).trigger('photoAdd');
- Finding the index of a selected element is very easy. jQuery gives us the ‘index’ method:
$('table tr').click(function(){ // Find index of clicked table row: var index = $('table tr').index(this); });
- You can create your own filter selectors. I did a post on this a while back, but take a look at an example anyway:
$.expr[':'].external = function(elem,index,match) { var url = elem.href || elem.src, loc = window.location; return !!url.match(new RegExp('^' + loc.protocol + '//' + '(?!' + loc.hostname + ')' )); }; // You can now use it within your selectors: // Find all external anchors: $('a:external'); // Find all external script elements: $('script:external'); // Determine if link is external: $('a#mylink').is(':external'); // true/false
- I see quite a lot of people still using JavaScript’s FOR or WHILE constructs to create loops in their jQuery scripts. There’s nothing wrong with this but be aware that jQuery’s ‘each’ method can also iterate over arrays and objects!
var myArr = ['apple','banana','orange']; $.each(myArr, function(index, item) { // Do something with 'item' // return false to BREAK // return true to CONTINUE });
- The ‘filter’ method accepts a String selector or a function. When using it with a function you must return false to remove the element from the stack and true to keep it:
$('div').filter(function(){ return this.childNodes.length > 10; // Must return a Boolean });
- You don’t have to give new elements IDs or classes to reference them later, just cache them into a variable:
var myInjectedDiv = $('<div/>').appendTo('body'); // Use 'myInjectedDiv' to reference the element: myInjectedDiv.bind('click', function(){ // ... });
- jQuery’s ‘map’ method is incredibly useful, the passed function will be run on every item of the passed array (or object) and whatever the function returns each time is added to the new array, take a look:
// Create an array containing all anchor HREF attributes: var URLs = $.map($('a'), function(elem, index){ return elem.href; }); // URLs = ['http://google.com', 'http://whatever.com', 'http://yahoo.com']
- This isn’t jQuery related but it can be very useful. When you need to compare two different ways of doing something (performance-wise) you can use the Firebug console to log the time taken to complete a chunk of code, for example:
console.time('My first method'); // Do something... console.timeEnd('My first method'); console.time('My second method'); // Do something else... console.timeEnd('My second method'); // Firebug will log the time (in milliseconds) taken // to complete each chunk...
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Oh, this is very helpful. Thank you! π
Very useful, as always. Thanks, James!
Thanks a lot for these tips!
But it’s a pain to click “show code” on each to be able to read… π
Bad ass, once again…well done.
Nice list, on the second point you can also use this
There’s no need to instantiate created HTML chunks before inserting them;
You can specify the scope of a selector:
Is is often useful:
But there’s always a way to be more efficient and lazy:
Make your jQuery plugin bulletproof by encapsulating it;
That way you are sure that “$” always refers to a jQuery instance, even if you mix in more libraries (like Prototype).
That’s all I could think of right now..
You might want to add a “return this” to the jQuery.fn.doSomething function (just to show proper chaining.)
Very good list though.
Thank you for the great tips! Appreciate it.
Great tips. Thanks!
Great post, I discovered a great blog too. π
Great tips, bookmarked!
Great tips!
One that has gotten me before is the event queuing. If you want to ensure that events occur one after the other use the callbacks.
Just an FYI, most of this, if not all, can be done in MooTools.
This is a better example, because I just tested it. Small difference that occurs but knowing this will help you out in managing your desired effects.
Some of your tips conflict with other things I’ve read about jQuery.
http://jquery-howto.blogspot.com/2009/02/5-easy-tips-on-how-to-improve-code.html
They said using .each on large datasets is slower than a for loop. What has your experience been with it?
Thanks for all the comments. Also, thank you to those who posted some jQuery tips! π
@Nicolas Hoizey, sorry about that, I’ve removed that “feature” for now…
@Dan G. Switzer, II, good idea, I’ll add it now!
@h3, great tips!
@Timothy, I don’t think anyone is denying that. Afterall it’s all JavaScript so whatever can be done with one framework can definitely be done with any other.
@Jon Erickson, Good advice… Although it can get a little bloated, nested callback within nested callback etc.
@ernest leitch, jQuery is simply an abstraction and so will, by definition, be slower and probably less efficient than pure JavaScript. Using a while/for loop (assuming proper construction) will almost always be faster than any library’s stock iterator. On very large data-sets it would make more sense to iterate without the aid of a library. But then if performance is your prime concern using a library for anything probably isn’t a good idea.
Some great tips π
For the index one, I’d prefer to do it like:
It looks a little longer but I think it’s computationally cheaper…
You are not entirely correct about the JSONP tip. The requested domain must support JSONP. if it does not support it, you are out of luck, because the data you get back will not be wrapped in a function call, and you will get an error.
i recently struggled with just this. I ended up having to make a jsonp proxy in perl:
Using jQuery with NYT json API
Your second tip
is not so great, because if there are no elements, this tries to access an invalid element, and may fire an error. You might test if the array object has the particular key, but like Matt Hobbs said, it’s easier and more “purist” to use the .length property.
Good stuff, James!
@Kelvin Luck, Ah, very clever! I never really tried out that
event.data
thing. Thanks.@mk, Sorry, I’ll adjust it so it states there needs to be a JSONP API (with callback).
@Jaanus, My solution will never fire an error. Trying to access [0] is the same as trying to access any property – if it exists it will return the property and if it doesn’t it will return
undefined
which evaluates tofalse
. Not sure what you mean by more “purist” – either way is absolutely fine.@malsup, Thanks!
jquery is so badass
The index method is cumbersome, I prefer
Nice post
@Jaanus, My solution will never fire an error. Trying to access [0] is the same as trying to access any property – if it exists it will return the property and if it doesnβt it will return undefined which evaluates to false. Not sure what you mean by more βpuristβ – either way is absolutely fine.
I agree with the [0] statement. I won’t fire and error unless you try to access properties on it.
Another way is to use the .size() method.
Either way, the example will most likely lead to consumers of this post doing a double selection:
Make sure you create a pointer variable:
Some great exaples of jquery,:D thanks a lot james
Another nice tutorial from James. Really appreciate the amount of thought and work behind this. really shows. Extremely useful.
Keep ‘hem coming and Congrats !
JQuery is way ahead of prototype and mootools. JavaScript can be really tricky, and I find this article really helpful.
Great post, thank you for the effort.
One minor remark: you say the the event object is normalized across all browsers. This is true, but I do like to point out that the event triggers are not normalized. For example, listening to keypresses works differently in different browsers, the same goes for the submit event, which is different in Opera.
jQuery’s Event object has “keyCode” for keyup/keydown events and “which” for keypress and mouse events (which button was pressed). I think “which” is reliable cross browser, it gives you the character code of keypress events.
nice stuff.
I think there is a little type here, the second ‘i’ should be replaced by a ‘j’
Great post! Found the answers to several JQuery questions that I had. Thanks for taking the time to post this!
I was a little confused by the namespacing events example:
1. Why put the cleanUp function inside jQuery.myPlugin = {…}, couldn’t you just use var cleanUp = function() {…} ?
2. If you needed to declare some variables for you plugin’s state, where would you put them: in the jQuery.fn.myPlugin = function() {…} block; or the jQuery.myPlugin = {…} block?
My apologies if these are stupid questions. π
Great list! There was at least one I needed! π Thanks!
Lots of peeps do not actually realize jQuery(document).ready(); is really and EVENT HANDLER! – handle the .ready() event.
Call a function on page load:
function MyFunction()
{
// do lots of my stuff here
}
jQuery(document).ready(MyFunction());
OR
jQuery(document).ready(function(){// do little stuff here});
@Mark Schultheiss
Yes, ready is an event handler (a great one, since it fires when the DOM is ready, rather than waiting for images to load).
However, when using it, you want to pass a reference to the function, not execute the function itself.
Rather than:
Do:
Also, one of the nicest things jQuery does, is if you pass it a function straight away, that is a shortcut to the document ready event.
Is exactly the same as: