如何检测何时已加载iframe

B T*_*B T 28 javascript jquery dom

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

我真正喜欢的是拥有一个在加载iframe时或之后总是被调用一次的函数.为了更清楚,这里有两种情况:

  • iframe尚未加载:加载后运行回调函数.
  • iframe已经加载:立即运行回调.

我怎样才能做到这一点?

dud*_*ude 35

在我发现这里发生了什么事之前,我一直撞到了墙上.

背景资料

  • .load()如果已经加载iframe,则无法使用(事件永远不会触发)
  • .ready()不支持在iframe元素上使用(引用),即使iframe尚未加载,也会立即调用回调
  • 只有在控制iframe时才可以在iframe内部使用postMessage或调用容器函数load
  • $(window).load()在容器上使用也会等待其他资产加载,例如图像和其他iframe.如果您只想等待特定的iframe,这不是解决方案
  • readyState在Chrome中检查alredy已启动的onload事件是没有意义的,因为Chrome会使用"about:blank"空白页面初始化每个iframe.在readyState此页面的可能complete,但它不是readyState你所希望的网页(的src属性).

以下是必要的:

  1. 如果iframe尚未加载,我们可以观察该.load()事件
  2. 如果已经加载了iframe,我们需要检查 readyState
  3. 如果readyStatecomplete,我们通常可以假设已经加载了iframe.但是,由于Chrome的上述行为,我们还需要检查它是否readyState是空页面
  4. 如果是这样,我们需要readyState在一个间隔中观察以检查实际文档(与src属性相关)是否是complete

我用以下功能解决了这个问题.它已经(转化为ES5)成功通过测试

  • Chrome 49
  • Safari 5
  • Firefox 45
  • IE 8,9,10,11
  • 边缘24
  • iOS 8.0("Safari Mobile")
  • 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();
    }
};
Run Code Online (Sandbox Code Playgroud)

工作实例

由两个文件组成(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>
Run Code Online (Sandbox Code Playgroud)

iframe.html:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Child</title>
</head>
<body>
    <p>Lorem ipsum</p>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

您还可以src将内部属性更改为index.html" http://example.com/ ".只是玩弄它.