191 votes

jQuery .ready dans une iframe insérée dynamiquement

Nous utilisons jQuery boîte épaisse pour afficher dynamiquement une iframe lorsque quelqu'un clique sur une image. Dans cette iframe, nous utilisons galleria une bibliothèque javascript pour afficher plusieurs images.

Le problème semble être que $(document).ready dans l'iframe semble être déclenché trop tôt et le contenu de l'iframe n'est même pas encore chargé, donc le code de la galleria n'est pas appliqué correctement sur les éléments du DOM. $(document).ready semble utiliser l'état ready du parent de l'iframe pour décider si l'iframe est prêt.

Si nous extrayons la fonction appelée par le document prêt dans une fonction séparée et l'appelons après un délai d'attente de 100 ms. Cela fonctionne, mais nous ne pouvons pas prendre le risque en production avec un ordinateur lent.

$(document).ready(function() { setTimeout(ApplyGalleria, 100); });

Ma question : quel événement jQuery doit-on lier pour pouvoir exécuter notre code lorsque l'iframe dynamique est prêt et pas seulement lorsqu'il est parent ?

1 votes

Et vous confirmez que Galleria fonctionne lorsque vous le chargez directement au lieu de passer par un iframe, correct ?

0 votes

Oui, galleria fonctionne parfaitement lorsque nous l'utilisons directement dans une page normale.

0 votes

3voto

Crashalot Points 3805

Cette fonction de cette réponse est la meilleure façon de gérer cette situation, car $.ready échoue explicitement pour les iframes. Voici la décision pour ne pas soutenir cela.

El load ne se déclenche pas non plus si l'iframe est déjà chargé. C'est très frustrant que cela reste un problème en 2020 !

function onIframeReady($i, successFn, errorFn) {
    try {
        const iCon = $i.first()[0].contentWindow,
        bl = "about:blank",
        compl = "complete";
        const callCallback = () => {
            try {
                const $con = $i.contents();
             if($con.length === 0) { // https://git.io/vV8yU
                throw new Error("iframe inaccessible");
             }

   successFn($con);
     } catch(e) { // accessing contents failed
        errorFn();
     }
  };
  const observeOnload = () => {
    $i.on("load.jqueryMark", () => {
        try {
            const src = $i.attr("src").trim(),
            href = iCon.location.href;
            if(href !== bl || src === bl || src === "") {
                $i.off("load.jqueryMark");
                callCallback();
            }
        } catch(e) {
            errorFn();
        }
    });
  };
  if(iCon.document.readyState === compl) {
    const src = $i.attr("src").trim(),
    href = iCon.location.href;
    if(href === bl && src !== bl && src !== "") {
        observeOnload();
    } else {
        callCallback();
    }
  } else {
    observeOnload();
  }
} catch(e) {
    errorFn();
}

}

2voto

En gros, c'est ce que d'autres ont déjà affiché mais, à mon avis, c'est un peu plus propre :

$('<iframe/>', {
    src: 'https://example.com/',
    load: function() {
        alert("loaded")
    }
}).appendTo('body');

1voto

Danny G Points 2124

Essayez ça,

<iframe id="testframe" src="about:blank" onload="if (testframe.location.href != 'about:blank') testframe_loaded()"></iframe>

Il vous suffit alors de créer la fonction JavaScript testframe_loaded().

1voto

Pavel Savara Points 1745

Je charge le PDF avec jQuery ajax dans le cache du navigateur. Ensuite, je crée un élément incorporé avec les données déjà dans le cache du navigateur. Je pense que cela fonctionnera aussi avec les iframes.

var url = "http://example.com/my.pdf";
// show spinner
$.mobile.showPageLoadingMsg('b', note, false);
$.ajax({
    url: url,
    cache: true,
    mimeType: 'application/pdf',
    success: function () {
        // display cached data
        $(scroller).append('<embed type="application/pdf" src="' + url + '" />');
        // hide spinner
        $.mobile.hidePageLoadingMsg();
    }
});

Vous devez également définir correctement vos en-têtes http.

HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";

1voto

Jon Freynik Points 237

C'est exactement le problème que j'ai rencontré avec notre client. J'ai créé un petit plugin jquery qui semble fonctionner pour la préparation des iframes. Il utilise le polling pour vérifier le readyState du document iframe combiné avec l'url du document interne combiné avec la source de l'iframe pour s'assurer que l'iframe est en fait "prêt".

Le problème avec "onload" est que vous devez avoir accès à la iframe réelle ajoutée au DOM, si vous ne l'avez pas, vous devez essayer d'attraper le chargement de l'iframe, ce qui, s'il est mis en cache, peut ne pas être le cas. Ce dont j'avais besoin était un script qui pouvait être appelé à tout moment, et déterminer si l'iframe était "prêt" ou non.

Voici la question :

Le Saint Graal pour déterminer si un iframe local a été chargé ou non.

et voici le jsfiddle que j'ai finalement trouvé.

https://jsfiddle.net/q0smjkh5/10/

Dans le jsfiddle ci-dessus, j'attends que onload ajoute une iframe au dom, puis je vérifie l'état de préparation du document interne de l'iframe - qui devrait être inter-domaine puisqu'il pointe vers wikipedia - mais Chrome semble signaler "complete". La méthode iready du plug-in est ensuite appelée lorsque l'iframe est effectivement prête. Le callback essaie de vérifier à nouveau l'état de préparation du document interne - cette fois-ci, il signale une requête inter-domaine (ce qui est correct). Quoi qu'il en soit, cela semble fonctionner pour ce dont j'ai besoin et j'espère que cela aidera d'autres personnes.

<script>
  (function($, document, undefined) {
    $.fn["iready"] = function(callback) {
      var ifr = this.filter("iframe"),
          arg = arguments,
          src = this,
          clc = null, // collection
          lng = 50,   // length of time to wait between intervals
          ivl = -1,   // interval id
          chk = function(ifr) {
            try {
              var cnt = ifr.contents(),
                  doc = cnt[0],
                  src = ifr.attr("src"),
                  url = doc.URL;
              switch (doc.readyState) {
                case "complete":
                  if (!src || src === "about:blank") {
                    // we don't care about empty iframes
                    ifr.data("ready", "true");
                  } else if (!url || url === "about:blank") {
                    // empty document still needs loaded
                    ifr.data("ready", undefined);
                  } else {
                    // not an empty iframe and not an empty src
                    // should be loaded
                    ifr.data("ready", true);
                  }

                  break;
                case "interactive":
                  ifr.data("ready", "true");
                  break;
                case "loading":
                default:
                  // still loading
                  break;   
              }
            } catch (ignore) {
              // as far as we're concerned the iframe is ready
              // since we won't be able to access it cross domain
              ifr.data("ready", "true");
            }

            return ifr.data("ready") === "true";
          };

      if (ifr.length) {
        ifr.each(function() {
          if (!$(this).data("ready")) {
            // add to collection
            clc = (clc) ? clc.add($(this)) : $(this);
          }
        });
        if (clc) {
          ivl = setInterval(function() {
            var rd = true;
            clc.each(function() {
              if (!$(this).data("ready")) {
                if (!chk($(this))) {
                  rd = false;
                }
              }
            });

            if (rd) {
              clearInterval(ivl);
              clc = null;
              callback.apply(src, arg);
            }
          }, lng);
        } else {
          clc = null;
          callback.apply(src, arg);
        }
      } else {
        clc = null;
        callback.apply(this, arguments);
      }
      return this;
    };
  }(jQuery, document));
</script>

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