如何检测 iframe 何时已加载 [英] How to detect when an iframe has already been loaded

查看:36
本文介绍了如何检测 iframe 何时已加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果在 iframe 加载完成后附加 $('#someIframe').load(function(){...}) 似乎不会触发.那是对的吗?

It seems that $('#someIframe').load(function(){...}) won't fire if it is attached after the iframe has finished loading. Is that correct?

我真正想要的是有一个函数,它总是在 iframe 加载时或之后调用一次.为了更清楚地说明这一点,这里有两种情况:

What I'd really like is to have a function that is always called once when or after an iframe has loaded. To make this clearer, here are two cases:

  • Iframe 尚未加载:加载后运行回调函数.
  • Iframe 已经加载:立即运行回调.
  • Iframe hasn't loaded yet: run a callback function once it loads.
  • Iframe has already loaded: run the callback immediately.

我该怎么做?

推荐答案

我一直在用头撞墙,直到我发现这里发生了什么.

I've banged my head against a wall until I found out what's happening here.

  • 如果 iframe 已经加载,则无法使用 .load()(事件永远不会触发)
  • 不支持在 iframe 元素上使用 .ready()(参考) 并且即使 iframe 尚未加载也会立即调用回调
  • 使用 postMessage 或在 iframe 内的 load 上调用容器函数只有在控制它时才可能
  • 在容器上使用 $(window).load() 也会等待其他资产加载,如图像和其他 iframe.如果您只想等待特定的 iframe,这不是解决方案
  • 在 Chrome 中检查 readyState 是否已触发 onload 事件是没有意义的,因为 Chrome 使用about:blank"空页面初始化每个 iframe.这个页面的readyState可能是complete,但不是你期望的页面的readyState(src属性).
  • Using .load() isn't possible if the iframe has already been loaded (event will never fire)
  • Using .ready() on an iframe element isn't supported (reference) and will call the callback immediately even if the iframe isn't loaded yet
  • Using postMessage or a calling a container function on load inside the iframe is only possible when having control over it
  • Using $(window).load() on the container would also wait for other assets to load, like images and other iframes. This is not a solution if you want to wait only for a specific iframe
  • Checking readyState in Chrome for an alredy fired onload event is meaningless, as Chrome initializes every iframe with an "about:blank" empty page. The readyState of this page may be complete, but it's not the readyState of the page you expect (src attribute).

以下是必要的:

  1. 如果 iframe 尚未加载,我们可以观察 .load() 事件
  2. 如果 iframe 已经加载,我们需要检查 readyState
  3. 如果 readyStatecomplete,我们通常可以假设 iframe 已经加载.但是,由于 Chrome 的上述行为,我们还需要检查它是否为空页面的 readyState
  4. 如果是这样,我们需要间隔一段时间观察readyState以检查实际文档(与src属性相关)是否complete
  1. If the iframe is not loaded yet we can observe the .load() event
  2. If the iframe has been loaded already we need to check the readyState
  3. If the readyState is complete, we can normally assume that the iframe has already been loaded. However, because of the above-named behavior of Chrome we furthermore need to check if it's the readyState of an empty page
  4. If so, we need to observe the readyState in an interval to check if the actual document (related to the src attribute) is complete

我已经用以下函数解决了这个问题.它已(转译为 ES5)在

I've solved this with the following function. It has been (transpiled to ES5) successfully tested in

  • 铬 49
  • Safari 5
  • 火狐 45
  • IE 8、9、10、11
  • 边缘 24
  • iOS 8.0(Safari 移动版")
  • Android 4.0(浏览器")

函数取自 jquery.mark

/**
 * Will wait for an iframe to be ready
 * for DOM manipulation. Just listening for
 * the load event will only work if the iframe
 * is not already loaded. If so, it is necessary
 * to observe the readyState. The issue here is
 * that Chrome will initialize iframes with
 * "about:blank" and set its readyState to complete.
 * So it is furthermore necessary to check if it's
 * the readyState of the target document property.
 * Errors that may occur when trying to access the iframe
 * (Same-Origin-Policy) will be catched and the error
 * function will be called.
 * @param {jquery} $i - The jQuery iframe element
 * @param {function} successFn - The callback on success. Will 
 * receive the jQuery contents of the iframe as a parameter
 * @param {function} errorFn - The callback on error
 */
var onIframeReady = function($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) { // accessing contentWindow failed
        errorFn();
    }
};

工作示例

由两个文件(index.html 和 iframe.html)组成:index.html:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Parent</title>
</head>
<body>
    <script src="https://code.jquery.com/jquery-1.12.2.min.js"></script>
    <script>
        $(function() {

            /**
             * Will wait for an iframe to be ready
             * for DOM manipulation. Just listening for
             * the load event will only work if the iframe
             * is not already loaded. If so, it is necessary
             * to observe the readyState. The issue here is
             * that Chrome will initialize iframes with
             * "about:blank" and set its readyState to complete.
             * So it is furthermore necessary to check if it's
             * the readyState of the target document property.
             * Errors that may occur when trying to access the iframe
             * (Same-Origin-Policy) will be catched and the error
             * function will be called.
             * @param {jquery} $i - The jQuery iframe element
             * @param {function} successFn - The callback on success. Will 
             * receive the jQuery contents of the iframe as a parameter
             * @param {function} errorFn - The callback on error
             */
            var onIframeReady = function($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) { // accessing contentWindow failed
                    errorFn();
                }
            };

            var $iframe = $("iframe");
            onIframeReady($iframe, function($contents) {
                console.log("Ready to got");
                console.log($contents.find("*"));
            }, function() {
                console.log("Can not access iframe");
            });
        });
    </script>
    <iframe src="iframe.html"></iframe>
</body>
</html>

iframe.html:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Child</title>
</head>
<body>
    <p>Lorem ipsum</p>
</body>
</html>

您还可以将 index.html 中的 src 属性更改为例如"http://example.com/".随便玩玩吧.

You can also change the src attribute inside index.html to e.g. "http://example.com/". Just play around with it.

这篇关于如何检测 iframe 何时已加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆