【问题标题】:jQuery .ready in a dynamically inserted iframejQuery .ready 在动态插入的 iframe 中
【发布时间】:2010-09-17 07:59:12
【问题描述】:

我们正在使用 jQuery thickbox 在有人点击图片时动态显示 iframe。在这个 iframe 中,我们使用 galleria 一个 javascript 库来显示多张图片。

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

如果我们将 document ready 调用的函数提取到一个单独的函数中,并在 100 毫秒超时后调用它。它可以工作,但我们不能在速度慢的计算机上冒险。

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

我的问题:我们应该绑定哪个 jQuery 事件才能在动态 iframe 准备好时执行我们的代码,而不仅仅是它的父级?

【问题讨论】:

  • 当您直接加载而不是通过 iframe 加载 Galleria 时,您确认它可以正常工作,对吗?
  • 是的,当我们直接在普通页面中使用 Galleria 时,它可以完美运行。

标签: javascript jquery iframe thickbox galleria


【解决方案1】:

我回答了一个类似的问题(请参阅Javascript callback when IFRAME is finished loading?)。 您可以通过以下代码获得对 iframe 加载事件的控制:

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

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

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

【讨论】:

  • 不应该在调用attr('src')之前设置加载事件吗?
  • 不,没关系。 Load 事件至少在下一个事件循环之前不会触发。
  • 加载事件不适用于用于下载的 iframe。像
  • 加载的问题是它会在所有图像和子帧都加载后触发。一个类似 jQuery 的 ready 事件会更有用。
【解决方案2】:

在 IFrames 中,我通常通过在块的最后放置一个小脚本来解决这个问题:

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

这对我来说大部分时间都在工作。有时最简单、最天真的解决方案是最合适的。

【讨论】:

  • +1 这个解决方案非常适合我!一个很棒的补充是您可以使用parent 来获取jQuery 的副本并使用parent.$(document).ready(function(){ parent.IFrameLoaded( ); }); 来初始化iframe。
【解决方案3】:

找到问题的解决方案。

当您单击打开 iframe 的厚框链接时,它会插入一个 id 为 TB_iframeContent 的 iframe。

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

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

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

【讨论】:

  • 找到解决方案了吗?看起来 Pier 已经发布了它。不管你是不是自己找到的,礼仪都是接受他的回答,从而奖励他为你回答所花的时间。
  • Pier 的解决方案和这个(以及我在代码中缺少的)之间的区别在于上下文 top.document 允许我在 iframe 中拥有代码inside告诉 iframe 何时加载。 (尽管load 自此答案以来已被弃用,应替换为on("load")。)
【解决方案4】:

使用 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:这是一个错字 - 现在已修复。
  • 我最终也使用了投票解决方案。其他解决方案似乎只取得了部分成功。但是,对我来说,在成功之前,我需要检查是否存在 javascript 函数,而不仅仅是 iframe 的内容。例如。 (typeof iframe.contentWindow.myfunc == 'function')
  • 这个解决方案是递归的,每次调用都会占用内存。如果 iframe 永远不会加载,那么它将永远调用越来越深,直到内存耗尽。我建议使用setInterval 进行轮询。
【解决方案5】:

试试这个,

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

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

【讨论】:

  • 加载的问题是它会在所有图像和子帧都加载后触发。类似 jQuery 的 ready 事件会更有用。
【解决方案6】:

我正在将带有 jQ​​uery 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";

【讨论】:

  • 请注意,它不适用于缓存可能小于 PDF 大小的移动浏览器。
【解决方案7】:

按照 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 准备好之前就被执行了。使用这种方法效果很好!
【解决方案8】:

这正是我与客户遇到的问题。我创建了一个似乎适用于 iframe 准备工作的小 jquery 插件。它使用轮询来检查 iframe 文档 readyState 结合内部文档 url 结合 iframe 源,以确保 iframe 实际上是“就绪”。

“onload”的问题是您需要访问被添加到 DOM 的实际 iframe,如果您不这样做,那么您需要尝试捕获 iframe 加载,如果它被缓存,那么您可能不会。我需要的是一个可以随时调用的脚本,并确定 iframe 是否“就绪”。

问题来了:

Holy grail for determining whether or not local iframe has loaded

这是我最终想出的 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>

【讨论】:

  • 对我来说效果很好
【解决方案9】:

基本上是其他人已经发布的内容,但恕我直言,更简洁:

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

【讨论】:

    【解决方案10】:

    this answer 中的此函数是处理此问题的最佳方法,因为 $.ready 显式地对 iframe 失败。 Here's the decision 不支持这个。

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

    }

    【讨论】:

      猜你喜欢
      • 2011-03-30
      • 1970-01-01
      • 2013-12-08
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多