Before we begin, please accept my apologies for not posting this tutorial sooner. I know at least two or three people were beginning to wonder if I'd ever finish what I started with this tooltip series. Please also forgive me if the phrase "flip your tip" has a double meaning in some ultra-hip corner of the universe. If it does, I can assure you that I am unaware of it—ignorant and unhip, to be sure, but more important, innocent. Now, on with the show.
In my last three tutorials, I discussed how to put together a very simple tooltip, and I introduced a different feature or concept in each one. In an effort to continue in the spirit of simplicity, I will refrain from repeating the explanations of previous posts and instead simply direct your attention to them before we begin:
- Simple Tooltip for Huge Number of Elements
- Binding Multiple Events to Reduce Redundancy with Event Delegation Tooltips
- Using setTimeout to Delay Showing Event Delegation Tooltips
Now I'd like to round off this series with a little exploration of one way to keep the tooltip within view when it might otherwise get clipped to the right or the bottom of the viewable area.
As with the previous tooltip tutorials, I start by setting a few variables:[js]var $liveTip = $('').hide().appendTo('body'), $win = $(window), showTip; [/js]
The first line creates the tooltip container, hides it, appends it to the body, and stores a reference to it for later use. The next line is more for convenience than anything else (though it might provide nominal performance benefit), since I'll be calling jQuery methods on the
window object a few times. The third line will be used as a reference to a
setTimeout function for delaying the tooltip's visibility.
Position the Tip
Okay. So far, so boring. But here is where things start getting fun. I have a few more pieces I want to store and retrieve, but since they're all related, I figured it would be nice to have them be properties of a single
Since the position needs to be calculated each time the tooltip is displayed, I've made
position a function—in other words, a method of the
tip object. This is the part that does the hard work of figuring out where exactly to put the tooltip and whether or not to "flip" it above or to the left of the mouse position. Because this method is going to be called repeatedly as the mouse moves over a link, I wanted to make it as bare-bones as possible. I defined two objects. The first one contains the
y mouse coordinates. The other contains two sets of numbers: (x) the inner width of the browser window and outer width of the tooltip, and (y) the distance from the top of the document to the bottom edge of the viewable area and the outer height of the tooltip.
Notice that the properties of the
dimensions object have the same names as the properties of the
positions object. I did that so I could easily loop through the properties of one and use the same loop variable for both, as shown in lines 18-26 above. Within that little loop, I'm testing to see if the tooltip, plus the mouse position, plus our user-defined "offset," is greater than the window dimension—on the x axis and then on the y. If it is, I subtract the tooltip's dimension and its offset from the mouse position; if not, I just add the offset to the mouse position.
Finally, I set the
left style properties of the tooltip with the appropriate coordinates. I suppose it would have been cool to name the properties of my two objects "
left" and "
top". That way, I could have written
y make more sense to me as I read them, so I figured it would be worth a few extra bytes to help me remember what I was doing here when I look back at the script later on.
Wire it up
Now that the tooltip positioning is taken care of, it's time to hook it up to some events. If you read Binding Multiple Events to Reduce Redundancy with Event Delegation Tooltips, you may remember that I used the
.bind() method and passed in three events:
mousemove. That seemed to work fine, but now that jQuery 1.4.2 has provided us with the more convenient
.delegate() method, we can use that one instead. Jordan Boesch recently wrote a nice introductory article on Using Delegate and Undelegate in jQuery, so check that out if you're uncertain about what the method does. One thing I'd like to note about it, though, is that it's currently the only jQuery method (of its kind) that doesn't map the
this keyword in the callback function to the current element within the matched set. In the code below, for example, the callback function for other methods would map
this to the "#mytable" element; for
.delegate(), the function maps to its first argument, "a" — when it is within "#mytable" and when it or one of its ancestors is the
event.target element. I hope this little .delegate() excursion didn't confuse matters. Suffice it to say that the method allows us to use
this for the link we want to act on, instead of going through the rigamarole of checking for the event target ourselves as I did in my previous tooltip articles.
Since a lot of what's going on in this code snippet has already been discussed in my previous posts, I'll just mention a couple things.
The tip positioning occurs in line 13, triggered by the
mouseover event, and in line 34, triggered by
mousemove. It only gets triggered in the
mousemove, however, if the mouse has been over the link for the time specified in
tip.delay for the
setTimeout. That's where the link's "tipActive" data attribute is set.
This time around, I also added a little fade effect to show the tooltip. As a safeguard, I hide the tooltip and reset its opacity to 0 (using
.fadeout() with a 0ms duration) before fading it in, because jQuery will only fade in elements if they are hidden to begin with. This extra step is only necessary if the fade-in duration is greater than
So that wraps up this tooltip series. As always, if you see something that I could do better, please leave a comment. I always appreciate learning better ways of plying the craft. And if you have any questions about what's going on in the code, please point out what you'd like me to clarify.