web-dev-qa-db-fra.com

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

Nous utilisons jQuery thickbox pour afficher dynamiquement un 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 que le contenu de l'iframe n'est pas encore chargé. Le code galleria n'est donc pas appliqué correctement sur les éléments DOM. $(document).ready semble utiliser l'état prêt de l'iframe parent pour décider si l'iframe est prêt.

Si nous extrayons la fonction appelée par document ready 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 de risque en production avec un ordinateur lent.

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

Ma question: à quel événement jQuery devons-nous nous lier pour pouvoir exécuter notre code lorsque l'iframe dynamique est prêt et pas seulement un parent?

180
EtienneT

J'ai répondu à une question similaire (voir rappel Javascript lorsque le chargement de IFRAME est terminé? ). Vous pouvez obtenir le contrôle de l'événement de chargement iframe avec le code suivant:

function callIframe(url, callback) {
    $(document.body).append('<IFRAME id="myId" ...>');
    $('iframe#myId').attr('src', url);

    $('iframe#myId').load(function() {
        callback(this);
    });
}

En traitant des iframes, j'ai trouvé assez bon pour utiliser l'événement load au lieu de l'événement document ready.

289
Pier Luigi

En utilisant jQuery 1.3.2, les éléments suivants ont fonctionné pour moi:

$('iframe').ready(function() {
  $('body', $('iframe').contents()).html('Hello World!');
});

RÉVISION:! En fait, le code ci-dessus semble parfois fonctionner dans Firefox, jamais dans Opera.

Au lieu de cela, j'ai implémenté une solution de sondage pour mes besoins. Simplifié, ça ressemble à ça:

$(function() {
  function manipIframe() {
    el = $('body', $('iframe').contents());
    if (el.length != 1) {
      setTimeout(manipIframe, 100);
      return;
    }
    el.html('Hello World!');
  }
  manipIframe();
});

Cela ne nécessite pas de code dans les pages appelées iframe. Tout le code réside et s'exécute à partir du cadre/de la fenêtre parent.

28
David Valentiate

Dans IFrames, je résous généralement ce problème en mettant un petit script à la fin du bloc:

<body>
The content of your IFrame
<script type="text/javascript">
//<![CDATA[
   fireOnReadyEvent();
   parent.IFrameLoaded();
//]]>
</script>
</body>

Ce travail la plupart du temps pour moi. Parfois, la solution la plus simple et la plus naïve est la plus appropriée.

14
Tamas Czinege

Suivant l’idée du Dr Jokepu et de David Murdoch, j’ai mis en place une version plus complète. Il nécessite jQuery sur le parent et iframe et le iframe d'être dans votre contrôle.

code iframe:

var iframe = window.frameElement;

if (iframe){
    iframe.contentDocument = document;//normalization: some browsers don't set the contentDocument, only the contentWindow

    var parent = window.parent;
    $(parent.document).ready(function(){//wait for parent to make sure it has jQuery ready
        var parent$ = parent.jQuery;

        parent$(iframe).trigger("iframeloading");

        $(function(){
            parent$(iframe).trigger("iframeready");
        });

        $(window).load(function(){//kind of unnecessary, but here for completion
            parent$(iframe).trigger("iframeloaded");
        });

        $(window).unload(function(e){//not possible to prevent default
            parent$(iframe).trigger("iframeunloaded");
        });

        $(window).on("beforeunload",function(){
            parent$(iframe).trigger("iframebeforeunload");
        });
    });
}

code de test parent:

$(function(){
    $("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
        console.log(e.type);
    });
});
9
Ricardo Freitas

Trouvé la solution au problème.

Lorsque vous cliquez sur un lien "heavybox" ouvrant une iframe, il insère une iframe avec un identifiant TB_iframeContent.

Au lieu de compter sur l'événement $(document).ready dans le code iframe, il me suffit de faire le lien avec l'événement load de l'iframe dans le document parent:

$('#TB_iframeContent', top.document).load(ApplyGalleria);

Ce code se trouve dans l'iframe mais est lié à un événement d'un contrôle dans le document parent. Cela fonctionne dans FireFox et IE.

4
EtienneT

Essaye ça,

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

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

1
Danny G

Fondamentalement ce que d'autres ont déjà posté mais IMHO un peu plus propre:

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

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


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";
1
Pavel Savara

C'est le problème que j'ai rencontré avec notre client. J'ai créé un petit plugin jquery qui semble fonctionner pour la préparation à l'iframe. Il utilise polling pour vérifier le document iframe readyState combiné à l'URL du document interne, associé au source de l'iframe, afin de s'assurer que l'iframe est bien "prêt".

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

Voici la question:

Saint Graal pour déterminer si l'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 un iframe au dom, puis vérifie l'état prêt du document interne d'iframe - ce qui devrait être interdomaine car il est dirigé vers wikipedia - mais Chrome semble signaler "complete ". La méthode iready du plug-in est appelée lorsque l'iframe est prête. Le rappel tente de vérifier à nouveau l'état de préparation du document interne - en signalant cette fois une demande interdomaine (ce qui est correct) - de toute façon, il semble fonctionner pour ce dont j'ai besoin et espère que cela aidera les autres.

<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>
0
Jon Freynik