jQuery .ready в динамично вмъкнат iframe

Ние използваме jQuery thickbox за динамично показване на iframe, когато някой кликне върху снимка. В тази iframe използваме galleria библиотека на JavaScript за показване на множество снимки.

Проблемът изглежда е, че $(document).ready в iframe изглежда се задейства твърде рано и съдържанието на iframe дори още не е заредено, така че кодът на galleria не се прилага правилно върху DOM елементите. $(document).ready изглежда използва състоянието на родителска готовност на iframe, за да реши дали iframe е готов.

Ако извлечем функцията, извикана от document ready, в отделна функция и я извикаме след изчакване от 100 ms. Работи, но не можем да рискуваме в производството с бавен компютър.

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

Моят въпрос: към кое събитие на jQuery трябва да се свържем, за да можем да изпълним нашия код, когато динамичният iframe е готов, а не просто е родител?


person EtienneT    schedule 15.10.2008    source източник
comment
И вие потвърждавате, че galleria работи, когато я заредите директно, вместо през iframe, нали?   -  person Jason Kealey    schedule 15.10.2008
comment
Да, galleria работи перфектно, когато я използваме директно в нормална страница.   -  person EtienneT    schedule 15.10.2008
comment
възможен дубликат на обратно извикване на Javascript, когато IFRAME приключи със зареждането?   -  person Robert MacLean    schedule 28.05.2013


Отговори (10)


Отговорих на подобен въпрос (вижте Обратно извикване на Javascript, когато IFRAME приключи със зареждането?). Можете да получите контрол над събитието за зареждане на iframe със следния код:

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

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

При работа с iframes намерих достатъчно добър, за да използвам събитие за зареждане вместо събитие за готовност на документ.

person Pier Luigi    schedule 15.10.2008
comment
Не трябва ли да зададете събитието за зареждане преди извикване на attr('src')? - person Shay Erlichmen; 25.12.2010
comment
Не, няма значение. Събитието Load няма да се задейства най-малко до следващия цикъл на събитието. - person Barum Rho; 24.06.2011
comment
събитието за зареждане няма да работи за вградени рамки, които се използват за изтегляне. като ‹iframe src=my.pdf/› - person Mike Starov; 03.11.2011
comment
Динамично вмъкнато, защото маркерът на iframe е вмъкнат в DOM от javascript на клиента. Така че iframe може да съществува или не на страницата, въз основа на условие или събитие, което се случва на клиента. - person Pier Luigi; 12.12.2011
comment
Проблемът със зареждането е, че се задейства, когато всички изображения и подрамки са заредени. Готово събитие като jQuery би било по-полезно. - person Tom; 14.12.2011
comment
Събитието за зареждане няма да се задейства за iFrames, които вече са се заредили до момента, в който манипулаторът за зареждане е обвързан. Като такъв той не е сравним с jQuery(document).ready() за iFrames. Единственото решение, за което се сещам, е да копирам как jQuery го прави за рамката, в която jQuery е зареден. - person Woodgnome; 17.01.2012
comment
Това не работи по някаква причина, така че се опитах да заменя първите два реда с: myFrame = $('<iframe src="' + src + '" id="myFrame"></iframe>').appendTo('body'); и всичко работи добре. - person Skunkwaffle; 30.04.2012
comment
@PierLuigi Бихте ли обяснили променливата callback? Какво трябва да бъде? Наистина не разбирам - person Alex; 10.05.2012
comment
callback е всяка javascript функция, която искате да бъде извикана, след като iframe се зареди. Това може да бъде функция, локална за прозореца на повикващия или дори функция, дефинирана в прозореца на iframe. Можете да получите достъп до прозореца на iframe с window['myId'] на повикващия. - person Pier Luigi; 11.05.2012
comment
Имайте предвид, че има две различни .load()-функции. api.jquery.com/load за зареждане на съдържание в обект и api.jquery.com/load-event за обвързване на събитието onLoad. - person Zut; 18.12.2012
comment
Какво ще стане, ако искам да извикам обратно извикване, когато DOM на iframe е зареден? $.load се задейства само когато всичко (много изображения) е заредено. Това, от което се нуждая, е събитието DOM ready на iframe. Може ли да се направи без извикване на js през iframes? - person NeoWang; 24.03.2014
comment
Има ли налично решение за улавяне на събитието load за pdf файлове в iframe? - person Zain Shaikh; 14.04.2014

Използвайки 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 страници. Целият код се намира и изпълнява от родителския кадър/прозорец.

person Community    schedule 09.04.2009
comment
Каква е функцията movePreview, посочена в setTimeout()? - person cam8001; 26.11.2009
comment
@cam8001: Беше правописна грешка - вече е коригирана. - person Már Örlygsson; 28.03.2011
comment
В крайна сметка използвах и решение за анкета. Други решения изглежда работят само с частичен успех. За мен обаче трябваше да проверя за съществуването на функция на javascript, а не само за съдържанието на iframe, преди да постигна успех. напр. (typeof iframe.contentWindow.myfunc == 'функция') - person Julian; 18.06.2013
comment
Това решение е рекурсивно, което заема памет за всяко повикване. Ако iframe никога не се зарежда, тогава ще се обажда все по-дълбоко и по-дълбоко завинаги, докато паметта не свърши. Вместо това бих препоръчал setInterval за гласуване. - person eremzeit; 03.12.2015

В IFrames обикновено решавам този проблем, като поставям малък скрипт в самия край на блока:

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

Това работи през повечето време за мен. Понякога най-простото и наивно решение е най-подходящото.

person Tamas Czinege    schedule 15.10.2008
comment
+1 Това решение работи чудесно за мен! Едно чудесно допълнение е, че можете да стигнете до parent, за да вземете копие на jQuery и да използвате parent.$(document).ready(function(){ parent.IFrameLoaded( ); });, за да инициализирате iframe. - person David Murdoch; 21.01.2011

Следвайки идеята на 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);
    });
});
person Ricardo Freitas    schedule 30.04.2013
comment
по някаква причина използването на $(iframe).ready(function...) в родителя няма да работи за мен. Изглеждаше, че функцията за обратно извикване се изпълнява преди iframe dom да е готов. Използването на този метод работи страхотно! - person w--; 23.03.2014

Намерих решението на проблема.

Когато щракнете върху дебела връзка, която отваря iframe, тя вмъква iframe с идентификатор TB_iframeContent.

Вместо да разчитам на събитието $(document).ready в кода на iframe, просто трябва да се свържа със събитието за зареждане на iframe в основния документ:

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

Този код е в iframe, но се свързва със събитие на контрола в родителския документ. Работи във FireFox и IE.

person EtienneT    schedule 15.10.2008
comment
Намерихте ли решението? Изглежда Пиър вече го е публикувал. Независимо дали сте го намерили сами или не, етикетът би бил да приемете отговора му, като по този начин възнаграждавате времето, прекарано да ви отговаря. - person Sky Sanders; 04.04.2010
comment
Разликата между решението на Pier и това (и това, което ми липсваше в моя код) е контекстът top.document, който ми позволява да имам код вътре във вградената рамка, за да мога да разбера кога вградената рамка е заредена. (Въпреки че load е остарял след този отговор и трябва да бъде заменен с on("load").) - person Teepeemm; 04.10.2014

По принцип това, което другите вече са публикували, но IMHO малко по-чисто:

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

Тази функция от този отговор е най-добрият начин да се справите с това, тъй като $.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();
}

}

person Crashalot    schedule 23.05.2020

Опитайте тази,

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

Всичко, което трябва да направите тогава, е да създадете JavaScript функцията testframe_loaded().

person Danny G    schedule 12.10.2009
comment
Проблемът със зареждането е, че се задейства, когато всички изображения и подрамки са заредени. Готово събитие като jQuery би било по-полезно. - person Tom; 14.12.2011

Зареждам 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";
person Pavel Savara    schedule 16.08.2012
comment
Имайте предвид, че не работи добре на мобилни браузъри, където кешът може да е по-малък от размера на PDF. - person Pavel Savara; 15.09.2014

Това беше точният проблем, с който се сблъсках с нашия клиент. Създадох малък плъгин jquery, който изглежда работи за готовност за iframe. Той използва запитване, за да провери готовото състояние на iframe документа, комбинирано с URL адреса на вътрешния документ, комбиниран с източника на iframe, за да се увери, че iframe всъщност е „готов“.

Проблемът с „onload“ е, че имате нужда от достъп до действителния iframe, добавен към DOM, ако нямате, тогава трябва да опитате да хванете зареждането на iframe, което, ако е кеширано, може да не го направите. Това, от което се нуждаех, беше скрипт, който можеше да бъде извикан по всяко време и да определи дали iframe е „готов“ или не.

Ето го въпроса:

Свещеният граал за определяне дали локалната iframe има или не заредено

и ето jsfiddle, който в крайна сметка измислих.

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

В jsfiddle по-горе чакам onload, за да добавя iframe към dom, след което проверявам състоянието на готовност на вътрешния документ на iframe - което трябва да е кръстосано домейн, защото е насочено към wikipedia - но 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>
person Jon Freynik    schedule 10.02.2016
comment
Работи добре за мен - person Hassan Tareq; 09.09.2019