A while ago a friend of mine realized he had so many employees that listing them all on a single page would make for one of great length. A strange problem to have.
A definitions list provides the perfect data structure for our purposes:
<dl class="drawerify">
<dt>A bird</dt>
<dd>
<img decoding="async" src="images/thumb-bird.jpg">This is a picture of a bird sitting an a tree, and looking quite proud of that.
</dd>
<dt>A cave</dt>
<dd>
<img decoding="async" src="images/thumb-cave.jpg">This is the description of the cave.. Tor lives here, and is a Zombie.
</dd>
<dt>A zombie</dt>
<dd>
<img decoding="async" src="images/thumb-zombie.jpg">Poker was pretty popular for a while, even the zombies got into it.
</dd>
<dt>Bobby</dt>
<dd>
<img decoding="async" src="images/thumb-bobby.jpg">This is a picture of bobby. I actually have a giant version of this framed on a wall over to the right.
</dd>
</dl>
<div id="info_container"></div>
For each entry, we have a title (dt) and the data itself (dd). After the definition list, we have an empty div identified as info_container. This is where we’ll display the appropriate data.
The HTML itself is pretty minimal and hopefully clear, which is just the way we like it.
The javascript is a bit more complicated:
google.load("jquery", "1"); // We want jquery... let's say version 1?
google.setOnLoadCallback( // Once google tells us JQuery has finished loading, we can do some stuff
function() { // This is the annonymous function google will call-back
$('dl.drawerify dt').click( //Every time we click a dt in the drawerified definitions list
function() {
var new_one = $('dl.drawerify dt').index(this);
if (selected == new_one) return; // Don't do anything if we've clicked on the one that's already open
$('dl.drawerify dt:nth('+selected+')').removeClass('current');
selected = new_one;
$(this).addClass('current');
show_info($(this).next('dd').html() ); // Fill the info_container
}
);
$('dl.drawerify dt:first').click(); // Initially, we want the first one shown (so we just fake-click on it)
}
);
var selected = -1; // The index of the currently displayed entry in the definitions list
// Fill the info_container with the appropriate data
function show_info( desc ) {
var container = $('#info_container');
if (container.html().length > 0) {
// Animate the drawer retracting
container.animate({
width: "0px"
}, 300, "swing", function() { container.html(''); show_info( desc ); } );
} else {
container.html("
<div class=\"inner\">" + desc + "</div>
"); // Fill the container
// Animate the drawer extending
container.animate({
width: "250px"
}, 300, "swing" );
}
}
The first line is us telling Google that we want to use JQuery. We could just include JQuery ourselves, but man we are way too lazy for that.
We want only one dt tag to have the current class applied to it at a given time. Lines 8 removes the class from the previously selected dt and line 10 adds it to the new one, using various jquery selector strings.
In line 11, we pass the contents of the dd immediately following the current dt to the show_info function.
Now comes the animation part. Jquery provides a function called animate. We can use that to animate things!
Starting at line 23, the logic goes a little something like this: If there there is already information in the info_container, we want to retract the drawer. If not, we’re good to extend it once we’ve filled it with the new content.
A lot of jquery functions take callbacks as optional parameters, which is useful for us because once a drawer has closed we’d like to clear it of content and then fill it with new content and open it.
That last part sounds an awful lot like what we’d do anyway if the drawer were empty. So maybe all we really have to do is clear it of content, and then have the show_info function call itself with its original parameters? Sounds good to me, and we can totally do it with another anonymous function. That’s what line 28 is about.
And now the Javascript is done. That wasn’t so complicated! I am a big fan of anonymous functions.
Next comes the CSS. This is where we’ll define what the drawer-thing looks like:
dl.drawerify, #info_container {
height: 108px;
background-color: #fff;
padding: 10px;
}
dl.drawerify {
width: 140px;
float: left;
margin: 0px;
}
dl.drawerify dt.current {
color: #ffffff;
}
dl.drawerify dd {
display: none;
}
dl.drawerify dt {
cursor: pointer;
font-weight: bold;
background: #324e63;
color: #b3c4d2;
font-family: arial, verdana;
padding: 3px;
padding-left: 10px;
margin-bottom: 1px;
font-size: 16px;
}
dl.drawerify dt:hover {
background-color: #233544;
}
#info_container {
width: 0px;
float: left;
overflow: hidden;
font-family: verdana;
font-size: 8pt;
padding-left: 0px;
color: #324e63;
}
#info_container .inner {
width: 220px;
}
#info_container img {
margin-right: 10px;
float: left;
}
body {
background-color: #EDEFF0;
}
The most important parts of the css are:
Line 7 and 8 let us display info_container to the left of the definitions list, rather than below it.
Line 19 tells the browser to display the little hand icon when you mouse-over the definition titles instead of an arrow (like when you mouse-over a link).
Line 35 and 42 allow the animation to work nicely. 35 tells the browser to clip anything that exceeds the dimensions of the info_container, and 42 sets the width of the inner div. By setting it, we prevent its width from automatically changing to fit within info_container. That way its content doesn’t get re-arranged during the animation.
The complete code is available, though everything important can be found above.