동적으로 삽입 된 iframe의 jQuery .ready


184

우리는 누군가가 그림을 클릭 할 때 jQuery thickbox 를 사용하여 iframe을 동적으로 표시합니다. 이 iframe에서는 여러 그림을 표시하기 위해 갤러리아 를 자바 스크립트 라이브러리로 사용 하고 있습니다.

문제 $(document).ready는 iframe에서 너무 빨리 실행되고 iframe 내용이 아직로드되지 않아서 갤러리 요소가 DOM 요소에 제대로 적용되지 않는 것 같습니다. $(document).readyiframe이 준비되었는지 여부를 결정하기 위해 iframe 상위 준비 상태를 사용하는 것 같습니다.

별도의 함수에서 준비된 문서에 의해 호출 된 함수를 추출하고 100ms 시간 초과 후 호출하십시오. 작동하지만 컴퓨터 속도가 느린 프로덕션에서는 기회를 잡을 수 없습니다.

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

내 질문 : 동적 iframe이 준비되어 있고 부모 일뿐 만 아니라 코드를 실행할 수 있도록 바인딩해야 할 jQuery 이벤트는 무엇입니까?


1
그리고 iframe을 통하지 않고 직접로드 할 때 갤러리아가 작동하는지 확인하십시오. 맞습니까?
Jason Kealey

예, 갤러리아는 일반 페이지에서 직접 사용할 때 완벽하게 작동합니다.
EtienneT

답변:


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')를 호출하기 전에로드 이벤트를 설정해서는 안됩니까?
Shay Erlichmen

15
아닙니다. 상관 없습니다. 다음 이벤트 루프가 최소가 될 때까지로드 이벤트가 시작되지 않습니다.
Barum Rho

29
다운로드에 사용되는 iframe에서는로드 이벤트가 작동하지 않습니다. <iframe src = "my.pdf"/>
Mike Starov 18

5
로드 문제는 모든 이미지와 서브 프레임이로드 될 때 발생한다는 것입니다. ready 이벤트와 같은 jQuery가 더 유용합니다.
Tom


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

@ 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의 생각에 따라 더 완전한 버전을 구현했습니다. 그것은 필요 컨트롤에있을 부모 및 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--

4

문제에 대한 해결책을 찾았습니다.

iframe을 여는 thickbox 링크를 클릭하면 ID가 TB_iframeContent 인 iframe이 삽입됩니다.

$(document).readyiframe 코드 의 이벤트에 의존하는 대신 부모 문서에서 iframe의로드 이벤트에 바인딩하면됩니다.

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

이 코드는 iframe에 있지만 부모 문서의 컨트롤 이벤트에 바인딩됩니다. FireFox 및 IE에서 작동합니다.


9
해결책을 찾았습니까? 부두가 이미 게시 한 것 같습니다. 당신이 그것을 스스로 찾든 말든, 에티켓은 그의 대답을 받아 들여서 그가 당신에게 대답하는 시간을 보상하는 것입니다.
Sky Sanders

Pier의 솔루션과 이것 (및 코드에서 누락 된 것)의 차이점 은 iframe 내부top.document코드 가 iframe 이로 드 된 시점을 알 수 있게 하는 컨텍스트 입니다 . (하지만은 이 대답 이후 사용되지 않습니다, 그리고 교체해야합니다 .)loadon("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
로드 문제는 모든 이미지와 서브 프레임이로드 될 때 발생한다는 것입니다. ready 이벤트와 같은 jQuery가 더 유용합니다.
Tom

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

1

이것이 내가 고객과 마주 친 정확한 문제였습니다. iframe 준비를 위해 작동하는 것 같은 작은 jquery 플러그인을 만들었습니다. 폴링을 사용하여 iframe 소스와 결합 된 내부 문서 URL과 결합 된 iframe 문서 readyState를 확인하여 iframe이 실제로 "준비"되어 있는지 확인합니다.

"onload"의 문제는 DOM에 추가되는 실제 iframe에 액세스해야한다는 것입니다. 그렇지 않으면 캐시 된 경우 iframe 로딩을 잡아야합니다. 내가 필요한 것은 언제든지 호출 할 수있는 스크립트였으며 iframe이 "준비"되었는지 여부를 결정했습니다.

질문은 다음과 같습니다.

로컬 iframe의로드 여부를 결정하는 성배

그리고 여기에 결국 jsfiddle이 있습니다.

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

위의 jsfiddle에서 onload가 dom에 iframe을 추가 할 때까지 기다리고 있습니다 .iframe의 내부 문서의 준비 상태를 확인하십시오-위키 백과를 가리 키기 때문에 교차 도메인이어야합니다. 그러나 Chrome은 "완료"라고보고합니다. 그런 다음 iframe이 실제로 준비되면 플러그인의 iready 메소드가 호출됩니다. 콜백은 내부 문서의 준비 상태를 다시 확인하려고 시도합니다. 이번에는 도메인 간 요청을보고합니다 (올바른).

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