37

$('#someIframe').load(function(){...})iframeの読み込みが終わってからつけると発火しないようです。あれは正しいですか?

私が本当に欲しいのは、iframe がロードされたときまたはロードされた後に常に 1 回呼び出される関数を持つことです。これを明確にするために、2 つのケースを次に示します。

  • Iframe がまだロードされていません: ロードされたらコールバック関数を実行してください。
  • Iframe は既に読み込まれています: コールバックをすぐに実行してください。

これどうやってするの?

4

8 に答える 8

51

ここで何が起こっているのかがわかるまで、頭を壁にぶつけました。

背景情報

  • .load()iframe が既に読み込まれている場合は使用できません (イベントは発生しません)。
  • iframe 要素での使用.ready()はサポートされておらず (参照)、iframe がまだ読み込まれていない場合でも、すぐにコールバックが呼び出されます。
  • iframe 内でのコンテナー関数の使用postMessageまたは呼び出しは、loadそれを制御できる場合にのみ可能です。
  • コンテナーでを使用$(window).load()すると、画像や他の iframe などの他のアセットが読み込まれるまで待機します。特定の iframe だけを待ちたい場合、これは解決策ではありません
  • readyStateChrome はすべての iframe を「about:blank」の空のページで初期化するため、既に発生した onload イベントを Chrome でチェックしても意味がありません。このreadyStateページの は である可能性がありますが、期待するページcompleteの ではありませんreadyState(src属性)。

解決

以下が必要です。

  1. iframe がまだロードされていない場合は、.load()イベントを観察できます
  2. iframe がすでに読み込まれている場合は、readyState
  3. readyStateがの場合、complete通常は iframe が既に読み込まれていると見なすことができます。ただし、上記の Chrome の動作のためreadyState、空のページかどうかをさらに確認する必要があります。
  4. もしそうreadyStateなら、実際のドキュメント (src 属性に関連する) がcomplete

私はこれを次の関数で解決しました。これは (ES5 にトランスパイルされ) で正常にテストされています。

  • クロム 49
  • サファリ5
  • Firefox 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();
    }
};

実施例

2 つのファイル (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>

src内部の属性index.htmlを「http://example.com/ 」などに変更することもできます。遊んでみてください。

于 2016-03-22T13:23:13.640 に答える
0

クロスブラウザーで一貫して機能するソリューションにたどり着くのに非常に苦労しました。重要: 私はそのような解決策にたどり着くことができませんでした。しかし、ここに私が得た限りです:

// runs a function after an iframe node's content has loaded
// note, this almost certainly won't work for frames loaded from a different domain
// secondary note - this doesn't seem to work for chrome : (
// another note - doesn't seem to work for nodes created dynamically for some reason
function onReady(iframeNode, f) {
    var windowDocument = iframeNode[0].contentWindow.document;
    var iframeDocument = windowDocument?windowDocument : iframeNode[0].contentWindow.document;

    if(iframeDocument.readyState === 'complete') {
        f();
    } else {
        iframeNode.load(function() {
            var i = setInterval(function() {
                if(iframeDocument.readyState === 'complete') {
                    f();
                    clearInterval(i);
                }
            }, 10);
        });
    }
}

そして、私はそれを次のように使用していました:

onReady($("#theIframe"), function() {
    try {
        var context = modal[0].contentWindow;
        var i = setInterval(function() {
            if(context.Utils !== undefined && context.$) { // this mess is to attempt to get it to work in firefox
                context.$(function() {
                    var modalHeight = context.someInnerJavascript();

                    clearInterval(i);
                });
            }
        }, 10);
    } catch(e) { // ignore
        console.log(e);
    }
});

これでも問題は解決しないことに注意してください。このソリューションにはいくつかの問題があります。

  • onReady では、動的に追加された iframe の場合、iframeDocument.readyState が「初期化されていない」状態でスタックしているように見えるため、コールバックが起動しません。
  • なんらかの理由で、セットアップ全体がまだ Firefox で機能していないようです。setInterval 関数が外部でクリアされているようです。
  • これらの問題のいくつかは、ページに他の多くのものがロードされている場合にのみ発生することに注意してください。これにより、これらのタイミングが決定的ではなくなります。

したがって、誰かがこれを改善できる場合は、大歓迎です。

于 2013-06-27T18:54:20.167 に答える
-1

iframe 内のコンテンツがロードされた場合にのみ、innerDoc は true になり、if 内のコードを起動します。

    window.onload = function(){
 function manipulateIframe(iframeId, callback) {
     var iframe = document.getElementById(iframeId).contentWindow.document;
         callback(iframe);
 };
 manipulateIframe('IFwinEdit_forms_dr4r3_forms_1371601293572', function (iframe) {
     console.log(iframe.body);
 });};

于 2013-06-18T07:23:52.420 に答える