About

Nick Carter is a programmer from Alexandria, VA. His interest-spectrum runs the gamut from JavaScript to hip-hop to sound synthesis & sampling to video games to gadgets and new technology. He can usually be found either in front of or near a computer screen, often reading a new book on coding practices or one by Douglas Coupland or Chuck Palahniuk. He also greatly enjoys watching movies and sitcoms with his wife, Terri, however often their tastes may diverge. He is one-half of Breathe Media. His music can be heard on DC Beats.

Carter works for RideCharge, where he specializes in front-end development with Ruby on Rails.

He can be reached at thynctank@thynctank.com.

Loading...

Gracefully Degrading Widgets, Part 1: Tabs Comments

First, the code again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    $(function() {
      $(".tab").click(function(){
        var tabset = $(this).parents(".tabset");
        tabset.find(".active_tab").removeClass("active_tab");
        $(this).addClass("active_tab");
 
        var old_content = tabset.find(".active_tab_content");
 
        old_content.fadeOut("fast", function(){
          old_content.removeClass("active_tab_content");
          var index = tabset.find(".tab").index(tabset.find(".active_tab")[0]);
          var current_content = $(tabset.find(".tab_content")[index]);
          current_content.addClass("active_tab_content");
          current_content.fadeIn("fadeIn");
        });
      });
    });

Line 1 calls the $ function. This function is known as the selector, and is quite powerful in jQuery. Using CSS syntax, you can narrow your selection down to a single item, or select every instance of a particular type of element on a page. Our example here shows a special use of the $ function which, when passed a literal function or function name, simply calls that function when the DOM has finished loading. That sentence (and this whole paragraph) is a bit loaded, so let’s take a moment to cover the topics presented in it.

Functions are chunks of code which a programmer defines (languages also come with many functions pre-defined) to perform a given task or tasks any time he needs them done. Functions are callable, meaning you can run them at any time simply by invoking their names, and occasionally passing them some parameters if the functions in question make use of custom input. In JavaScript, you define a reusable function simply by writing: function funcname() {//some code here}

Here, we’ve seen a variation of this, called a function literal or anonymous function, which is useful if you need to define a function which will never be called directly anywhere in your code. This anonymous function is passed to $() as its only parameter.

The DOM, or Document Object Model, is the browser’s in-memory construct of the Web page. JavaScript provides plenty of built-in functions for dealing with the DOM, but jQuery and similar libraries make it even easier to deal with than the native functions.

Within the special call to the function $(), there is another call to $(). This second call is the normal usage, and takes CSS-style selector syntax to select an element or group of elements. In this case, we’ve selected all tab elements, for the purpose of adding an event handler. An event handler is a block of JavaScript (a function) which executes when an object receives a specific event, which in this case is the “click” event. (accomplished by calling the click() function of the selected group of elements) As you might guess, this means that when the tab is clicked, it will react by executing the code therein. This code, as we’ll see, is an anonymous function.

Anonymous functions are just like regular functions, only they have no names, so they can’t be called directly. Anonymous functions are useful when you have a piece of functionality you want to assign to something and don’t want to bother setting up and calling a function. So it’s a shortcut, and smart programmers love shortcuts. Within this anonymous event handler, we find the entire encompassing tabset of the clicked-on tab (which itself is obtained by calling $(this)). jQuery provides many handy functions for navigating through the DOM structure, with less fuss than the native DOM code available in your browser. The function parents is one of these. It obtains any matching parent element of the element object it is called on, in this case finding the containing div with “tabset” for set in its class attribute. We assign this tabset to a local variable named, appropriately, tabset. Local variables cease to exist when they are no longer needed. In programming jargon this is referred to as going out of scope.

We then find the currently-active tab within that tabset using the similar find() function. Now that we have the pieces we need to fade from current tab to new tab, we’re good to set things in motion. On to the effect call. First we remove the “active_tab” class from the recently-active tab, and add the class to the just-clicked tab. We do so by calling removeClass(). This function takes a string of the class name you want to remove. This has the effect of switching the tabs themselves. But now we’ve got to deal with the content area.

Calling fadeOut on the old_content object, (which we obtain via another find() we pass it two parameters. The first is the string “fast”, which ensures that our effect will run at a decent clip and not leave the user tapping his fingers as he waits for the animation to finish so he can get on with browsing the content. (or worse, he leaves the page!) The second parameter, as you may have noticed, is another anonymous function. Good eyes on you, holmes.

This new anonymous function is what’s referred to as a callback. Callback functions are automatically called when some event occurs, in this case when the fadeOut animation finishes. Our second anonymous function removes the class “active_tab_content” from the currently-active tab’s corresponding content (now that it’s hidden no one can see the change happen!) by calling removeClass(). After this, we obtain the index (that is, the location within a group of items, most frequently an array) of the just-clicked tab. This is the most complex line in our code. We’ve crammed 3 function calls on one line. Two calls to find() and another to index(). This new function is of interest. index() allows you to obtain the index of a particular object from within a collection. The collection is obtained by the first call to find(), grabbing all tabs. We want the index of the new “active_tab” element, which was the just-clicked tab. We pass the second find() call’s results to the index function and get back a number referencing the tab’s position among all tabs in the tabset. We store this number in another local variable.

To obtain a reference to the current content area, we run a find for all “tab_content” elements and then selectively grab only the proper-index item from the collection. This code assumes you store content areas in the same order as their tabs, naturally. Now that we have the current_content variable pointing to the newly-current content, we can add the class “active_tab_content” to it (by calling addClass()) and fade it in using the cleverly-named fadeIn() function.

Reading this code can be a bit tough at first, particularly so if you’ve never dealt with a) function literals, b) chaining, or c) programming at all! To recap on the first two topics:

Function literals, aka anonymous functions, are a shortcut when you don’t need to call a function by name and only need to assign it once to a parameter of a function call or object literal.

Chaining has not yet been discussed by name, but you’ve seen examples of it in the code. Chaining is a technique used in jQuery and other libraries wherein you combine multiple function calls into a single line. It is more compact, and arguably more readable on many occasions. Any code like "func().func().func()" is a function chain. In jQuery, each function returns the current set of objects or the current object, which can be modified/filtered by subsequent calls in the same chain.

Try playing with different aspects of the code and figuring out what works and what doesn’t. And of course ask questions when you can’t figure it out!

 

5 Comments

  1. Posted March 20, 2008 at 11:15 am | Permalink

    Great tutorial. This is just what developers like I need to get up and running, so to speak, with incorporating useful jQuery functionality. Good explanations of rudimentary programming concepts such as functions, callback functions, and event handlers.

    A couple of suggestions I want to make to make the (potential series of) tutorial(s) better:

    Show the markup again (alongside the Javascript) on the top of the second page.

    Make a list of pre-requisites at the beginning of th article, including required knowledge (in this case, intermediate HTML, intermediate CSS, and a basic understanding of the DOM) and libraries (You already took care of this).

    Make side-bars for explanations of base concepts prominently styled as subordinate info. so that developers with this pre-requisite knowledge can easily skip over it.

    Perhaps present the source code with the appropriate line highlighted (or the rest of the source code blurred or obfuscated) by its step-by-step explanation so that the user dosen’t have to scroll up to visually read along with the code.

    Dude, you gotta make that code plugin use a style sheet with greater contrasing text colors! The blues and greens are hard to see on my shitty HP monitor at work!!

    One very nitpicky suggestion: Perhaps link each jQuery function to its documentation on the jQuery site or gotapi.com or, better yet, show this info. in a jQuery-based pop-up div! (This is me running wild with ideas.)

    And, finally, more cowbell.

    One question (not suggestion) regarding this code:

    Why is it necessary to specify the (”.tabset”) in Line 3?

    var tabset = $(this).parents(”.tabset”);

    Can’t there only be one possible parent element for any given element? Wait–don’t answer. I just realized that the $(this) selector might refer to multiple elements, thereby having multiple, distinct parents…right?

  2. Posted March 31, 2008 at 2:52 pm | Permalink

    You guys and your Code!~!!!!!

  3. Posted April 4, 2008 at 4:12 am | Permalink

    Thanks for the read/comments, David. Definitely good suggestions.

    I plan on adding some sort of sidebar (perhaps one with minimizable sections that bobs along with the reader) with relevant code fragments/explanations beside the content, as well as plopping in a TOC sort of box dealy with named anchors for long posts like this one.

    The syntax highlighter plugin actually doesn’t use a stylesheet, it’s all inline styles so it works even within feed readers. (check it out on FeedBurner by following the RSS link at bottom) I will definitely be looking at this soon.

    Linking to relevant jQuery docs is a good idea, which I’ll follow inasmuch as the first time a function shows up I’ll link to it.

    I’ll look into the cowbell suggestion.

    If I had linked to the jQ function parents() line 3 would’ve made more sense. (see here: http://docs.jquery.com/Traversing/parents#expr)

    Passing in a CSS selector string filters the results which would otherwise provide the entire set of ancestors for a given node.

    The function “parent()” (singular) returns only the direct parent. The function “parents()” (plural) returns this ancestor set. It’s the fastest way to obtain such references.

    Since “.tabset” may or may not be the direct parent, depending on varying markup mentioned earlier in the article, calling parents() was the safer bet.

  4. Posted April 4, 2008 at 4:15 am | Permalink

    To clarify further on your last comment: “$(this)” is required to obtain a jQuery object vs a simple DOM node, which the variable “this” is before wrapping it.

  5. Posted April 10, 2008 at 7:11 pm | Permalink

    Yes, do look into that cowbell…
    …and thanks for the clarifications.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*