web-dev-qa-db-fra.com

Comment intégrer un widget Javascript dépendant de jQuery dans un environnement inconnu

Je développe un widget javascript qui dépend de jQuery. Le widget peut ou non être chargé sur une page sur laquelle jQuery a déjà été chargé. Il y a beaucoup de problèmes qui se posent dans ce cas ...

  1. Si la page Web ne contient pas jQuery, je dois charger mon propre jQuery. Cela semble toutefois poser un problème de timing délicat. Par exemple, si mon widget se charge et s'exécute avant que jQuery ne se charge et ne s'exécute, une erreur jQuery is not defined est générée.

  2. Si la page Web contient jQuery, je peux généralement travailler avec. Si la version jQuery est ancienne, cependant, j'aimerais charger la mienne. Si je charge moi-même, je dois le faire de manière à ne pas écraser leur variable $. Si je définis jQuery.noConflict() et que l’un de leurs scripts dépend de $, je viens de casser leur page.

  3. Si la page Web utilise une autre bibliothèque JavaScript (prototype, par exemple), je devais également être sensible à la variable $ du prototype.

En raison de tout ce qui précède, il semble plus facile de ne pas dépendre de jQuery. Mais avant de me lancer dans cette voie, qui impliquera principalement de réécrire mon code de widget, je voulais d'abord demander conseil.

Le squelette de base de mon code, y compris le bogue de synchronisation et parfois les bogues $, est le suivant:

<script type="text/javascript" charset="utf-8">
// <![CDATA
 if (typeof jQuery === 'undefined') {
  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = '{{ URL }}/jquery.js';
  head.appendChild(script);
 }
// ]]>
</script>
<script type="text/javascript" src="{{ URL }}/widget.js"></script>

Mon widget a la structure suivante:

(function($) {
 var mywidget = {
  init: function() {
   ...
  }
 };
 $(document).ready(function() {
   mywidget.init();
 });
})(jQuery);

S'il existe des indicateurs ou des ressources permettant de créer un widget pouvant fonctionner dans tous les environnements mentionnés, ils seraient grandement appréciés.

60
robhudson

Après avoir passé en revue certaines réponses et indications, et trouvé des pirates jQuery utiles, je me suis retrouvé avec quelque chose comme:

(function(window, document, version, callback) {
    var j, d;
    var loaded = false;
    if (!(j = window.jQuery) || version > j.fn.jquery || callback(j, loaded)) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = "/media/jquery.js";
        script.onload = script.onreadystatechange = function() {
            if (!loaded && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
                callback((j = window.jQuery).noConflict(1), loaded = true);
                j(script).remove();
            }
        };
        (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script);
    }
})(window, document, "1.3", function($, jquery_loaded) {
    // Widget code here
});

Cela chargera jQuery s'il n'est pas déjà chargé et l'encapsule dans le rappel afin qu'il ne soit pas en conflit avec un jQuery préexistant sur la page. Il vérifie également qu'une version minimale est disponible ou charge une version connue - dans ce cas, v1.3. Il envoie une valeur booléenne au rappel (mon widget) sur le chargement ou non de jQuery au cas où des déclencheurs seraient nécessaires. Et ce n’est qu’après le chargement de jQuery que celui-ci appelle mon widget, lui transmettant jQuery.

96
robhudson

Voir Comment construire un widget Web (avec jQuery) par Alex Marandon.

(function() {

// Localize jQuery variable
var jQuery;

/******** Load jQuery if not present *********/
if (window.jQuery === undefined || window.jQuery.fn.jquery !== '1.4.2') {
    var script_tag = document.createElement('script');
    script_tag.setAttribute("type","text/javascript");
    script_tag.setAttribute("src",
        "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js");
    if (script_tag.readyState) {
      script_tag.onreadystatechange = function () { // For old versions of IE
          if (this.readyState == 'complete' || this.readyState == 'loaded') {
              scriptLoadHandler();
          }
      };
    } else { // Other browsers
      script_tag.onload = scriptLoadHandler;
    }
    // Try to find the head, otherwise default to the documentElement
    (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
} else {
    // The jQuery version on the window is the one we want to use
    jQuery = window.jQuery;
    main();
}

/******** Called once jQuery has loaded ******/
function scriptLoadHandler() {
    // Restore $ and window.jQuery to their previous values and store the
    // new jQuery in our local jQuery variable
    jQuery = window.jQuery.noConflict(true);
    // Call our main function
    main(); 
}

/******** Our main function ********/
function main() { 
    jQuery(document).ready(function($) { 
        // We can use jQuery 1.4.2 here
    });
}

})(); // We call our anonymous function immediately
5
Pavel Chuchuva

Et si vous aussi vous voulez utiliser des plugins jQuery? Est-il prudent de créer un fichier unique avec les versions détaillées des plugins et de les charger, comme indiqué ci-dessous? (Chargé de S3, dans cet exemple particulier.)

(function(window, document, version, callback) {
  var j, d;
  var loaded = false;
  if (!(j = window.jQuery) || version > j.fn.jquery || callback(j, loaded)) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js";
    script.onload = script.onreadystatechange = function() {
        if (!loaded && (!(d = this.readyState) || d == "loaded" || d == "complete")) {
            window.jQuery.getScript('http://mydomain.s3.amazonaws.com/assets/jquery-plugins.js', function() {
              callback((j = window.jQuery).noConflict(1), loaded = true);
              j(script).remove();
            });
        }
    };
    document.documentElement.childNodes[0].appendChild(script)
  }
})(window, document, "1.5.2", function($, jquery_loaded) {
  // widget code goes here
});
2
cailinanne

Pouvez-vous utiliser document.write () pour ajouter éventuellement le script jQuery à la page? Cela devrait forcer jQuery à se charger de manière synchrone. Essaye ça:

<script type="text/javascript" charset="utf-8">
// <![CDATA
 if (typeof jQuery === 'undefined') {
  document.write('<script src="{{ URL }}/jquery.js"><' + '/script>');
 }
// ]]>
</script>
<script type="text/javascript" src="{{ URL }}/widget.js"></script>

Si vous voulez faire la vérification jQuery à l'intérieur de votre script de widget, alors je pense que le fonctionnement suivant fonctionne entre plusieurs navigateurs:

(function() {
 function your_call($) {
  // your widget code goes here
 }
 if (typeof jQuery !== 'undefined') your_call(jQuery);
 else {
  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = '{{ URL }}/jquery.js';
  var onload = function() {
   if (!script.readyState || script.readyState === "complete") your_call(jQuery);
  }
  if ("onreadystatechange" in script) script.onreadystatechange = onload;
  else script.onload = onload;
  head.appendChild(script);
 }
})()
1
Sean Hogan
1
micahwittman

Je sais que c'est un vieux sujet ... mais j'ai quelque chose de plus rapide que votre bidouille . Essayez dans votre widget

"init": function()

ça va régler le problème

0