jQuery. đã có trong iframe được chèn động


184

Chúng tôi đang sử dụng jQuery thickbox để hiển thị động iframe khi ai đó nhấp vào ảnh. Trong khung nội tuyến này, chúng tôi đang sử dụng galleria một thư viện javascript để hiển thị nhiều hình ảnh.

Vấn đề dường như là $(document).readytrong iframe dường như bị loại bỏ quá sớm và nội dung iframe thậm chí chưa được tải, do đó mã galleria không được áp dụng đúng cho các phần tử DOM. $(document).readydường như sử dụng trạng thái sẵn sàng cha mẹ iframe để quyết định xem iframe đã sẵn sàng chưa.

Nếu chúng ta trích xuất hàm được gọi bởi tài liệu đã sẵn sàng trong một hàm riêng biệt và gọi nó sau khi hết thời gian 100 ms. Nó hoạt động, nhưng chúng ta không thể có cơ hội sản xuất với một máy tính chậm.

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

Câu hỏi của tôi: sự kiện jQuery nào chúng ta nên liên kết để có thể thực thi mã của mình khi iframe động đã sẵn sàng và không chỉ là cha mẹ?


1
Và bạn xác nhận rằng galleria hoạt động khi bạn tải nó trực tiếp thay vì thông qua iframe, đúng không?
Jason Kealey

Có, galleria hoạt động hoàn hảo khi chúng ta sử dụng nó trực tiếp trong một trang bình thường.
EtienneT

Câu trả lời:


291

Tôi đã trả lời một câu hỏi tương tự (xem phần gọi lại Javascript khi IFRAME tải xong? ). Bạn có thể có được quyền kiểm soát đối với sự kiện tải iframe với mã sau:

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

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

Khi xử lý iframe tôi thấy đủ tốt để sử dụng sự kiện tải thay vì sự kiện sẵn sàng cho tài liệu.


17
Bạn có nên đặt sự kiện tải trước khi gọi attr ('src') không?
Shay Erlichmen

15
Không, nó không sao cả. Tải sự kiện sẽ không kích hoạt cho đến khi vòng lặp sự kiện tiếp theo ở mức tối thiểu.
Barum Rho

29
sự kiện tải sẽ không hoạt động đối với iframe được sử dụng để tải xuống. như <iframe src = "my.pdf" />
Mike Starov

5
Vấn đề với tải là nó kích hoạt khi tất cả các hình ảnh và khung con đã được tải. Một sự kiện như jQuery đã sẵn sàng sẽ hữu ích hơn.
Tom


30

Sử dụng jQuery 1.3.2 như sau làm việc cho tôi:

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

CÁCH MẠNG :! Trên thực tế, đoạn mã trên đôi khi trông giống như nó hoạt động trong Firefox, không bao giờ có vẻ như nó hoạt động trong Opera.

Thay vào đó tôi thực hiện một giải pháp bỏ phiếu cho mục đích của tôi. Đơn giản hóa nó trông như thế này:

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

Điều này không yêu cầu mã trong các trang iframe được gọi. Tất cả các mã nằm và thực thi từ khung / cửa sổ cha.


Fucntion movePreview được nhắc đến trong setTimeout () là gì?
cam8001

@ cam8001: Đó là một lỗi đánh máy - hiện đã được sửa.
Már rlygsson

Tôi đã kết thúc bằng cách sử dụng một giải pháp bỏ phiếu quá. Các giải pháp khác dường như chỉ hoạt động với một phần thành công. Tuy nhiên, đối với tôi, tôi cần kiểm tra sự tồn tại của một hàm javascript, thay vì chỉ cho nội dung của iframe, trước khi tôi thành công. ví dụ. (typeof iframe.contentWindow.myfunc == 'function')
Julian

2
Giải pháp này là đệ quy chiếm bộ nhớ cho mỗi cuộc gọi. Nếu iframe không bao giờ tải, thì nó sẽ gọi sâu hơn và mãi mãi cho đến khi hết bộ nhớ. Tôi muốn giới thiệu setIntervalđể bỏ phiếu thay thế.
eremzeit

15

Trong IFrames tôi thường giải quyết vấn đề này bằng cách đặt một đoạn script nhỏ đến cuối khối:

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

Công việc này hầu hết thời gian cho tôi. Đôi khi giải pháp đơn giản và ngây thơ nhất là thích hợp nhất.


4
+1 Giải pháp này hiệu quả với tôi! Một bổ sung tuyệt vời là bạn có thể tiếp cận với parentđể lấy một bản sao của jQuery và sử dụng parent.$(document).ready(function(){ parent.IFrameLoaded( ); });để khởi tạo iframe.
David Murdoch

9

Theo ý tưởng của DrJokepu và David Murdoch, tôi đã triển khai một phiên bản hoàn chỉnh hơn. Nó yêu cầu jQuery trên cả cha mẹ và iframe và iframe nằm trong tầm kiểm soát của bạn.

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

mã kiểm tra phụ huynh:

$(function(){
    $("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
        console.log(e.type);
    });
});

vì bất kỳ lý do gì sử dụng $ (iframe). yet (hàm ...) trong cha mẹ sẽ không hoạt động đối với tôi. Có vẻ như chức năng gọi lại đã được thực thi trước khi dom iframe sẵn sàng. Sử dụng phương pháp này làm việc tuyệt vời!
w--

4

Tìm thấy giải pháp cho vấn đề.

Khi bạn nhấp vào liên kết hộp dày mở iframe, nó sẽ chèn iframe có id là TB_iframeContent.

Thay vì dựa vào $(document).readysự kiện trong mã iframe, tôi chỉ phải liên kết với sự kiện tải của iframe trong tài liệu gốc:

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

Mã này nằm trong iframe nhưng liên kết với một sự kiện của điều khiển trong tài liệu gốc. Nó hoạt động trong FireFox và IE.


9
Tìm được giải pháp? Có vẻ như Pier đã đăng nó. Cho dù bạn có tự mình tìm thấy nó hay không, nghi thức sẽ là chấp nhận câu trả lời của anh ấy, do đó thưởng cho thời gian anh ấy dành để trả lời bạn.
Sky Sanders

Sự khác biệt giữa giải pháp của Pier và điều này (và những gì tôi bị thiếu trong mã của mình) là bối cảnh top.documentcho phép tôi có mã bên trong iframe có thể biết khi nào iframe được tải. (Mặc dù loadđã không được chấp nhận kể từ câu trả lời này và nên được thay thế bằng on("load").)
Teepeemm

2

Về cơ bản những gì người khác đã đăng nhưng IMHO sạch hơn một chút:

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

1

Thử cái này,

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

Tất cả những gì bạn cần làm sau đó là tạo hàm JavaScript testframe_loaded ().


1
Vấn đề với tải là nó kích hoạt khi tất cả các hình ảnh và khung con đã được tải. Một sự kiện như jQuery đã sẵn sàng sẽ hữu ích hơn.
Tom

1

Tôi đang tải PDF với jQuery ajax vào bộ đệm của trình duyệt. Sau đó, tôi tạo phần tử nhúng với dữ liệu đã có trong bộ đệm của trình duyệt. Tôi đoán nó cũng sẽ hoạt động với 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();
    }
});

Bạn phải đặt tiêu đề http chính xác.


HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";

1
Lưu ý rằng nó không hoạt động tốt trên các trình duyệt di động nơi bộ đệm có thể nhỏ hơn kích thước của PDF.
Pavel Savara

1

Đây là vấn đề chính xác tôi gặp phải với khách hàng của chúng tôi. Tôi đã tạo một plugin jquery nhỏ dường như hoạt động để sẵn sàng cho iframe. Nó sử dụng bỏ phiếu để kiểm tra tài liệu iframe readyState kết hợp với url tài liệu bên trong kết hợp với nguồn iframe để đảm bảo iframe thực sự "sẵn sàng".

Vấn đề với "onload" là bạn cần quyền truy cập vào iframe thực tế được thêm vào DOM, nếu bạn không cần phải thử tải iframe mà nếu nó được lưu vào bộ đệm thì bạn có thể không. Điều tôi cần là một tập lệnh có thể được gọi bất cứ lúc nào và xác định xem iframe có "sẵn sàng" hay không.

Đây là câu hỏi:

Chén thánh để xác định liệu iframe địa phương có được tải hay không

và đây là jsfiddle cuối cùng tôi đã nghĩ ra.

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

Trong jsfiddle ở trên, tôi đang chờ onload để nối iframe vào trạng thái dom, sau đó kiểm tra trạng thái sẵn sàng của tài liệu bên trong iframe - phải là tên miền chéo vì nó được trỏ đến wikipedia - nhưng Chrome dường như báo cáo "hoàn tất". Phương thức i sẵn của trình cắm sau đó được gọi khi iframe thực sự sẵn sàng. Cuộc gọi lại cố gắng kiểm tra lại trạng thái sẵn sàng của tài liệu bên trong - lần này báo cáo một yêu cầu tên miền chéo (chính xác) - dù sao thì nó dường như hoạt động cho những gì tôi cần và hy vọng nó sẽ giúp người khác.

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

Làm việc tốt cho tôi
Hassan Tareq

0

Hàm này từ câu trả lời này là cách tốt nhất để xử lý việc này vì $.readythất bại rõ ràng đối với iframe. Đây là quyết định không hỗ trợ này.

Sự loadkiện cũng không kích hoạt nếu iframe đã được tải. Rất bực bội vì đây vẫn là một vấn đề trong năm 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();
}

}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.