Detecção de eventos de alteração do iFrame src?

181

Supondo que não tenho controle sobre o conteúdo do iframe, existe alguma maneira de detectar uma alteração de src na página pai? Algum tipo de carga, talvez?

Meu último recurso é fazer um teste de intervalo de 1 segundo se o iframe src for o mesmo de antes, mas fazer essa solução hacky seria péssimo.

Estou usando a biblioteca jQuery, se ajudar.

Matrym
fonte
3
A srcpropriedade mudará quando um link no iframe for clicado? Não tenho certeza disso - se eu tivesse que adivinhar, se diria "não". Não são maneiras de propriedades do monitor (no Firefox, pelo menos, AFAIK), mas eu não tenho certeza se ele vai ser de alguma utilidade neste caso.
Pekka
Estou tentando implementar algo assim e posso confirmar que o srcatributo no DOM não muda nas versões mais recentes do FireFox ou Safari. @ A resposta de Michiel abaixo é tão boa quanto pude chegar até agora.
Kaelin Colclasure

Respostas:

201

Você pode usar o onLoadevento, como no exemplo a seguir:

<iframe src="http://www.google.com/" onLoad="alert('Test');"></iframe>

O alerta será exibido sempre que o local dentro do iframe for alterado. Ele funciona em todos os navegadores modernos, mas pode não funcionar em alguns navegadores mais antigos, como o IE5 e o Opera. ( Fonte )

Se o iframe estiver mostrando uma página no mesmo domínio do pai , você poderá acessar o local com contentWindow.location, como no exemplo a seguir:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>
Daniel Vassallo
fonte
13
Você quer dizer this.contentWindow.location.href
Nikolai
@Daniel Vassallo assumindo que você adota essa abordagem quando deseja lidar com problemas de segurança, alguém não pode simplesmente substituir o evento onLoad e alterar o conteúdo do iframe?
Noam Shaish
15
Quando eu this.contentWindow.location.hrefreceboPermission denied to access property 'href'
Mazatec
4
this.contentWindow.location.href não funcionará porque você está fazendo uma solicitação de origem cruzada. :( Todos os navegadores restringem isso agora.
Naveen
2
Correção: this.contentWindow.locationestá disponível para outros domínios. Even this.contentWindow.location.hrefestá disponível para outros domínios, mas apenas para escrita. No entanto, a leitura this.contentWindow.location.hrefé limitada ao mesmo domínio.
Wadim 4/03/15
57

Resposta baseada em JQuery <3

$('#iframeid').load(function(){
    alert('frame has (re)loaded');
});

Conforme mencionado pelo subharb, a partir do JQuery 3.0, isso precisa ser alterado para:

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});

https://jquery.com/upgrade-guide/3.0/#breaking-change-load-unload-and-error-removed

Michiel
fonte
2
Não funciona exatamente como planejado. Ele dispara o alerta no carregamento da página inicial. Dependendo de quais são seus planos para esse código (para mim, estou animando a caixa de diálogo na página de envio de formulário), esta solução não funcionará.
30515 stacigh
@ stracigh, está correto, o evento será acionado toda vez que o quadro for carregado, assim como a primeira vez no carregamento da página inicial. Se você não deseja que seu código seja acionado na primeira vez, você deve detectar de alguma forma que esta é sua primeira carga e retorno de quadro.
31515 Michiel
@ FooBar, Sim, ele funciona em vários domínios, na verdade é para isso que eu o usei. Mas você não pode acessar a página no iFrame com javascript quando essa página estiver em outro domínio.
317 Michiel
isso funciona e detectar quando o conteúdo é carregado, eu preciso de uma maneira de detectar quando o src está mudando. Algo como beforeLoad (para ativar um carregador)
Andrea_86
Conforme indicado por @stacigh, o evento será disparado uma vez no carregamento da página pai. Se você declarar um contador fora dessa função e lê-lo dentro, poderá detectar as alterações depois disso. Você iria incrementar manipulador balcão dentro ONLOAD além de ter if (counter > 1) { // do something on iframe actual change }(contra assumindo foi definido como 0 no início)
Alex
38

Se você não tem controle sobre a página e deseja observar algum tipo de alteração, o método moderno é usar MutationObserver

Um exemplo de seu uso, observando o srcatributo mudar de umiframe

new MutationObserver(function(mutations) {
  mutations.some(function(mutation) {
    if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
      console.log(mutation);
      console.log('Old src: ', mutation.oldValue);
      console.log('New src: ', mutation.target.src);
      return true;
    }

    return false;
  });
}).observe(document.body, {
  attributes: true,
  attributeFilter: ['src'],
  attributeOldValue: true,
  characterData: false,
  characterDataOldValue: false,
  childList: false,
  subtree: true
});

setTimeout(function() {
  document.getElementsByTagName('iframe')[0].src = 'http://jsfiddle.net/';
}, 3000);
<iframe src="http://www.google.com"></iframe>

Saída após 3 segundos

MutationRecord {oldValue: "http://www.google.com", attributeNamespace: null, attributeName: "src", nextSibling: null, previousSibling: null…}
Old src:  http://www.google.com
New src:  http://jsfiddle.net/ 

On jsFiddle

A resposta postada aqui, pois a pergunta original foi encerrada como duplicata desta.

Xotic750
fonte
E se eu estiver usando Web Components e um dos meus componentes personalizados tiver um atributo src? Eu acho que isso iria pegá-lo acidentalmente.
Lucas
Então você altera as opções observe. Este é apenas um exemplo.
Xotic750
O @PaulRoub removeu o link jsfiddle (deve ter sido um erro de copiar / colar) e incluiu o código como um snippet.
Xotic750
4
Mas, no meu caso, o frame.contentDocument.URL muda (dos links dentro do quadro) e não o src. Posso assistir isso?
httpete 22/09/16
Não tenho certeza, é melhor escrever uma pergunta, eu acho.
Xotic750 22/09/16
11

Nota: O snippet funcionaria apenas se o iframe estiver com a mesma origem.

Outras respostas propuseram o loadevento, mas ele é acionado após o carregamento da nova página no iframe. Pode ser necessário ser notificado imediatamente após a alteração do URL , não após o carregamento da nova página.

Aqui está uma solução JavaScript simples:

function iframeURLChange(iframe, callback) {
    var unloadHandler = function () {
        // Timeout needed because the URL changes immediately after
        // the `unload` event is dispatched.
        setTimeout(function () {
            callback(iframe.contentWindow.location.href);
        }, 0);
    };

    function attachUnload() {
        // Remove the unloadHandler in case it was already attached.
        // Otherwise, the change will be dispatched twice.
        iframe.contentWindow.removeEventListener("unload", unloadHandler);
        iframe.contentWindow.addEventListener("unload", unloadHandler);
    }

    iframe.addEventListener("load", attachUnload);
    attachUnload();
}

iframeURLChange(document.getElementById("mainframe"), function (newURL) {
    console.log("URL changed:", newURL);
});
<iframe id="mainframe" src=""></iframe>

Isso rastreará com êxito as srcalterações de atributo, bem como quaisquer alterações de URL feitas no próprio iframe.

Testado em todos os navegadores modernos.

Eu fiz uma essência com esse código também. Você pode verificar minha outra resposta também. Ele se aprofundou um pouco na maneira como isso funciona.

dodov
fonte
6

O iframe sempre mantém a página pai, você deve usá-lo para detectar em qual página você está no iframe:

Código HTML:

<iframe id="iframe" frameborder="0" scrolling="no" onload="resizeIframe(this)" width="100%" src="www.google.com"></iframe>

Js:

    function resizeIframe(obj) {
        alert(obj.contentWindow.location.pathname);
    }
Tarik FAMIL
fonte
2

Desde a versão 3.0 do Jquery, você pode receber um erro

TypeError: url.indexOf não é uma função

O que pode ser facilmente corrigido fazendo

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});
subharb
fonte
1

Aqui está o método usado nos módulos Commerce SagePay e Commerce Paypoint Drupal, que basicamente se comparam document.location.hrefao valor antigo, carregando primeiro seu próprio iframe, depois o externo.

Então, basicamente, a idéia é carregar a página em branco como um espaço reservado com seu próprio código JS e formulário oculto. O código JS pai enviará o formulário oculto onde #actionaponta para o iframe externo. Depois que o redirecionamento / envio acontece, o código JS que ainda está sendo executado nessa página pode rastrear suas document.location.hrefalterações de valor.

Aqui está o exemplo de JS usado no iframe:

;(function($) {
  Drupal.behaviors.commercePayPointIFrame = {
    attach: function (context, settings) {
      if (top.location != location) {
        $('html').hide();
        top.location.href = document.location.href;
      }
    }
  }
})(jQuery);

E aqui está o JS usado na página pai:

;(function($) {
  /**
   * Automatically submit the hidden form that points to the iframe.
   */
  Drupal.behaviors.commercePayPoint = {
    attach: function (context, settings) {
      $('div.payment-redirect-form form', context).submit();
      $('div.payment-redirect-form #edit-submit', context).hide();
      $('div.payment-redirect-form .checkout-help', context).hide();
    }
  }
})(jQuery);

Em seguida, na página de destino temporária em branco, é necessário incluir o formulário que será redirecionado para a página externa.

kenorb
fonte
Isso pode ser um hack, mas a sobrecarga é consideravelmente menor que o observador de mutações. Quando usado em um aplicativo de página única, lembre-se de não criar nenhuma referência que impeça as sobras desse hack de serem coletadas no lixo.
Jeff