jQuery. Ready ใน iframe ที่แทรกแบบไดนามิก


184

เราใช้ jQuery thickboxเพื่อแสดง iframe แบบไดนามิกเมื่อมีคนคลิกที่รูปภาพ ใน iframe นี้เรากำลังใช้ไลบรารี javascript แกลเลอเรียเพื่อแสดงรูปภาพหลายภาพ

ปัญหาน่าจะเป็นที่$(document).readyiframe ดูเหมือนว่าจะถูกไล่ออกเร็วเกินไปและเนื้อหา iframe ยังไม่ได้โหลดดังนั้นรหัส galleria จึงไม่ได้ใช้อย่างถูกต้องในองค์ประกอบ DOM $(document).readyดูเหมือนว่าจะใช้สถานะพร้อมผู้ปกครอง iframe เพื่อตัดสินใจว่า iframe พร้อมหรือไม่

ถ้าเราแยกฟังก์ชั่นที่เรียกโดยเอกสารพร้อมในฟังก์ชั่นแยกต่างหากและเรียกมันหลังจากหมดเวลา 100 ms ใช้งานได้ แต่เราไม่สามารถรับโอกาสในการผลิตด้วยคอมพิวเตอร์ที่ช้า

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

คำถามของฉัน: เหตุการณ์ jQuery ใดที่เราควรผูกไว้เพื่อให้สามารถเรียกใช้รหัสของเราได้เมื่อ iframe แบบไดนามิกพร้อมใช้งานและไม่ใช่เฉพาะผู้ปกครองเท่านั้น


1
และคุณยืนยันว่าแกลเลอเรียทำงานเมื่อคุณโหลดโดยตรงแทนที่จะผ่าน iframe ถูกต้องไหม
Jason Kealey

ใช่แกลเลอเรียทำงานได้อย่างสมบูรณ์แบบเมื่อเราใช้โดยตรงในหน้าปกติ
EtienneT

คำตอบ:


291

ฉันตอบคำถามที่คล้ายกัน (ดูJavascript callback เมื่อ IFRAME เสร็จสิ้นการโหลดหรือไม่ ) คุณสามารถควบคุมเหตุการณ์โหลด iframe ได้ด้วยรหัสต่อไปนี้:

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

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

ในการจัดการกับ iframes ฉันพบว่าดีพอที่จะใช้เหตุการณ์โหลดแทนเหตุการณ์เอกสารพร้อม


17
คุณไม่ควรตั้งค่าเหตุการณ์โหลดก่อนที่จะเรียก attr ('src') ใช่ไหม
Shay Erlichmen

15
ไม่มันไม่สำคัญ เหตุการณ์การโหลดจะไม่เริ่มจนกว่าจะมีเหตุการณ์วนรอบต่อไปอย่างน้อยที่สุด
Barum Rho

29
เหตุการณ์โหลดจะไม่ทำงานสำหรับ iframes ที่ใช้สำหรับการดาวน์โหลด ชอบ <iframe src = "my.pdf" />
Mike Starov

5
ปัญหาเกี่ยวกับโหลดคือไฟเมื่อภาพและเฟรมย่อยทั้งหมดโหลด jQuery เช่นเหตุการณ์ที่พร้อมจะมีประโยชน์มากกว่า
Tom


30

ใช้ jQuery 1.3.2 ต่อไปนี้ทำงานให้ฉัน:

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

REVISION :! ที่จริงแล้วโค้ดด้านบนบางครั้งดูเหมือนว่าจะทำงานใน Firefox ไม่เคยดูเหมือนว่ามันทำงานได้ใน Opera

แต่ฉันใช้วิธีการลงคะแนนเลือกตั้งเพื่อจุดประสงค์ของฉัน เรียบง่ายลงดูเหมือนว่า:

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

สิ่งนี้ไม่จำเป็นต้องใช้รหัสในหน้า iframe ที่เรียกว่า รหัสทั้งหมดอยู่และดำเนินการจากเฟรมหลัก / หน้าต่าง


movePreview fucntion หมายถึงอะไรใน setTimeout ()
cam8001

@ cam8001: มันเป็นตัวพิมพ์ผิด - ตอนนี้ได้รับการแก้ไขแล้ว
MárÖrlygsson

ฉันลงเอยด้วยการใช้โซลูชันการลงคะแนนด้วย โซลูชันอื่น ๆ ดูเหมือนจะทำงานได้เฉพาะกับความสำเร็จบางส่วน อย่างไรก็ตามสำหรับฉันฉันต้องตรวจสอบการมีอยู่ของฟังก์ชั่นจาวาสคริปต์มากกว่าเพียงแค่เนื้อหาของ iframe ก่อนที่ฉันจะประสบความสำเร็จ เช่น. (typeof iframe.contentWindow.myfunc == 'function')
Julian

2
โซลูชันนี้เรียกซ้ำซึ่งใช้หน่วยความจำสำหรับการโทรแต่ละครั้ง หาก iframe ไม่เคยโหลดมันจะโทรลึกและลึกกว่านี้ตลอดไปจนกว่าหน่วยความจำจะหมด ฉันแนะนำsetIntervalให้ทำการสำรวจแทน
eremzeit

15

ใน IFrames ฉันมักจะแก้ปัญหานี้โดยวางสคริปต์เล็ก ๆ ไว้ที่ส่วนท้ายสุดของบล็อก:

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

งานนี้ใช้เวลาส่วนใหญ่สำหรับฉัน บางครั้งการแก้ปัญหาที่ง่ายที่สุดและไร้เดียงสาที่สุดเหมาะสมที่สุด


4
+1 โซลูชันนี้ใช้งานได้ดีสำหรับฉัน! การเพิ่มที่ยอดเยี่ยมอย่างหนึ่งคือคุณสามารถเข้าถึงparentเพื่อคว้าสำเนาของ jQuery และใช้parent.$(document).ready(function(){ parent.IFrameLoaded( ); });เพื่อเริ่มต้น iframe
David Murdoch

9

ตามความคิดของ DrJokepu และ David Murdoch ฉันได้ติดตั้งเวอร์ชันที่สมบูรณ์มากขึ้น มันต้องมี jQuery ทั้งบนพาเรนต์และ iframe และ iframe เพื่อให้คุณสามารถควบคุมได้

รหัส 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--

4

พบวิธีแก้ไขปัญหา

เมื่อคุณคลิกที่ลิงค์ thickbox ที่เปิด iframe มันจะแทรก iframe ด้วย id ของ TB_iframeContent

แทนที่จะอาศัย$(document).readyเหตุการณ์ในรหัส iframe ฉันต้องผูกเข้ากับเหตุการณ์โหลดของ iframe ในเอกสารหลัก:

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

รหัสนี้อยู่ใน iframe แต่ผูกกับเหตุการณ์ของการควบคุมในเอกสารหลัก มันทำงานได้ใน FireFox และ IE


9
พบวิธีแก้ปัญหา? ดูเหมือนว่า Pier จะโพสต์ไปแล้ว ไม่ว่าคุณจะพบด้วยตัวคุณเองหรือไม่มารยาทก็คือการยอมรับคำตอบของเขาจึงให้รางวัลเวลาที่เขาใช้ตอบคุณ
Sky Sanders

ความแตกต่างระหว่างโซลูชันของ Pier และสิ่งนี้ (และสิ่งที่ฉันขาดหายไปในรหัสของฉัน) คือบริบทtop.documentที่อนุญาตให้ฉันมีรหัสภายใน iframe สามารถบอกได้เมื่อ iframe โหลดแล้ว (แม้ว่าจะloadเลิกใช้แล้วตั้งแต่คำตอบนี้และควรถูกแทนที่ด้วยon("load"))
Teepeemm

2

โดยทั่วไปสิ่งที่ผู้อื่นโพสต์แล้ว แต่ IMHO เป็นบิตที่สะอาดกว่า:

$('<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 เช่นเหตุการณ์ที่พร้อมจะมีประโยชน์มากกว่า
Tom

1

ฉันกำลังโหลด PDF ด้วย jQuery ajax ลงในแคชเบราว์เซอร์ จากนั้นฉันก็สร้างองค์ประกอบฝังตัวที่มีข้อมูลอยู่ในแคชเบราว์เซอร์แล้ว ฉันเดาว่ามันจะทำงานกับ 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

1

นี่เป็นปัญหาที่แน่นอนที่ฉันพบกับลูกค้าของเรา ฉันสร้าง jquery plugin เล็ก ๆ น้อย ๆ ที่ดูเหมือนว่าจะทำงานเพื่อความพร้อม iframe มันใช้การสำรวจความคิดเห็นเพื่อตรวจสอบเอกสาร iframe readyState รวมกับ url ของเอกสารภายในรวมกับแหล่ง iframe เพื่อให้แน่ใจว่า iframe เป็นจริง "พร้อม"

ปัญหาเกี่ยวกับ "onload" คือคุณต้องเข้าใช้ iframe จริงที่เพิ่มเข้ามาใน DOM หากคุณไม่ทำแล้วคุณต้องพยายามโหลด iframe ซึ่งถ้ามันถูกแคชคุณอาจไม่ได้ สิ่งที่ฉันต้องการคือสคริปต์ที่สามารถเรียกได้ทุกเวลาและตัดสินว่า iframe นั้น "พร้อม" หรือไม่

นี่คือคำถาม:

จอกศักดิ์สิทธิ์สำหรับการพิจารณาว่า iframe ในท้องที่นั้นโหลดแล้วหรือยัง

และนี่คือ jsfiddle ที่ฉันสร้างขึ้นมาในที่สุด

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

ใน jsfiddle ด้านบนฉันรอ onload เพื่อผนวก iframe ต่อ dom จากนั้นตรวจสอบสถานะพร้อมของเอกสารภายในของ iframe - ซึ่งควรจะข้ามโดเมนเพราะชี้ไปที่วิกิพีเดีย - แต่ Chrome ดูเหมือนจะรายงานว่า "สมบูรณ์" วิธีการที่ยุ่งยากของปลั๊กอินนั้นจะถูกเรียกใช้เมื่อ 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

ฟังก์ชั่นจากคำตอบนี้เป็นวิธีที่ดีที่สุดในการจัดการกับสิ่งนี้เพราะ$.readyล้มเหลวใน iframes อย่างชัดเจน นี่คือการตัดสินใจที่จะไม่สนับสนุนสิ่งนี้

loadเหตุการณ์ยังไม่ไฟหาก iframe ได้โหลดแล้ว น่าผิดหวังมากที่เรื่องนี้ยังคงเป็นปัญหาในปี 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.