4 votes

Bootstrap Info-bulles avec AJAX (Une fois de plus)

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.

0voto

rg6 Points 134

Idée centrale

Pour moi, la clé était dans la solution fournie ici, que j'avais liée à la question initiale.

Le problème avec cette solution tel qu'il apparaissait dans le code source était l'utilisation de $el.data('tooltip').tip().is(':visible') qui causait invariablement l'erreur $el.data(...) undefined, mais l'idée de vérifier l'état d'affichage de l'info-bulle plutôt que l'état de survol de la souris semblait prometteuse.

Un examen approfondi du fonctionnement de l'info-bulle de Bootstrap v3.3.6 a révélé qu'il définissait 'aria-describedby' sur l'élément survolé sur l'ID de l'info-bulle. Avec cette information, nous pouvons faire $( "#" + $el.attr( "aria-describedby" ) ).is( ":visible" )

Je ne sais pas dans quelle mesure cela sera durable, mais cela marche, enfin, sans interruption.

Code

$( ".ajax-tooltip-required" ).tooltip(
        {
            html: true,
            trigger: "manual"
        }
        ).on(
        {
            mouseenter:
                function() {
                    var $el = $(this);
                    if( $el.data( "fetched" ) === undefined ) {
                        $el.data( "fetched", true );
                        $el.attr( "data-original-title", "" ).tooltip( "show" );
                        $.ajax(
                            {
                                url: "ajax/tooltip_data.php",
                                data:                                   {
                                        lookup_id: $el.attr("lookup_id")
                                    },
                                success:
                                    function( response ) {
                                        $el.attr( "data-original-title", response );
                                        if( $( "#" + $el.attr( "aria-describedby" ) ).is( ":visible" ) ) {
                                            $el.tooltip( "show" );
                                        }
                                    },
                                dataType: "html"
                            }
                            );
                    } else {
                        $(this).tooltip( "show" );
                    }
                },
            mouseleave:
                function() {
                    $(this).tooltip( "hide" );
                }
        }
        );

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X