Binding Multiple Events to Reduce Redundancy with Event-Delegation Tooltips

Last time I showed how to use event delegation to create a simple tooltip for a huge number of elements without running into the problem of binding an event handler to all of those elements. In this tutorial, I'm going to refine that tooltip script a bit, avoiding some code repetition and fixing a bug that someone pointed out in a comment.

For review, here is the final version of the script from the last post:

[js]$('
').hide().appendTo('body'); var tipTitle = ''; $('#mytable').bind('mouseover', function(event) { var $link = $(event.target).closest('a'); if ($link.length) { var link = $link[0]; tipTitle = link.title; link.title = ''; $('#livetip') .css({ top: event.pageY + 12, left: event.pageX + 12 }) .html('
' + tipTitle + '
' + link.href + '
') .show(); } }).bind('mouseout', function(event) { var $link = $(event.target).closest('a'); if ($link.length) { $link.attr('title', tipTitle); $('#livetip').hide(); } }).bind('mousemove', function(event) { if ($(event.target).closest('a').length) { $('#livetip').css({ top: event.pageY + 12, left: event.pageX + 12 }); } }); [/js]

Bind Multiple Events at One Time

As I mentioned last time, this repeats code where it really shouldn't. For example, lines 5, 19, and 24 all check that moused-over element (or one of its ancestors) is a link. One way to reduce the repetition is to bind all three events at the same time (line 4):

[js]var $liveTip = $('
').hide().appendTo('body'); var tipTitle = ''; $('#mytable').bind('mouseover mouseout mousemove', function(event) { var $link = $(event.target).closest('a'); if (!$link.length) { return; } var link = $link[0]; if (event.type == 'mouseover' || event.type == 'mousemove') { $liveTip.css({ top: event.pageY + 12, left: event.pageX + 12 }); } if (event.type == 'mouseover') { tipTitle = link.title; link.title = ''; $liveTip .html('
' + tipTitle + '
' + link.href + '
') .show(); } if (event.type == 'mouseout') { $liveTip.hide(); if (tipTitle) { link.title = tipTitle; } } }); [/js]

This modification relies on the type property of the event object, checking it to see which of the three event types were triggered and acting accordingly. That way, I can get away with writing the $link and link declarations and the link check one time instead of two or three.

Other changes

Here are a few other changes worth mentioning:

  • Line 1 declares the $livetip variable while at the same time creating the tooltip div and appending it to the body. Now the script can refer to that variable rather than having to call $('#livetip') each time it is shown, moved, hidden, and so on.
  • Rather than wrap nearly everything in an if block, I made the function return if there is no link selected. Some people have a philosophical problem with multiple return points in a function, and it can make it hard to decipher and debug code sometimes, but here I think it's okay.
  • On "mouseout" (lines 23-28), the script is resetting link.title to tipTitle only if tipTitle has a value. This, I'm hoping, fixes the problem reported by Moritz Peters of the tooltip not displaying correctly if you first mouse over a link before the document is ready.

Try the demo or download the zip (or both).

Adding a Delay

Tomorrow—for realz this time—I'll show how to add a short delay on mouseover before showing the tooltip.

P.S. You see the "$" in "$liveTip" and "$link"? There is nothing special about that—no special meaning given to "$" in JavaScript. It's just a convention that some jQuery folks have adopted to remind ourselves that the variable is holding a jQuery object.