“Drawing” from Our Learning: How to Build a Lesson Drawer in jQuery, HTML, and CSS

In the spirit of showing how things are done at SitePoint‘s sister company Learnable—a new site where anyone can create an online course about anything at all, and then sell access to that course—I’m going to run you through a small component of Learnable’s student view: the lesson drawer. I’ve simplified the layout and styling a little so that you can easily understand what’s going on, but the JavaScript is exactly the same.

In this post, I’ll cover the HTML (including some of the new HTML5 elements), CSS, and JavaScript (with a little help from jQuery) needed to build a drawer. The drawer is a large container that holds a list of the lessons in a course, which can be opened by a button. The lessons are represented by a clickable thumbnail. To view an example of what we’ll be building, check it out here.

If you’re unfamiliar with HTML5, you’ll see I’ve used a few tags that will be new to you. The section is used to describe sections of your site, and the nav tag is used to contain navigation elements. The HTML5 tags help to reduce “div-itis”; for example, where you may have <div id="section"></div> and <div id="nav"></div>.

Apart from the new tags, it’s basically standard HTML. The unordered list would contain the linked thumbnails.

The JavaScript is where it becomes interesting. I’m going to proceed through this step by step, building it the way I would write it while explaining along the way.

var LEARNABLE = {};

This creates an object that will contain the functions for your page. We’ve named it LEARNABLE, as it “namespaces” your functions. This form of writing JavaScript is called the module pattern. It’s a great way to structure your JavaScript for a number of reasons:

It reduces the number of global variables, as they are evil.

You can easily have lots of functions working together without affecting other scripts you may have running.

It’s relatively straightforward to maintain and work on.

Other developers involved in the project will be able to see what’s going on easily.

Now we create our functions. I’ve named it lessonDrawer, and as it’s a part of the LEARNABLE codebase, it becomes LEARNABLE.lessonDrawer.

LEARNABLE.lessonDrawer is a function, so it is written as = (function (){, then the code, and is closed with })();.

Here are the outside bits of the function:

LEARNABLE.lessonDrawer = (function () {
...
})();

Count the parentheses, and you’ll see that there’s an extra set of () at the end. This means it is self-executing; when your browser has downloaded this script, it will run it, allowing it to be accessed by any other JavaScript function in your script. Well, sort of.

var init = function () {
...
};
// Public API
return {
init: init
};

This little bit of trickery means that within the lessonDrawer function there are further functions, in this case called init. The tricky part is that at the end of the lessonDrawer function, it will return to your script a way to gain access to the init function. You may ask, what’s the big deal? Let’s say we changed it a little:

Because we’ve specifically said that we only want the init function available, no function outside of lessonDrawer can touch the function named otherThing. It’s safe inside its parent function. You can’t accidentally run it and, more importantly, when you give it to other developers, they can only use it the way you’ve specified.

Now to the insides of the init function:

$("#lessonsTab").hide();

First, we hide the #lessonsTab, which is our nav container. We do that in this function so that JavaScript-less users can still see the lesson drawer:

The jQuery Toggle method displays or hides elements that it’s told about. The first half between the $('#lessonsTabTarget').toggle(function() { and }); means that when it’s clicked, it will show the lesson drawer.

Its counterpart, between the function() { and closing });, will hide the lesson drawer if it’s open:

$(this) means whatever its parent is talking about (in this case, it’s the $('#lessonsTabTarget'). I’ve been assuming you know what $('#lessonsTabTarget') means, but for those who’ve been playing at home, it asks jQuery (represented by the $) for any elements that have an ID of lessonsTabTarget.

We add a class called “shown" to $(this), and then look through its children for an anchor. Looking back at the HTML structure we’re using, this is <span id="lessonsTabTarget"><a href="#">Show Lessons</a></span>, and on the child anchor we change the text (or its HTML) to the next text (“Hide Lesson Navigator”) as an instruction to the user that if they click the button, they will hide it).

$("nav#lessonsTab").show("slow");

Now we tell it to find the nav#lessonsTab and make it appear slowly. The jQuery Show method accepts a few arguments, the first being how long you want it to take to show an element. Instead of "slow", you can use "fast" or the number of milliseconds, where 1,000 means one second (as this is typed as an integer, no quotation marks are necessary, unlike "fast" and "slow").

return false;

We return false to stop the browser from following the link. (As it is, it doesn’t go anywhere, but the <a href="#" would make the page jump to the top without returning false).

To summarize:

Set up an unobstructive JavaScript function any time the page contains a #lessonsTabTarget element.

Add a class (telling the CSS that it’s “shown“).

Change the button text.

Open the lesson tab drawer.

Stop the page from following the link.

The second half is exactly the opposite! It hides the lesson drawer, removes the “shown” class name, and changes the text back. It also stops the browser from following the link. On a side note, the return false always needs to be the last thing in your function.

Lastly, we need to run the code. Our final task is to tell the document that when it’s downloaded all its resources (JavaScript and CSS, as well as images), or when it’s ready, to run the init() function. To access the init() function, you have to use its full name: LEARNABLE.lessonDrawer.init();.

$(document).ready(function() {
LEARNABLE.lessonDrawer.init();
});

The CSS: the Polish

Adding the CSS is the last bit needed to make this a really great component.