Mundane, Common Use Case
I would like a tool tip that shows a loading GIF immediately on mouseover that is replaced by HTML resulting from an AJAX success callback. There are almost as many broken answers for AJAX tool tips as people asking this exact question online. In particular, I have been modeling my efforts on this, this, and especially several answers here.
The problem that I consistently encounter for all these solutions, even those specifically claiming to address the issue, is that tool tips occasionally appear late and fail to disappear. It is not a library issue either, because I have had the same problem in the past with jQuery-UI tool tips.
Libraries
- jQuery v2.2.3
- Bootstrap v3.3.6
Previously tried with jQuery 1 and had the same problem:
- jQuery v1.12.3
My page also relies on the jQuery Datatables v1.10.11 library, but this is orthogonal to the tool tip issue other than the fact that my particular use case generates more and faster AJAX tool tip requests with variable latencies, which increases the probability of observing the "sticking" problem.
Current Attempt
My current solution is based roughly on the answer by 'towr' here that is very close to working. I have tried every solution above that on the page and they either are broken for one reason or another or have the "sticking" tool tip issue.
So after an AJAX request loads data to populate the table, I iterate over the cells by class $( ".tooltip-cell" ).on( "mouseover", set_tooltip )
, where set_tooltip
is a function that does the following
var $e = $(this);
if( $e.attr( "title" ) === undefined ) { // request data once per cell
// show loading GIF
$e.attr( "title", "<img src='images/wait.gif'/>" );
$e.tooltip(
{
html: true,
trigger: "hover"
}
).tooltip( "show" );
// perform AJAX GET
$.ajax(
{
url: "ajax/tooltip_data.php",
data:
{
lookup_id: $e.attr("lookup_id")
},
success:
function(response) {
// prepare final version of tooltip
$e.attr( "title", response ).tooltip( "fixTitle" );
// if mouse still hovers, display final version
if( $e.is( ":hover" ) ) {
$e.tooltip( "show" );
}
},
dataType: "html"
}
);
}
The problem here is the hover detection in the AJAX success callback. Now apparently the .is(":hover")
has been deprecated for quite awhile, so I have tried various alternative methods, such as $( ":focus :active" ).filter( $e ).length > 0
, but the result is always either that all tool tips are shown and stick or no tool tips are shown. It is easy to reproduce this by introducing a setTimeout( function() {}, 2000 );
to wrap the code inside the callback. Then the problem always occurs. In fact, I can create 10 adjacent tool tips, run the mouse across them, and after 2 seconds, they will all appear with the AJAX HTML and stick, despite the fact that the mouse is not hovering on any of them. With print statements and debugging, I can verify that the value inside if( <hover-check> )
is correctly, in turn, each of the 10 elements, so somehow the hover check is either itself incorrect or returning stale information.
The Crux
How can I accurately determine if a mouse is currently positioned on top of an element at the time that the callback corresponding to that element has prepared the tool tip content?
Other Thoughts
I have had two complicating issues while working on this, but I do not think they are related.
1) I get an error that .tooltip()
is undefined unless my jQuery JS include is in <head>
and my Bootstrap JS include is at the bottom of my HTML body. If I either move Bootstrap up or jQuery down (always keeping jQuery above Bootstrap), I get the error.
2) The solution here seems nice, but no matter how closely I try to duplicate the code, I get $el.data(...) undefined
errors for the line $el.data('tooltip').tip().is(':visible')
in my code. This is despite the fact that the linked demo page itself works fine for me.
General Hope
I hope that this question can become a canonical resource for people who want to do this, because I actually spent 16 (yes, really 16) hours yesterday working on this, implementing every variation I could imagine of every solution I could find online.