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

Holy grail for determining whether or not local iframe has loaded

本文关键字:加载 圣杯 是否 iframe 用于      更新时间:2023-09-26

首先,我看到这个问题被问了几次,但没有一个答案令人满意。我正在寻找的是能够随时调用脚本并确定 iframe 是否已加载 - 并且不限制脚本需要添加到 onload 属性中的 iframe 标签本身。

这里有一些背景:我一直在研究一个不显眼的脚本,试图确定 dom 中的本地 iframe 是否已加载,这是因为我们的一个客户在他们的网站上以 iframe 的形式包含表单,其中许多表单在灯箱中打开 - 它随时动态地将 iframe 添加到 dom 中。 我可以附加到灯箱的打开事件,但它是否在加载之前"捕获"iframe。

让我再解释一下。

在我的测试中,我确定 onload 事件只会触发一次 - 并且只有在 iframe 实际加载之前绑定它时。例如:此页面应该只提醒"添加到 iframe 标签",之后附加的侦听器不会触发 - 对我来说这是有道理的。(我使用 iframe onload 属性作为简单示例(。

https://jsfiddle.net/g1bkd3u1/2/

<script>
    function loaded () {
        alert ("added to iframe tag");
        $("#test").load(function(){
            alert("added after load finished");
        });
    };
</script>
<iframe onload="loaded()" id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>

我的下一个方法是检查 iframe 的文档就绪状态,这似乎几乎适用于我所有的测试,除了 chrome 报告"完成"——我期待跨域请求的"访问被拒绝"。我可以接受跨域错误,因为我可以忽略 iframe,因为我只对本地 iframe 感兴趣 - Firefox 报告"unintialized",我没问题,因为我知道我可以附加一个 onload 事件。

请在Chrome中打开:https://jsfiddle.net/g1bkd3u1/

<iframe id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>
<script>
    alert($("#test").contents()[0].readyState);
</script>

我发现如果我只等待 100 毫秒 - 那么 iframe 似乎按预期报告(跨域安全异常 - 这是我想要的 - 但我不想等待任意长度(。

https://jsfiddle.net/g1bkd3u1/4/

<iframe id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>
<script>
    setTimeout(function () { 
        try {
            alert($("#test").contents()[0].readyState);
        } catch (ignore) {
            alert("cross domain request");
        }
    }, 100);
</script>

我目前的解决方法/解决方案是添加 onload 事件处理程序,然后将 iframe 与 dom 分离,然后将其插入回 dom 的同一位置 - 现在 onload 事件将触发。下面是一个等待 3 秒(希望有足够的时间加载 iframe(的示例,以显示分离和重新附加会导致 iframe onload 事件触发。

https://jsfiddle.net/g1bkd3u1/5/

<iframe id="test" src="https://en.wikipedia.org/wiki/HTML_element#Frames"></iframe>
<script>
    setTimeout(function(){
        var placeholder = $("<span>");
        $("#test").load(function(){
            alert("I know the frame has loaded now");
        }).after(placeholder).detach().insertAfter(placeholder);
        placeholder.detach();
    }, 3000);
</script>

虽然这有效,但它让我想知道是否有更好更优雅的技术来检查 iframe 负载(不显眼(?

谢谢你的时间。

今天我实际上遇到了一个错误,我删除和重新插入 iframe 会破坏网站上的所见即所得编辑器。因此,我创建了一个小型jQuery插件的启动来检查iframe的准备情况。它还没有准备好生产,我还没有对其进行太多测试,但它应该提供分离和重新附加 iframe 的更好替代方案 - 如果需要,它确实使用轮询,但应该在 iframe 准备就绪时删除 setInterval。

它可以像这样使用:

$("iframe").iready(function() { ... });

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

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

该示例等到窗口加载后动态将 iframe 添加到 DOM,然后提醒其文档的 readyState - 在 chrome 中错误地显示"完成"。应该在之后调用 iready 函数,并且尝试输出文档的 readyState 证明跨域异常 - 同样,这还没有经过彻底测试,但适用于我需要的东西。

我遇到了类似的问题,因为我有一个 iframe,一旦完成加载就需要修改它的文档。

如果您知道或可以控制iFrame中加载的文档的内容,那么您可以简单地检查/添加一个可以检查是否存在的元素,以便更新iframe文档。至少那时您知道要使用的元素已加载到文档中。

就我而言,我调用了一个函数,该函数本身检查了我的已知元素是否存在,该元素在我需要更新的元素已经加载后总是会找到 - 如果没有找到,它通过 setTimeout(( 再次调用自己。

function updateIframeContents() {
    if ($("#theIframe").contents().find('.SaveButton').length > 0) {
        // iframe DOM Manipulation
    } else {
        setTimeout(updateIframeContents, 250);
    }
}
updateIframeContents();