jQuery. Sudah dalam iframe yang dimasukkan secara dinamis

184

Kami menggunakan jQuery thickbox untuk secara dinamis menampilkan iframe ketika seseorang mengklik gambar. Dalam iframe ini, kami menggunakan galleria perpustakaan javascript untuk menampilkan banyak gambar.

Masalahnya adalah bahwa $(document).readydalam iframe tampaknya dipecat terlalu cepat dan konten iframe bahkan belum dimuat, jadi kode galleria tidak diterapkan dengan benar pada elemen DOM. $(document).readytampaknya menggunakan status siap induk iframe untuk memutuskan apakah iframe siap.

Jika kita mengekstrak fungsi yang disebut oleh dokumen siap dalam fungsi terpisah dan memanggilnya setelah batas waktu 100 ms. Ini bekerja, tetapi kami tidak dapat mengambil risiko dalam produksi dengan komputer yang lambat.

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

Pertanyaan saya: acara jQuery mana yang harus kita ikat untuk dapat mengeksekusi kode kita ketika iframe dinamis sudah siap dan bukan hanya itu orangtua?

EtienneT
sumber
1
Dan Anda mengonfirmasi bahwa galleria berfungsi saat Anda memuatnya secara langsung alih-alih melalui iframe, benar?
Jason Kealey
Ya, galleria berfungsi dengan baik saat kami menggunakannya langsung di halaman normal.
EtienneT

Jawaban:

291

Saya menjawab pertanyaan serupa (lihat Javascript callback ketika IFRAME selesai memuat? ). Anda dapat memperoleh kendali atas acara pemuatan iframe dengan kode berikut:

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

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

Dalam berurusan dengan iframe, saya menemukan cukup baik untuk menggunakan memuat acara, bukan acara siap dokumen.

Dermaga Luigi
sumber
17
Bukankah Anda seharusnya mengatur acara memuat sebelum memanggil attr ('src')?
Shay Erlichmen
15
Tidak, itu tidak masalah. Muat acara tidak akan diaktifkan hingga loop acara berikutnya minimum.
Barum Rho
29
acara pemuatan tidak akan berfungsi untuk iframe yang digunakan untuk mengunduh. seperti <iframe src = "my.pdf" />
Mike Starov
5
Masalah dengan pemuatan adalah bahwa itu menyala ketika semua gambar dan subframe telah dimuat. Acara jQuery like ready akan lebih bermanfaat.
Tom
30

Menggunakan jQuery 1.3.2 berikut ini bekerja untuk saya:

$('iframe').ready(function() {
  $('body', $('iframe').contents()).html('Hello World!');
});

REVISI:! Sebenarnya kode di atas terkadang terlihat berfungsi di Firefox, tidak pernah terlihat berfungsi di Opera.

Alih-alih saya menerapkan solusi pemungutan suara untuk tujuan saya. Sederhana seperti ini:

$(function() {
  function manipIframe() {
    el = $('body', $('iframe').contents());
    if (el.length != 1) {
      setTimeout(manipIframe, 100);
      return;
    }
    el.html('Hello World!');
  }
  manipIframe();
});

Ini tidak memerlukan kode di halaman iframe yang dipanggil. Semua kode berada dan dijalankan dari bingkai / jendela induk.

Már Örlygsson
sumber
Apa fungsi movePreview yang dimaksud dalam setTimeout ()?
cam8001
@ cam8001: Itu salah ketik - sekarang telah diperbaiki.
Már Örlygsson
Saya akhirnya menggunakan solusi polling juga. Solusi lain tampaknya hanya berhasil dengan keberhasilan parsial. Namun, bagi saya, saya perlu memeriksa keberadaan fungsi javascript, bukan hanya untuk isi iframe, sebelum saya sukses. misalnya. (typeof iframe.contentWindow.myfunc == 'function')
Julian
2
Solusi ini bersifat rekursif yang membutuhkan memori untuk setiap panggilan. Jika iframe tidak pernah dimuat, maka ia akan memanggil lebih dalam dan lebih dalam sampai memori habis. Saya akan merekomendasikan setIntervaljajak pendapat sebagai gantinya.
eremzeit
15

Di IFrames saya biasanya menyelesaikan masalah ini dengan meletakkan skrip kecil di bagian paling akhir dari blok:

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

Ini bekerja sebagian besar waktu untuk saya. Terkadang solusi paling sederhana dan paling naif adalah yang paling tepat.

Tamas Czinege
sumber
4
+1 Solusi ini sangat cocok untuk saya! Satu tambahan yang bagus adalah Anda dapat menjangkau hingga parentuntuk mengambil salinan jQuery dan menggunakannya parent.$(document).ready(function(){ parent.IFrameLoaded( ); });untuk menginisialisasi iframe.
David Murdoch
9

Mengikuti ide DrJokepu dan David Murdoch saya mengimplementasikan versi yang lebih lengkap. Ini membutuhkan jQuery di kedua induk dan iframe dan iframe berada di kendali Anda.

kode 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");
        });
    });
}

kode uji induk:

$(function(){
    $("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
        console.log(e.type);
    });
});
Ricardo Freitas
sumber
untuk alasan apa pun menggunakan $ (iframe). Sudah (fungsi ...) pada orang tua tidak akan bekerja untuk saya. Sepertinya fungsi callback dijalankan sebelum iframe dom siap. Menggunakan metode ini berhasil!
w--
4

Menemukan solusi untuk masalah tersebut.

Saat Anda mengeklik tautan kotak tebal yang membuka iframe, itu memasukkan iframe dengan id TB_iframeContent.

Alih-alih mengandalkan $(document).readyacara dalam kode iframe, saya hanya harus mengikat ke acara memuat iframe di dokumen induk:

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

Kode ini ada di iframe tetapi mengikat ke acara kontrol dalam dokumen induk. Ia bekerja di FireFox dan IE.

EtienneT
sumber
9
Menemukan solusinya? Sepertinya Pier sudah mempostingnya. Entah Anda menemukannya sendiri atau tidak, etiket berarti menerima jawabannya, sehingga menghargai waktu yang dihabiskannya untuk menjawab Anda.
Sky Sanders
Perbedaan antara solusi Pier dan ini (dan apa yang saya hilang dalam kode saya) adalah konteks top.documentyang memungkinkan saya untuk memiliki kode di dalam iframe dapat mengetahui kapan iframe telah dimuat. (Meskipun loadtelah usang sejak jawaban ini, dan harus diganti dengan on("load").)
Teepeemm
2

Pada dasarnya apa yang sudah diposkan orang lain tetapi IMHO sedikit lebih bersih:

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

Coba ini,

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

Yang perlu Anda lakukan adalah membuat fungsi JavaScript testframe_loaded ().

Danny G
sumber
1
Masalah dengan pemuatan adalah bahwa itu menyala ketika semua gambar dan subframe telah dimuat. Acara jQuery like ready akan lebih bermanfaat.
Tom
1

Saya memuat PDF dengan jQuery ajax ke cache browser. Lalu saya membuat elemen tertanam dengan data yang sudah ada di cache browser. Saya kira itu akan bekerja dengan iframe juga.


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();
    }
});

Anda harus mengatur tajuk http dengan benar.


HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";
Pavel Savara
sumber
1
Perhatikan, itu tidak berfungsi dengan baik pada browser seluler di mana cache bisa lebih kecil dari ukuran PDF.
Pavel Savara
1

Ini adalah masalah persis yang saya temui dengan klien kami. Saya membuat plugin jquery kecil yang tampaknya berfungsi untuk kesiapan iframe. Menggunakan polling untuk memeriksa dokumen iframe readyState yang dikombinasikan dengan url dokumen bagian dalam yang dikombinasikan dengan sumber iframe untuk memastikan iframe sebenarnya "siap".

Masalah dengan "onload" adalah bahwa Anda memerlukan akses ke iframe aktual yang ditambahkan ke DOM, jika Anda tidak maka Anda perlu mencoba untuk menangkap pemuatan iframe yang jika di-cache maka Anda mungkin tidak. Yang saya butuhkan adalah skrip yang bisa dipanggil kapan saja, dan menentukan apakah iframe itu "siap" atau tidak.

Inilah pertanyaannya:

Cawan suci untuk menentukan apakah iframe lokal telah dimuat atau tidak

dan inilah jsfiddle yang akhirnya kutemukan.

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

Dalam jsfiddle di atas, saya menunggu onload untuk menambahkan iframe ke dom, kemudian memeriksa status ready dokumen dalam iframe - yang harus lintas domain karena itu menunjuk ke wikipedia - tetapi Chrome tampaknya melaporkan "lengkap". Metode iready plug-in kemudian dipanggil ketika iframe sebenarnya sudah siap. Callback mencoba untuk memeriksa status siap dokumen dalam lagi - kali ini melaporkan permintaan lintas domain (yang benar) - bagaimanapun tampaknya berfungsi untuk apa yang saya butuhkan dan berharap itu membantu orang lain.

<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>
Jon Freynik
sumber
Bekerja dengan baik untuk saya
Hassan Tareq
0

Fungsi dari jawaban ini adalah cara terbaik untuk menangani ini karena $.readygagal secara eksplisit untuk iframe. Inilah keputusan untuk tidak mendukung ini.

The loadevent juga tidak api jika iframe telah dimuat. Sangat frustasi bahwa ini tetap menjadi masalah di tahun 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();
}

}

Crashalot
sumber