We have recently started using jQuery Tooltip (http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/) on some of the pages in a new website project. On one page, we wanted the tooltip to not only contain some brief info, but also a link to a FAQ page. Achieving this was easy enough, by implementing a bodyHandler to override the default behaviour of the tooltips (which is to display the element's title attribute).
Problems arose though when it was time to actually click the link. I assumed it was just a matter of passing the tooltip function the right arguments to delay the removal of the tooltip so you have a chance to move the mouse to it, and then of course keeping the tooltip visible until you move your mouse away from it. It turned out though that none of these features existed, so I started looking at a way of hacking it to suit our needs. Not being familiar with jQuery plugins in general, and not fully understanding how jQuery Tooltip does its magic, I went ahead the quick and dirty way so chances are that a much elegant solution than mine is possible.
First thing to change was the registration of the mouseout event handler on tooltip-enabled elements inside the $.fn.extend function:
.mouseout(save);
became
.mouseout(
function(){
delayMouseout($(this));
}
);
Since it's sometimes hard to predict what "this" will reference in javascript, I passed the JQuery $(this) instead which I think is more reliable in that sense. I then created the function delayMouseout which basically is responsible for the delayed removal:
function delayMouseout(jqobj){
removeDelay = setTimeout(function(){handleMouseout(jqobj)}, 1000);
}
and then the related function handleMouseout, which does the actual work:
function handleMouseout(jqobj){
hide.call($(jqobj)[0]);
}
Since the hide function relies on the this keyword representing the right object, and we have now lost the scope it was originally intended to be called in, we manipulate the scope by invoking it with the .call(new_scope_owner) construct. Since the hide function expects a normal DOM element and not a JQuery object, I use array notation to pass the actual element as the function argument: $(jqobj)[0].
So far so good: the removal of the tooltip is now delayed a second. Next challenge is to prevent the tooltip from disappearing if 1. we have moved the mouse to the tooltip itself, or 2. we have moved it back to the tooltip-enabled element. The second case is easiest so let's start with that. JQuery Tooltip already keeps track of which is the currently activated tooltip-enabled element, which is stored in the variable "current". So we just add a check to handleMouseout():
function handleMouseout(jqobj){
if($(jqobj)[0] == current){ //The element is hovered again so no need to hide
return;
}
else {
hide.call($(jqobj)[0]);
}
}
This too is no good though. "current" is normally set to null inside the hide() function, which we now prevent from being called. So "current" will remain referencing the last hovered element even after the mouse has left it. Let's set it to null in delayMouseout() instead:
function delayMouseout(jqobj){
current = null;
removeDelay = setTimeout(function(){handleMouseout(jqobj)}, 1000);
}
Now, current is set to null onmouseout, but may be set to reference a mouseovered object again in the 1000 millis before handleMouseout() is called, which in turn checks that the currently hovered element is not the one whose tooltip is visible, before hiding the tooltip.
Another issue we need to deal with is that during the removal delay, another tooltip-enabled element may be hovered. The desired behaviour in this case is that the current tooltip is hidden immediately, without waiting for the timeout to finish, and the new tooltip displayd instead. The natural place to put this logic is in the save() function, which is what's called onmouseover. But there is currently no way to tell if a tooltip is visible or not, since we have already set "current" to null when the mouse left the previous element. So we need to save the value of "current" before setting it to null. Why not take advantage of the "helper" object, which represents the actual tooltip, and simply add a property to it:
function delayMouseout(jqobj){
helper.current = current;
current = null;
removeDelay = setTimeout(function(){handleMouseout(jqobj)}, 1000);
}
and in save() we can add this snippet of code right after the first "if ( $.tooltip.blocked ... return;" part:
if(helper.current){
clearTimeout(removeDelay);
hide.call(helper.current);
}
Finally, we need to set helper.current to null when the tooltip is actually hidden:
function handleMouseout(jqobj){
if($(jqobj)[0] == current){
return;
}
else {
helper.current = null;
hide.call($(jqobj)[0]);
}
}
Hooray, we now have a tooltip that doesn't disappear until after a certain delay, isn't removed if the same tooltip-enabled element is found to be hovered again when the delay has passed, and that is removed immediately if another tooltip-enabled element is hovered before the delay has passed. Left is to make the tooltip stay if it itself is hovered when the delay has passed.