jQuery .ready在动态插入的iframe中


184

当有人点击图片时,我们使用jQuery 厚盒来动态显示iframe。在此iframe中,我们使用Galleria一个JavaScript库来显示多张图片。

问题似乎是,$(document).ready在iframe中似乎触发得太早了,而且iframe内容还没有加载,因此在DOM元素上未正确应用Galleria代码。 $(document).ready似乎使用iframe父就绪状态来确定iframe是否已就绪。

如果我们将准备好的文档调用的函数提取到单独的函数中,并在100毫秒超时后调用它。它可以工作,但是我们不能利用速度较慢的计算机来进行生产。

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

我的问题:当动态iframe准备好并且不仅仅是父项时,我们应该绑定到哪个jQuery事件以执行我们的代码?


1
并且您确认了当直接加载而不是通过iframe加载时,图库是否可以正常工作,对吗?
詹森·基利

是的,当我们在普通页面中直接使用Galleria时,它的效果非常好。
EtienneT

Answers:


291

我回答了类似的问题(完成IFRAME加载时,请参阅Javascript回调吗?)。您可以使用以下代码来控制iframe加载事件:

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

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

在处理iframe时,我发现足以使用加载事件而不是文档就绪事件了。


17
您是否应该在调用attr('src')之前设置加载事件?
Shay Erlichmen,2010年

15
不,没关系。加载事件只有在下一个事件循环最少时才会触发。
Barum Rho

29
load事件不适用于用于下载的iframe。就像<iframe src =“ my.pdf” />
Mike

5
加载的问题是,当所有图像和子帧都加载后会触发。像ready事件这样的jQuery会更有用。
汤姆(Tom)


30

使用jQuery 1.3.2,以下对我有用:

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

修订版:!实际上,上面的代码有时看起来像在Firefox中工作,而看起来却从未像在Opera中工作。

相反,我出于自己的目的实施了轮询解决方案。简化下来看起来像这样:

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

这在被调用的iframe页面中不需要代码。所有代码都驻留在父框架/窗口中并从其执行。


setTimeout()中所指的movePreview功能是什么?
cam8001

@ cam8001:这是一个错字-现在已修复。
三月Örlygsson

我最终也使用了轮询解决方案。其他解决方案似乎只能部分成功。但是,对我而言,在获得成功之前,我需要检查是否存在javascript函数,而不仅仅是iframe的内容。例如。(typeof iframe.contentWindow.myfunc =='function')
朱利安(Julian

2
此解决方案是递归的,占用每个调用的内存。如果iframe永不加载,那么它将永远调用越来越深,直到内存用完。我建议setInterval改为进行轮询。
eremzeit 2015年

15

在IFrame中,我通常通过将一个小脚本放在代码块的末尾来解决此问题:

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

这项工作大部分时间都在我身上。有时最简单,最幼稚的解决方案是最合适的。


4
+1这个解决方案对我来说很棒!一个很棒的补充是您可以parent获取jQuery副本并用于parent.$(document).ready(function(){ parent.IFrameLoaded( ); });初始化iframe。
大卫·默多克

9

按照DrJokepu和David Murdoch的想法,我实施了一个更完整的版本。它要求在父级和iframe上都使用jQuery,并将iframe置于您的控件中。

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");
        });
    });
}

父级测试代码:

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

无论出于何种原因,在父级中使用$(iframe).ready(function ...)都不适合我。似乎在iframe dom准备就绪之前就已经执行了回调函数。使用此方法效果很好!
w-- 2014年

4

找到了解决问题的办法。

当您单击打开iframe的thickbox链接时,它将插入ID为TB_iframeContent的iframe。

不必依赖$(document).readyiframe代码中的事件,我只需要绑定到父文档中iframe的load事件:

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

该代码位于iframe中,但绑定到父文档中的控件事件。它适用于FireFox和IE。


9
找到了解决方案?看来Pier已经发布了它。不管您是否自己找到它,礼节都会接受他的回答,从而奖励他花在回答您上的时间。
Sky Sanders '04

码头的解决方案,这(和我在我的代码丢失)是上下文之间的区别top.document,让我有代码里面的iframe能够在iframe中加载告诉。(尽管load自此回答以来已过时,应将其替换为on("load")。)
Teepeemm 2014年

2

基本上其他人已经发布了,但是恕我直言有点干净:

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

1

试试这个,

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

然后,您需要做的就是创建JavaScript函数testframe_loaded()。


1
加载的问题是,当所有图像和子帧都加载后会触发。像ready事件这样的jQuery会更有用。
汤姆(Tom)

1

我正在将带有jQuery ajax的PDF加载到浏览器缓存中。然后,使用浏览器缓存中已存在的数据创建嵌入式元素。我想它也可以与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();
    }
});

您还必须正确设置http标头。


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

1
请注意,它不能在缓存可能小于PDF大小的移动浏览器中正常工作。
Pavel Savara 2014年

1

这是我与客户碰到的确切问题。我创建了一个小jQuery插件,似乎可以为iframe做好准备。它使用轮询来检查iframe文档readyState和内部文档url以及iframe源,以确保iframe实际上是“就绪”的。

“ onload”的问题是,您需要访问添加到DOM的实际iframe,否则,您需要尝试捕获iframe的加载,如果该iframe进行了缓存,则可能不需要。我需要的是可以随时调用的脚本,它可以确定iframe是否“就绪”。

这是问题:

确定本地iframe是否已加载的圣杯

这是我最终想到的jsfiddle。

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

在上面的jsfiddle中,我正在等待onload将iframe附加到dom,然后检查iframe的内部文档的就绪状态-应该是跨域的,因为它指向维基百科-但Chrome似乎报告“完成”。当iframe实际上准备就绪时,便会调用该插件的iready方法。回调尝试再次检查内部文档的就绪状态-这次报告跨域请求(这是正确的)-无论如何,它似乎可以满足我的需求,希望对其他人有所帮助。

<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>

为我工作得很好
Hassan Tareq

0

答案中的此功能是处理此问题的最佳方法,$.ready因为iframe明确失败。这是不支持此决定的决定

load如果iframe已加载,则该事件也不会触发。非常令人沮丧的是,这仍然是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();
}

}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.