Cas d'utilisation banal et commun
Je voudrais un info-bulle qui montre un GIF de chargement immédiatement au survol de la souris, qui est remplacé par du HTML résultant d'un callback de succès AJAX. Il y a presque autant de réponses erronées pour les info-bulles AJAX que de personnes posant cette question exacte en ligne. En particulier, j'ai basé mes efforts sur ceci, ceci, et surtout plusieurs réponses ici.
Le problème que je rencontre systématiquement pour toutes ces solutions, même celles prétendant spécifiquement aborder le problème, est que les info-bulles apparaissent parfois tardivement et échouent à disparaître. Ce n'est pas non plus un problème de bibliothèque, car j'ai eu le même problème par le passé avec les info-bulles jQuery-UI.
Bibliothèques
- jQuery v2.2.3
- Bootstrap v3.3.6
Précédemment essayé avec jQuery 1 et eu le même problème :
- jQuery v1.12.3
Ma page repose également sur la bibliothèque jQuery Datatables v1.10.11, mais cela est orthogonal au problème de l'info-bulle, sauf le fait que mon cas d'utilisation particulier génère davantage et plus rapidement des requêtes d'info-bulle AJAX avec des latences variables, ce qui augmente la probabilité d'observer le problème de "sticking".
Tentative Actuelle
Ma solution actuelle est basée grossièrement sur la réponse de 'towr' ici qui est très proche de fonctionner. J'ai essayé chaque solution mentionnée sur la page et elles sont soit cassées pour une raison ou une autre, soit ont le problème de "sticking" de l'info-bulle.
Après qu'une requête AJAX charge des données pour peupler le tableau, j'itére sur les cellules par classe $( ".tooltip-cell" ).on( "mouseover", set_tooltip )
, où set_tooltip
est une fonction qui fait ce qui suit
var $e = $(this);
if( $e.attr( "title" ) === undefined ) { // demander les données une seule fois par cellule
// affiche le GIF de chargement
$e.attr( "title", "" );
$e.tooltip(
{
html: true,
trigger: "hover"
}
).tooltip( "show" );
// effectue un GET AJAX
$.ajax(
{
url: "ajax/tooltip_data.php",
data:
{
lookup_id: $e.attr("lookup_id")
},
success:
function(response) {
// prépare la version finale de l'info-bulle
$e.attr( "title", response ).tooltip( "fixTitle" );
// si la souris est toujours en survol, affiche la version finale
if( $e.is( ":hover" ) ) {
$e.tooltip( "show" );
}
},
dataType: "html"
}
);
}
Le problème ici est la détection du survol dans le callback de succès AJAX. Apparemment, la méthode .is(":hover")
a été obsolète depuis un certain temps, j'ai donc essayé diverses méthodes alternatives, telles que $( ":focus :active" ).filter( $e ).length > 0
, mais le résultat est toujours soit que toutes les info-bulles sont affichées et restent collées ou que aucune info-bulle n'est affichée. Il est facile de reproduire cela en introduisant un setTimeout( function() {}, 2000 );
pour envelopper le code à l'intérieur du callback. Ensuite, le problème survient toujours. En fait, je peux créer 10 info-bulles adjacentes, passer la souris dessus, et après 2 secondes, elles apparaîtront toutes avec le HTML AJAX et resteront collées, malgré le fait que la souris ne survole aucune d'entre elles. Avec des déclarations d'impression et du débogage, je peux vérifier que la valeur à l'intérieur de if( )
est correcte, à son tour, pour chacun des 10 éléments, donc d'une manière ou d'une autre, la vérification du survol est soit incorrecte soit retourne des informations obsolètes.
L'Énigme
Comment puis-je déterminer avec précision si une souris est actuellement positionnée au-dessus d'un élément au moment où le callback correspondant à cet élément a préparé le contenu de l'info-bulle ?
Autres Réflexions
J'ai rencontré deux problèmes compliquants en travaillant sur cela, mais je ne pense pas qu'ils soient liés.
1) J'obtiens une erreur indiquant que .tooltip()
est indéfini sauf si mon inclusion JS de jQuery est dans et mon inclusion JS de Bootstrap est en bas de mon corps HTML. Si je déplace Bootstrap vers le haut ou jQuery vers le bas (en gardant toujours jQuery au-dessus de Bootstrap), j'obtiens l'erreur.
2) La solution ici semble bonne, mais peu importe à quel point j'essaie de dupliquer le code, j'obtiens des erreurs $el.data(...) indéfini
pour la ligne $el.data('tooltip').tip().is(':visible')
dans mon code. Cela malgré le fait que la page de démonstration liée fonctionne bien pour moi.
Esprit d'Espoir
J'espère que cette question pourra devenir une ressource canonique pour les personnes qui veulent le faire, car j'ai réellement passé 16 (oui, vraiment 16) heures hier à travailler sur cela, implémentant chaque variation que je pouvais imaginer de chaque solution que je pouvais trouver en ligne.