動的に挿入されたiframeでのjQuery .ready


184

私たちは、誰かが画像をクリックしたときに動的にiframeを表示するためにjQuery thickboxを使用しています。このiframeでは、複数の画像を表示するためにJavaScriptライブラリのガレリアを使用しています。

問題は$(document).ready、iframeでの起動が早すぎるようで、iframeコンテンツがまだロードされていないため、ガレリアコードがDOM要素に適切に適用されていないようです。 $(document).readyiframeの準備完了状態を使用して、iframeの準備ができているかどうかを判断しているようです。

documentによって呼び出された関数を別の関数で抽出し、100 msのタイムアウト後に呼び出した場合。それは機能しますが、遅いコンピューターでの運用ではチャンスを利用できません。

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

私の質問:動的iframeの準備ができて、親だけではないときにコードを実行できるようにするには、どのjQueryイベントにバインドする必要がありますか?


1
そして、ガレリアがiframe経由ではなく直接ロードしたときに動作することを確認しましたね。
Jason Kealey、2008年

はい、ガレリアは通常のページで直接使用すると完全に機能します。
EtienneT 2008年

回答:


291

私は同様の質問に答えました(IFRAMEのロードが完了したらJavaScriptコールバックを参照してください)。次のコードを使用して、iframeロードイベントを制御できます。

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

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

iframeを処理する際、ドキュメント準備イベントの代わりにロードイベントを使用するのに十分なことがわかりました。


17
attr( 'src')を呼び出す前にloadイベントを設定するべきではありませんか?
Shay Erlichmen、

15
いいえ、関係ありません。Loadイベントは、少なくとも次のイベントループまで発生しません。
Barum Rho

29
ロードイベントは、ダウンロードに使用されるiframeでは機能しません。<iframe src = "my.pdf" />など
Mike Starov

5
ロードの問題は、すべての画像とサブフレームがロードされたときに発生することです。jQueryのようなreadyイベントはもっと便利でしょう。
トム


30

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

@ cam8001:それはタイプミスでした-今修正されました。
MARÖrlygsson

私も投票ソリューションを使用してしまいました。他の解決策は部分的に成功した場合にのみ機能するようでした。しかし、私にとっては、成功する前に、iframeのコンテンツだけでなく、javascript関数の存在を確認する必要がありました。例えば。(typeof iframe.contentWindow.myfunc == 'function')
Julian

2
このソリューションは再帰的であり、呼び出しごとにメモリを消費します。iframeがロードされない場合は、メモリがなくなるまで永遠に深く呼び出されます。setInterval代わりにポーリングをお勧めします。
eremzeit 2015

15

IFrameでは通常、ブロックの最後に小さなスクリプトを挿入することでこの問題を解決します。

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

ほとんどの場合、これは私にとってはうまくいきます。時には、最も単純で最も単純な解決策が最も適切です。


4
+1この解決策は私にとってとても効果的です!すばらしい点の1つはparent、jQueryのコピーを取得しparent.$(document).ready(function(){ parent.IFrameLoaded( ); });、iframeを初期化するために使用できることです。
David Murdoch

9

DrJokepuとDavid Murdochのアイデアに従って、より完全なバージョンを実装しました。それは必要とし、あなたの制御にあるように、親とiframeとiframeの両方でのjQueryを。

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の準備が整う前に、コールバック関数が実行されているように見えました。この方法を使用するとうまくいきました!
w-- 2014年

4

問題の解決策を見つけました。

iframeを開くシックボックスリンクをクリックすると、IDがTB_iframeContentのiframeが挿入されます。

$(document).readyiframeコードのイベントに依存する代わりに、親ドキュメントのiframeのloadイベントにバインドする必要があります。

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

このコードはiframe内にありますが、親ドキュメントのコントロールのイベントにバインドします。FireFoxとIEで動作します。


9
解決策を見つけましたか?ピアはすでに投稿したようです。あなたがそれを自分で見つけたかどうかに関係なく、エチケットは彼の答えを受け入れることでした。
スカイサンダーズ

Pierのソリューションとこれ(およびコードに欠けていたもの)の違いは、iframeが読み込まれたときにiframe top.documentコードで通知できるようにするコンテキストです。(ただし、この回答以降は廃止されており、で置き換える必要があります。)loadon("load")
Teepeemm

2

基本的に他の人がすでに投稿したものですが、私見は少しきれいです:

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

1

これを試して、

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

次に、JavaScript関数testframe_loaded()を作成するだけです。


1
ロードの問題は、すべての画像とサブフレームがロードされたときに発生することです。jQueryのようなreadyイベントはもっと便利でしょう。
トム

1

私はjQuery 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";

1
キャッシュがPDFのサイズよりも小さい可能性のあるモバイルブラウザーではうまく機能しないことに注意してください。
Pavel Savara 2014

1

これは、私がクライアントと遭遇した正確な問題でした。iframeの準備が整っているように見える小さなjqueryプラグインを作成しました。ポーリングを使用して、iframeドキュメントのreadyStateと、iframeソースと組み合わせた内部ドキュメントのURLを組み合わせてチェックし、iframeが実際に「準備完了」であることを確認します。

「onload」の問題は、DOMに追加されている実際のiframeにアクセスする必要があることです。それができない場合は、iframeのロードをキャッチする必要があります。キャッシュされている場合は、キャッチできません。必要なのは、いつでも呼び出すことができ、iframeが「準備完了」かどうかを判断できるスクリプトです。

ここに質問があります:

ローカルiframeが読み込まれたかどうかを判断するための聖杯

そして、これが私が最終的に思いついたjsfiddleです。

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

上記のjsfiddleで、onloadがiframeをdomに追加するのを待ってから、iframeの内部ドキュメントの準備完了状態をチェックします。ウィキペディアを指し示しているため、クロスドメインである必要がありますが、Chromeは「完了」と報告しているようです。プラグインのireadyメソッドは、iframeが実際に準備ができると呼び出されます。コールバックは、内部ドキュメントの準備完了状態をもう一度確認しようとします-今回はクロスドメインリクエスト(正しい)を報告します-とにかくそれは私が必要とするもののために機能しているようで、それが他の人を助けることを願っています。

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

私にとってはうまくいきました
Hassan Tareq

0

この回答のこの関数は、$.readyiframeで明示的に失敗するため、これを処理する最良の方法です。これをサポートしない決定は次のとおりです。

loadIFRAMEがすでにロードされている場合、イベントも発生しません。これが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();
}

}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.