Loading...

Gracefully Degrading Widgets, Part 1: Tabs Comments

Over the next few posts here on thyncology, I’ll describe how to create the front-end code (XHTML, CSS, and JavaScript) for a set of reusable, effects-laden widgets. The tutorials are aimed at developers/designers who are well-versed enough to understand the CSS/XHTML used here, but programmers of any level (including beginners) can benefit from the JavaScript coverage, no presumptions are made as to skill level. (though I’m not getting into craaaaazy detail here, feel free to post questions!) It’s a good way to learn some actually useful, modern front-end programming, as opposed to the innumerable JavaScript tutorials which are aimed either at skilled programmers, or are many moons out of date, or stink like hell.

These tutorials make use of jQuery, an easy-to-use JavaScript library that makes short work of complex tasks. Be sure to download the latest version from the jQuery site before continuing.

The widgets we’ll build over the next few posts will consist of a tabset, a navbar, and a carousel.

We’ll start with Tabs, since they’re the simplest to understand, both conceptually and in practice. Here’s a working example: http://www.thynctank.com/tabs_example.htm

Code and secret sauce after the jump!

The tabset we’re making can be described thusly: An arbitrary number of content containers and selector tabs which will activate one container as being the presently-visible content for the tabset. So basically, the same tabs you’re probably familiar with from years of [insert OS GUI of choice] use.

What we’ve defined here are two discrete units: controls (the interface whatnots you actually interact with by clicking, in this case) and content. This, you’ll see, is a theme throughout the GDW series. The focus in gracefully-degrading components is, of course, content. This is because when JavaScript is not available, controls become irrelevant. This should be of primary concern to the GDW developer: Focus on presenting the most relevant content first, and figure the best way to avoid ugly, unusable interfaces and inaccessible content.

Therefore, step one is the markup:

Tab 1Tab 2
Some Content 1
Some Content 2

In reality, you should certainly never use # alone for href except for debugging purposes (or if your Web app just can’t/doesn’t need to degrade properly). Here they act simply as placeholders. Note that your specific markup may vary. For example, if you want to style links within the tab_content the same as the rest of the body, you may wish to surround the tab links with a wrapper of some kind. For the purposes of this tutorial, the goal is to have minimalist markup that is highly customizable and easily accessible to JavaScript.

Next, enhancing progressively, add your CSS:

.tabset { border: 1px solid black; background-color: #ccc;}
.tab {
  text-decoration: none;
  color: #f00;
  display: block;
  float: left;
  height: 2em;
  line-height: 2em;
  width: 5em;
  text-align: center;
}
.active_tab { color: #0f0; background-color: #fff;}
.tab_content { display: none; clear: both;}
.active_tab_content { display: block; background-color: #fff;}

Note that we’ve gone with classes vs IDs. While unique IDs make selecting elements simpler in JavaScript, the benefit of going with classes is that, if you design smartly, you can have multiple instances of a given structure on the page, and all of them will work independently.

And finally, the coup de grace, the JavaScript to add interactivity:

    $(function() {
      $(".tab").css("display", "block").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");
        });
      });
    });

This presumes first that jQuery has been loaded. jQuery is probably the simplest well-rounded, well-maintained JavaScript library you’ll find around. I think MooTools is more powerful/faster but has much more confusing syntax and more involved setup for common functionality which can be achieved in only a few lines of jQ code. To load jQuery, download the current jQ source from jquery.com, upload to wherever you want your scripts to live, and reference it in your markup using:

The exact filename will vary, depending on your version and if you rename it. Note that you must include both start and end tags even though there is no content between. The additional JavaScript code should be entered between script tags, or alternately pasted into an external .js file and referenced exactly as jQuery was.

Up next, the JavaScript analysis.

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.

One Trackback

  1. […] Previously, I discussed some of the fundamental ideas behind Gracefully Degrading Widgets, but let’s quickly delineate them again here: […]

Post a Comment

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

*
*