jQuery. já em um iframe inserido dinamicamente

184

Estamos usando o thickbox jQuery para exibir dinamicamente um iframe quando alguém clica em uma imagem. Nesse iframe, estamos usando a galleria uma biblioteca javascript para exibir várias imagens.

O problema parece ser que $(document).readyno iframe parece ser acionado muito cedo e o conteúdo do iframe ainda nem foi carregado, portanto, o código da galeria não é aplicado corretamente nos elementos DOM. $(document).readyparece usar o estado de pai pronto para iframe para decidir se o iframe está pronto.

Se extrairmos a função chamada pelo documento ready em uma função separada e a chamarmos após um tempo limite de 100 ms. Funciona, mas não podemos arriscar na produção com um computador lento.

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

Minha pergunta: qual evento jQuery devemos associar para poder executar nosso código quando o iframe dinâmico estiver pronto e não apenas um pai?

EtienneT
fonte
1
E você confirma que a galeria funciona quando você a carrega diretamente, e não através de um iframe, correto?
Jason Kealey
Sim, a galleria funciona perfeitamente quando a usamos diretamente em uma página normal.
EtienneT 15/10/08
possível duplicação de retorno
Robert MacLean

Respostas:

291

Respondi a uma pergunta semelhante (consulte o retorno de chamada do Javascript quando o IFRAME terminar de carregar? ). Você pode obter controle sobre o evento de carregamento iframe com o seguinte código:

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

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

Ao lidar com iframes, achei bom o suficiente para usar o evento load em vez do evento ready do documento.

Pier Luigi
fonte
17
Você não deve definir o evento load antes de chamar o attr ('src')?
Shay Erlichmen
15
Não, isso não importa. O evento Load não será acionado até o próximo loop de evento no mínimo.
Barum Rho
29
o evento load não funcionará para iframes usados ​​para download. like <iframe src = "my.pdf" />
Mike Starov
5
O problema com o carregamento é que ele dispara quando todas as imagens e sub-quadros são carregados. Um evento jQuery como ready seria mais útil.
Tom
30

Usando o jQuery 1.3.2, o seguinte funcionou para mim:

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

REVISÃO:! Na verdade, o código acima às vezes parece que funciona no Firefox, nunca parece que funciona no Opera.

Em vez disso, implementei uma solução de pesquisa para meus propósitos. Simplificado, fica assim:

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

Isso não requer código nas páginas iframe chamadas. Todo o código reside e é executado no quadro / janela pai.

Már Örlygsson
fonte
Qual é a função movePreview referida em setTimeout ()?
Cam8001
@ cam8001: Foi um erro de digitação - agora foi corrigido.
Marár Örlygsson 28/03
Acabei usando uma solução de pesquisa também. Outras soluções pareciam funcionar apenas com sucesso parcial. No entanto, para mim, eu precisava verificar a existência de uma função javascript, e não apenas o conteúdo do iframe, antes de ter sucesso. por exemplo. (typeof iframe.contentWindow.myfunc == 'function')
Julian
2
Esta solução é recursiva, que ocupa memória para cada chamada. Se o iframe nunca carregar, será mais e mais profundo para sempre até que a memória se esgote. Eu recomendo setIntervala pesquisa.
eremzeit
15

Nos IFrames, normalmente resolvo esse problema colocando um pequeno script no final do bloco:

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

Este trabalho na maioria das vezes para mim. Às vezes, a solução mais simples e ingênua é a mais apropriada.

Tamas Czinege
fonte
4
+1 Esta solução funciona muito bem para mim! Uma ótima adição é que você pode acessar parentuma cópia do jQuery e usar parent.$(document).ready(function(){ parent.IFrameLoaded( ); });para inicializar o iframe.
David Murdoch
9

Seguindo a ideia de DrJokepu e David Murdoch, implementei uma versão mais completa. Ele exige jQuery sobre o pai eo iframe eo iframe para a sua controle.

código 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");
        });
    });
}

código de teste dos pais:

$(function(){
    $("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
        console.log(e.type);
    });
});
Ricardo Freitas
fonte
por qualquer motivo, usar $ (iframe) .ready (função ...) no pai não funcionaria para mim. Parecia que a função de retorno de chamada estava sendo executada antes que o iframe dom estivesse pronto. Usando este método funcionou muito bem!
w--
4

Encontrou a solução para o problema.

Quando você clica em um link de caixa grossa que abre um iframe, ele insere um iframe com um ID de TB_iframeContent.

Em vez de confiar no $(document).readyevento no código iframe, só preciso vincular ao evento load do iframe no documento pai:

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

Este código está no iframe, mas vincula a um evento de um controle no documento pai. Funciona no FireFox e no IE.

EtienneT
fonte
9
Encontrou a solução? Parece que Pier já havia postado. Se você a encontrou por conta própria ou não, a etiqueta seria aceitar a resposta dele, recompensando assim o tempo que ele gastou em responder a você.
Sky Sanders
A diferença entre a solução de Pier e essa (e o que estava faltando no meu código) é o contexto top.documentque me permite ter código dentro do iframe para saber quando o iframe foi carregado. (Embora loadtenha sido preterido desde esta resposta, e deve ser substituído on("load").)
Teepeemm
2

Basicamente, o que outros já postaram, mas IMHO é um pouco mais limpo:

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

Tente isso,

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

Tudo o que você precisa fazer é criar a função JavaScript testframe_loaded ().

Danny G
fonte
1
O problema com o carregamento é que ele dispara quando todas as imagens e sub-quadros são carregados. Um evento jQuery como ready seria mais útil.
Tom
1

Estou carregando o PDF com o jQuery ajax no cache do navegador. Em seguida, crio um elemento incorporado com os dados já no cache do navegador. Eu acho que também funcionará com 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();
    }
});

Você também deve definir corretamente os cabeçalhos http.


HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";
Pavel Savara
fonte
1
Observe que ele não funciona bem em navegadores móveis nos quais o cache pode ser menor que o tamanho do PDF.
Pavel Savara
1

Esse foi exatamente o problema que encontrei com nosso cliente. Eu criei um pequeno plugin jquery que parece funcionar para a disponibilidade de iframe. Ele usa polling para verificar o documento iframe readyState combinado com o URL interno do documento combinado com a fonte iframe para garantir que o iframe esteja realmente "pronto".

O problema com "onload" é ​​que você precisa acessar o iframe real que está sendo adicionado ao DOM; caso contrário, você deve tentar capturar o carregamento do iframe que, se estiver armazenado em cache, poderá não ser. O que eu precisava era de um script que pudesse ser chamado a qualquer momento e determinasse se o iframe estava ou não "pronto" ou não.

Aqui está a pergunta:

Santo Graal para determinar se o iframe local foi carregado ou não

e aqui está o problema que eu acabei inventando.

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

No jsfiddle acima, estou aguardando o onload anexar um iframe ao dom e, em seguida, verificando o estado pronto do documento interno do iframe - que deve ser de domínio cruzado porque está apontado para a wikipedia - mas o Chrome parece reportar "completo". O método incorreto do plug-in é chamado quando o iframe está pronto. O retorno de chamada tenta verificar o estado de prontidão do documento interno novamente - desta vez relatando uma solicitação entre domínios (que está correta) - de qualquer maneira parece funcionar para o que eu preciso e espero que ajude outras pessoas.

<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
fonte
Funcionou bem para mim
Hassan Tareq
0

Essa função desta resposta é a melhor maneira de lidar com isso, pois $.readyfalha explicitamente em iframes. Aqui está a decisão de não apoiar isso.

O loadevento também não dispara se o iframe já tiver sido carregado. Muito frustrante que isso continue sendo um problema em 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
fonte