Footnotes

A lot of people have asked me about my implementation of footnotes.1 I have a love-hate relationship with footnotes. I like to use footnotes, because they allow me to remove stuff that isn't directly relevant to the text I'm currently writing, while still keeping the information around in case anyone is interested. On the other hand, footnotes are a usability nightmare, yanking you out of your place in the text to a completely different place, and then back up. They are an interruption.

So I figured that it would be a good idea to show the content of a footnote as soon as you indicate that you are interested in the footnote. Namely, when you move the cursor over the footnote symbol.2 This allows footnotes to work on devices that don't support mouse hovering3, because you can still jump to a footnote via its link, but it removes the necessity of having to click on a footnote link for most people.

In case you can't see this

Some people have asked whether they can use my code for their own site.4 Sure, although it is unlikely that you will be able to use it without any changes. Here you go:

// this script requires jQuery
$(document).ready(function() {
    Footnotes.setup();
});

var Footnotes = {
    footnotetimeout: false,
    setup: function() {
        var footnotelinks = $("a[rel='footnote']")
        
        footnotelinks.unbind('mouseover',Footnotes.footnoteover);
        footnotelinks.unbind('mouseout',Footnotes.footnoteoout);
        
        footnotelinks.bind('mouseover',Footnotes.footnoteover);
        footnotelinks.bind('mouseout',Footnotes.footnoteoout);
    },
    footnoteover: function() {
        clearTimeout(Footnotes.footnotetimeout);
        $('#footnotediv').stop();
        $('#footnotediv').remove();
        
        var id = $(this).attr('href').substr(1);
        var position = $(this).offset();
    
        var div = $(document.createElement('div'));
        div.attr('id','footnotediv');
        div.bind('mouseover',Footnotes.divover);
        div.bind('mouseout',Footnotes.footnoteoout);

        var el = document.getElementById(id);
        div.html($(el).html());
        
        div.css({
            position:'absolute',
            width:'400px',
            opacity:0.9
        });
        $(document.body).append(div);

        var left = position.left;
        if(left + 420  > $(window).width() + $(window).scrollLeft())
            left = $(window).width() - 420 + $(window).scrollLeft();
        var top = position.top+20;
        if(top + div.height() > $(window).height() + $(window).scrollTop())
            top = position.top - div.height() - 15;
        div.css({
            left:left,
            top:top
        });
    },
    footnoteoout: function() {
        Footnotes.footnotetimeout = setTimeout(function() {
            $('#footnotediv').animate({
                opacity: 0
            }, 600, function() {
                $('#footnotediv').remove();
            });
        },100);
    },
    divover: function() {
        clearTimeout(Footnotes.footnotetimeout);
        $('#footnotediv').stop();
        $('#footnotediv').css({
                opacity: 0.9
        });
    }
}

Basically, I first collect all footnote links and bind mouseover and mouseout event handlers to them. The mouseover handler searches for the proper footnote based on the footnote link's id, and then shows a div with the footnote's HTML. The mouseout handler removes that div again. Moving the mouse from the footnote link to the footnote popup stops the mouseout handler's animation so that the popup sticks around in case you want to click on a link inside it; leaving the popup triggers the mouseout handler again. Depending on how your footnotes are rendered, you will need to change the setup function to find the footnote links on your site, and you will need to change the footnoteover function to find the proper footnote's content for each footnote link. You can add a #footnotediv rule to your CSS in order to style it.

I have tested this with Safari and Firefox.

A number of people have asked me to tell John Gruber to use the same footnote system for Daring Fireball. I'm not sure why people think that I would be able to convince him to change his site; I highly doubt he would listen to me. However, due to surprisingly popular demand, I have created a very simple, barebones version of my script that works with his site. I've made it into a bookmarklet. Simply drag this to your bookmarks bar:

Activate Daring Fireball Footnotes

Once Daring Fireball has fully loaded, you can click on the bookmarklet to add the mouse event handlers to the bookmark links. After doing so, you should be able to hover over a footnote link to see its corresponding text.

Again, I have tested this in Safari and Firefox. In case you're curious, here's the code:

var sups = document.getElementsByTagName('sup');
var footnotehtml = [];
for(var i=0; i<sups.length; i++) {
    var sup = sups[i];
    if(sup['id'] && sup['id'].substr(0,3) == 'fnr') {
        var footnr = sup['id'].substr(3);
        var footnote = document.getElementById('fn'+footnr);
        if(!footnote) continue;
        footnotehtml[i] = footnote.innerHTML;
        sup.setAttribute('footnoteindex',i);
        sup.onmouseover = function(event) {
            var footnotepopup = document.getElementById('footnotepopup');
            if(footnotepopup) footnotepopup.parentNode.removeChild(footnotepopup);
            var index = parseInt(this.getAttribute('footnoteindex'));
            var popup = document.createElement('div');
            popup.innerHTML = footnotehtml[index];
            popup.id = 'footnotepopup';
            popup.style.position = 'absolute';
            popup.style.left = (event.pageX - 125) + 'px';
            popup.style.top = (event.pageY + 25) + 'px';
            popup.style.width = '250px';
            popup.style.textAlign = 'left';
            popup.style.backgroundColor = '#4a525a';
            popup.style.border = '1px solid #636363';
            popup.style.padding = '10px';
            
            document.body.appendChild(popup);
        };
        sup.onmouseout = function(event) {
            var footnotepopup = document.getElementById('footnotepopup');
            if(footnotepopup) footnotepopup.parentNode.removeChild(footnotepopup);
        };
    }
}

Update

There are already examples of the script running on other people's sites here and here.

There's a Greasemonkey script for Wikipedia based on the Daring Fireball script over at no.good.at.coding, and Dr. Drang has created an extension for Safari.

Here's a version that works with kramdown.

The Feynman Lectures on Physics use footnote code based on the code published here.


  1. These footnotes here. ↩︎

  2. Like this. ↩︎

  3. Such as screen readers, or touchscreen devices like iPads. ↩︎

  4. And some people have asked me about a license for this piece of code. I think it's far too short to get its own license, so I'm relinquishing any copyright claims. Consider the code to be public domain. No attribution is necessary. ↩︎

If you require a short url to link to this article, please use http://ignco.de/275

designed for use cover

But wait, there's more!

Want to read more like this? Buy my book's second edition! Designed for Use: Create Usable Interfaces for Applications and the Web is now available DRM-free directly from The Pragmatic Programmers. Or you can get it on Amazon, where it's also available in Chinese and Japanese.