Binding Multiple Events to Reduce Redundancy with Event-Delegation Tooltips

read 11 comments

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:

JavaScript:
  1. $('<div id="livetip"></div>').hide().appendTo('body');
  2. var tipTitle = '';
  3. $('#mytable').bind('mouseover', function(event) {
  4.  var $link = $(event.target).closest('a');
  5.  if ($link.length) {
  6.    var link = $link[0];
  7.    tipTitle = link.title;
  8.    link.title = '';
  9.    $('#livetip')
  10.    .css({
  11.      top: event.pageY + 12,
  12.      left: event.pageX + 12
  13.    })
  14.    .html('<div>' + tipTitle + '</div><div>' + link.href + '</div>')
  15.    .show();
  16.  }
  17. }).bind('mouseout', function(event) {
  18.  var $link = $(event.target).closest('a');
  19.  if ($link.length) {
  20.    $link.attr('title', tipTitle);
  21.    $('#livetip').hide();
  22.  }
  23. }).bind('mousemove', function(event) {
  24.  if ($(event.target).closest('a').length) {
  25.    $('#livetip').css({
  26.      top: event.pageY + 12,
  27.      left: event.pageX + 12
  28.    });
  29.  }
  30. });

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):

JavaScript:
  1. var $liveTip = $('<div id="livetip"></div>').hide().appendTo('body');
  2. var tipTitle = '';
  3.  
  4. $('#mytable').bind('mouseover mouseout mousemove', function(event) {
  5.   var $link = $(event.target).closest('a');
  6.   if (!$link.length) { return; }
  7.   var link = $link[0];
  8.  
  9.   if (event.type == 'mouseover' || event.type == 'mousemove') {
  10.     $liveTip.css({
  11.       top: event.pageY + 12,
  12.       left: event.pageX + 12
  13.     });
  14.   }
  15.  
  16.   if (event.type == 'mouseover') {
  17.     tipTitle = link.title;
  18.     link.title = '';
  19.     $liveTip
  20.     .html('<div>' + tipTitle + '</div><div>' + link.href + '</div>')
  21.     .show();
  22.   }
  23.  
  24.   if (event.type == 'mouseout') {
  25.     $liveTip.hide();
  26.     if (tipTitle) {
  27.       link.title = tipTitle;
  28.     }
  29.   }
  30. });

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.

comment feed

11 comments

  1. Yes, the problem is fixed. Very nice.

    It didn't occur to me that the problem was just the mouseout event being fired before tipTitle was set. *head->wall* :)

  2. Nordes

    Hello, I tried in IE8 but I had a bug regarding the href link. It does not want to link to #row999.

    It works only when I am in compatibility mode (IE7)

  3. Jirka

    Hi, could you explain to me the difference between variable link and $link? Why is the dollar sign by variable ($link). Where I would have something to read or learn about it ?

    Thank you in advance for your reply

    • Hi Jirka,
      There is nothing special about the "$" in the variable name. It is just a little reminder to me that I'm dealing with a jQuery object. It has become a convention of sorts among some jQuery develpers, while others don't really like it because of its potential to confuse people who are new to JavaScript, perhaps coming from a different programming language that treats "$" in a special way. I suppose your confusion over it is evidence on the side of those who would rather avoid that notation.

  4. Josh Benham

    I see that you said.

    if (!$link.length) { return; }

    Just to see if the link exists, if the whole script is based off of anchors would it be better to define the selector abit better ?

    $('#myTable a')

    • Actually, I'm intentionally not making the selector that specific. I discuss the reasoning behind the design in my previous post. In a nutshell, if there are thousands of links within the table, binding a click handler to all of them can really bog down the browser.

  5. In this example, could .bind() be replaced by .delegate()?

    • Yes, absolutely. Of course, it couldn't when I wrote the article because .delegate() wasn't in the jQuery API at that point. But good to mention it now. Thanks.

5 Pings

  1. [...] Binding Multiple Events to Reduce Redundancy with Event-Delegation Tooltips Dec 16 2009 [...]

  2. [...] Binding Multiple Events to Reduce Redundancy with Event-Delegation Tooltips - Using setTimeout to Delay Showing Event-Delegation Tooltips [...]

  3. [...] 以上的代码来自:Learning jQuery:Binding Multiple Events to Reduce Redundancy with Event-Delegation Tooltips 分类: 互联网评论 标签: 评论 (0) 发表评论 [...]

  4. [...] my last two tutorials, I explained how to use event delegation for showing and hiding tooltips. In a comment on [...]

  5. [...] Binding Multiple Events to Reduce Redundancy with Event Delegation Tooltips [...]

Sorry, but comments for this entry are now closed.