Friday, June 15, 2012

Things you may not know about jQuery.


Do you have a tip nobody knows about? – Add it in the comments…
  • $.fn is just a shortcut to jQuery.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...

No comments:

Post a Comment