Chamando código JavaScript em um iframe da página pai

615

Basicamente, eu tenho um iframeincorporado em uma página e iframetem algumas rotinas de JavaScript que eu preciso chamar a partir da página pai.

Agora, o oposto é bastante simples, como você só precisa ligar parent.functionName(), mas, infelizmente, eu preciso exatamente o oposto disso.

Observe que meu problema não é alterar o URL de origem do iframe, mas invocar uma função definida no iframe.

Tamas Czinege
fonte
42
Sua observação sobre parent.fuctioName () resolveu meu problema de chamar uma função no html pai do iframe. Thanx!
CyberFonic
1
Observe que, durante a depuração, você pode fazer coisas como window.location.href ou parent.location.href para visualizar o URL do iframe, se desejar verificar se você tem uma referência ao iframe que está procurando.
precisa saber é o seguinte
Veja a resposta de @le dorfier ... funcionou perfeitamente para mim.
precisa saber é o seguinte

Respostas:

542

Suponha que o ID do seu iFrame seja "targetFrame" e a função que você deseja chamar é targetFunction():

document.getElementById('targetFrame').contentWindow.targetFunction();

Você também pode acessar o quadro usando em window.framesvez de document.getElementById.

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 
Joel Anair
fonte
3
trabalha em 7 FF, Chrome 12, IE 8/9 e Safari (não tenho certeza desta versão)
Darcy
3
Esta solução não trabalho para o meu dispositivo, aqui é o meu código document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"remote_iframe_0 é criado programmaticaly por um apache baile servidor, mas window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');"obras
J Bourne
3
@ Henry Dirty, o que você quer dizer com "uma função jQuery?" jQuery é uma biblioteca JavaScript.
Joel Anair
8
@JellicleCat Isso ocorre porque a página pai e o iframe têm hosts diferentes, tornando-o um quadro de origem cruzada, conforme a mensagem. Enquanto o protocolo, o domínio e a porta da página pai e do iframe corresponderem, tudo funcionará bem.
Mark-Amery #
1
Adicionei um exemplo de como usar o método window.frames mencionado.
Elijah Lynn
145

Existem algumas peculiaridades a serem observadas aqui.

  1. HTMLIFrameElement.contentWindowé provavelmente a maneira mais fácil, mas não é uma propriedade padrão e alguns navegadores não a suportam, principalmente os mais antigos. Isso ocorre porque o padrão HTML do nível 1 do DOM não tem nada a dizer sobre o windowobjeto.

  2. Você também pode tentar HTMLIFrameElement.contentDocument.defaultView, que alguns navegadores mais antigos permitem, mas o IE não. Mesmo assim, o padrão não diz explicitamente que você recupera o windowobjeto, pelo mesmo motivo que (1), mas você pode escolher algumas versões extras do navegador aqui, se quiser.

  3. window.frames['name']retornar a janela é a interface mais antiga e, portanto, mais confiável. Mas você precisa usar um name="..."atributo para obter um quadro pelo nome, que é um pouco feio / descontinuado / de transição. ( id="..."seria melhor, mas o IE não gosta disso.)

  4. window.frames[number]também é muito confiável, mas saber o índice certo é o truque. Você pode se safar disso, por exemplo. se você sabe que possui apenas um iframe na página.

  5. É perfeitamente possível que o iframe filho ainda não tenha sido carregado ou que algo deu errado para torná-lo inacessível. Você pode achar mais fácil reverter o fluxo de comunicações: ou seja, faça com que o iframe filho notifique seu window.parentscript quando terminar de carregar e estiver pronto para ser chamado de volta. Ao passar um de seus próprios objetos (por exemplo, uma função de retorno de chamada) para o script pai, esse pai pode se comunicar diretamente com o script no iframe sem ter que se preocupar com o que HTMLIFrameElement está associado.

bobince
fonte
13
Coisas boas! Especialmente a sua sugestão 5. mostrou ser extremamente valiosa. Isso me causou muitas dores de cabeça ao tentar acessar as funções do iFrame a partir dos pais no Chrome. Passar o objeto de função para fora do iFrame fez com que ele funcionasse como um encanto no Chrome, FF e até no IE das 9 às 7 e também facilitou a verificação de se a função já havia sido carregada. Obrigado!
Jpsy
O número 3 funciona, mas melhor se você fizer isso como @le dorfier colocou em seu comentário. Observe a methode()parte.
ErickBest
Observe também que, devido a restrições de segurança em navegadores modernos (FF29 e Chrome34 testados), importa muito onde você faz sua chamada de função. Apenas chamar a função a partir de uma tag de script ou $ (document) .ready () não funcionará. Em vez disso, coloque a chamada dentro da tag onload do corpo ou em uma âncora <a href="javascript:doit();"> faça </a> como sugerido em outro lugar (consulte stackoverflow.com/questions/921387/… ). Passar a chamada de função para um script como retorno de chamada também geralmente funciona.
titusn
@chovy: Não, veja os esclarecimentos na resposta de Vivek sobre isso.
bobince
66

iframeÉ possível chamar uma função JS pai de , mas somente quando o pai e a página carregada no iframesão do mesmo domínio, por exemplo, abc.com, e ambos estão usando o mesmo protocolo, ou seja, ambos estão em http://ou https://.

A chamada falhará nos casos mencionados abaixo:

  1. A página pai e a página iframe são de domínio diferente.
  2. Eles estão usando protocolos diferentes, um está em http: // e o outro está em https: //.

Qualquer solução alternativa para essa restrição seria extremamente insegura.

Por exemplo, imagine que eu registrei o domínio superwinningcontest.com e enviei links para os e-mails das pessoas. Quando eles carregavam a página principal, eu podia ocultar alguns iframesegundos e ler o feed do Facebook, verificar transações recentes da Amazon ou do PayPal ou - se eles usassem um serviço que não implementasse segurança suficiente - transferir dinheiro da conta contas. É por isso que o JavaScript é limitado ao mesmo domínio e ao mesmo protocolo.

Vivek Singh CHAUHAN
fonte
6
A solução é usar a bela e perigosa en.wikipedia.org/wiki/Cross-document_messaging
Laramie
Alguém sabe se um subdomínio diferente fará com que isso falhe? Estou tendo dificuldade em depurar um problema que acredito estar relacionado a isso - o iframe está chamando uma função de janela pai, mas a chamada não está acontecendo.
21413 Jake
1
Observe que também falharia em números de porta diferentes. abc.com:80! = abc.com:8080
prasanthv
31

No IFRAME, torne sua função pública no objeto de janela:

window.myFunction = function(args) {
   doStuff();
}

Para acessar a partir da página pai, use este:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);
Tomalak
fonte
Ótima resposta. Não tenho certeza da convenção aqui. Eu tenho uma questão de acompanhamento. Se eu começar uma nova pergunta, eu irei. Eu tenho um problema. O iframe da minha página é preenchido dinamicamente. Ele contém um botão cujo onclick()evento simplesmente chama window.print(). Isso imprime o conteúdo do iframeperfeitamente. Mas quando a função pública da página do iframe chama window.print()e se a página pai chama a função pública, o conteúdo do pai é impresso. Como devo codificar isso para que a chamada para window.print()a função pública se comporte como no onclickevento?
Karl
1
@Karl Você não quer ligar onclick. Você quer ligar iframe.contentWindow.print().
amigos estão
Infelizmente, isso não funciona. Talvez eu deva iniciar uma nova pergunta e documentar meu código? De qualquer forma, o conteúdo da página pai também é impresso. document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent () `(não o manipulador de eventos oncick) que chama window.print()era chamado desta maneira:document.getElementById('myFrame').contentWindow.printContent();
Karl
1
@ Karl - Sim, então é melhor começar uma nova pergunta. A menos que a janela pai esteja em um domínio diferente do iframe. Então, nada que você tente funcionará .
precisa saber é o seguinte
O que relatei aqui parece ser um problema do IE (teste no IE8). Não há problema na versão atual do Chrome. Ainda não testou outros navegadores. Para os interessados, ver este jsFiddle (Acho que este é um bom exemplo de carregamento iframe dinâmico e chamar uma função pública do pai.)
Karl
20

Se o destino do iFrame e o documento contido estiverem em um domínio diferente, os métodos publicados anteriormente podem não funcionar, mas existe uma solução:

Por exemplo, se o documento A contiver um elemento iframe que contém o documento B e o script no documento A chamar postMessage () no objeto Window do documento B, um evento de mensagem será disparado nesse objeto, marcado como originário da Janela de documento A. O script no documento A pode se parecer com:

var o = document.getElementsByTagName('iframe')[0];
o.contentWindow.postMessage('Hello world', 'http://b.example.org/');

Para registrar um manipulador de eventos para eventos recebidos, o script usaria addEventListener () (ou mecanismos semelhantes). Por exemplo, o script no documento B pode se parecer com:

window.addEventListener('message', receiver, false);
function receiver(e) {
  if (e.origin == 'http://example.com') {
    if (e.data == 'Hello world') {
      e.source.postMessage('Hello', e.origin);
    } else {
      alert(e.data);
    }
  }
}

Esse script verifica primeiro o domínio esperado e, em seguida, analisa a mensagem, que é exibida para o usuário ou responde enviando uma mensagem de volta ao documento que a enviou em primeiro lugar.

via http://dev.w3.org/html5/postmsg/#web-messaging

19greg96
fonte
1
O easyXDM fornece uma abstração sobre o PostMessage para navegadores mais antigos (incluindo um fallback em Flash (LocalConnection) para os dinossauros mais difíceis).
JonnyReeves
1
isso é ótimo ... depois de tentar as respostas acima, finalmente recebi sua resposta, obrigado!
vkGunasekaran 5/03
9

Só para constar, encontrei o mesmo problema hoje, mas desta vez a página foi incorporada a um objeto, não a um iframe (já que era um documento XHTML 1.1). Veja como ele funciona com objetos:

document
  .getElementById('targetFrame')
  .contentDocument
  .defaultView
  .targetFunction();

(desculpe pelas quebras de linha feias, não cabiam em uma única linha)

Tamas Czinege
fonte
8

O IFRAME deve estar na frames[]coleção. Use algo como

frames['iframeid'].method();
dkretz
fonte
Essa resposta certamente poderia usar mais algumas informações, pois há definitivamente outras considerações. Por um lado, suponho que o dev precise criar methoduma propriedade do windowobjeto do iframe .
matty
8

Quirksmode tinha um post sobre isso .

Como a página agora está quebrada e acessível apenas via archive.org, eu a reproduzi aqui:

IFrames

Nesta página, dou uma breve visão geral do acesso a iframes na página em que estão. Não é de surpreender que haja algumas considerações sobre o navegador.

Um iframe é um quadro embutido, um quadro que, embora contenha uma página completamente separada com sua própria URL, é colocado dentro de outra página HTML. Isso oferece possibilidades muito agradáveis ​​em web design. O problema é acessar o iframe, por exemplo, para carregar uma nova página nele. Esta página explica como fazê-lo.

Moldura ou objeto?

A questão fundamental é se o iframe é visto como um quadro ou como um objeto.

  • Conforme explicado nas páginas Introdução aos quadros , se você usar quadros, o navegador criará uma hierarquia de quadros para você ( top.frames[1].frames[2]e tal). O iframe se encaixa nessa hierarquia de quadros?
  • Ou o navegador vê um iframe apenas como outro objeto, um objeto que possui uma propriedade src? Nesse caso, temos que usar uma chamada DOM padrão (como document.getElementById('theiframe'))acessá-la. Em geral, os navegadores permitem ambas as visualizações em iframes 'reais' (codificados)), mas os iframes gerados não podem ser acessados ​​como quadros.

Atributo NAME

A regra mais importante é fornecer qualquer iframe que você criar um nameatributo, mesmo se você também usar um id.

<iframe src="iframe_page1.html"
    id="testiframe"
    name="testiframe"></iframe>

A maioria dos navegadores precisa do nameatributo para tornar o iframe parte da hierarquia de quadros. Alguns navegadores (principalmente o Mozilla) precisam idtornar o iframe acessível como um objeto. Ao atribuir os dois atributos ao iframe, você mantém suas opções em aberto. Mas nameé muito mais importante que id.

Acesso

Você acessa o iframe como um objeto e altera o seu srcou acessa o iframe como um quadro e altera o seu location.href.

document.getElementById ('iframe_id'). src = 'newpage.html'; frames ['iframe_name']. location.href = 'newpage.html'; A sintaxe do quadro é um pouco preferível porque o Opera 6 suporta, mas não a sintaxe do objeto.

Acessando o iframe

Portanto, para uma experiência completa em vários navegadores, você deve dar um nome ao iframe e usar o

frames['testiframe'].location.href

sintaxe. Tanto quanto sei, isso sempre funciona.

Acessando o documento

O acesso ao documento dentro do iframe é bastante simples, desde que você use o nameatributo Para contar o número de links no documento no iframe, faça frames['testiframe'].document.links.length.

Iframes gerados

Porém, quando você gera um iframe por meio do DOM do W3C, o iframe não é inserido imediatamente na framesmatriz e a frames['testiframe'].location.hrefsintaxe não funciona imediatamente. O navegador precisa de um tempo para que o iframe apareça na matriz, tempo durante o qual nenhum script pode ser executado.

A document.getElementById('testiframe').srcsintaxe funciona bem em todas as circunstâncias.

O targetatributo de um link também não funciona com iframes gerados, exceto no Opera, mesmo que eu tenha dado ao iframe gerado a namee an id.

A falta de targetsuporte significa que você deve usar o JavaScript para alterar o conteúdo de um iframe gerado, mas como você precisa do JavaScript para gerá-lo em primeiro lugar, não vejo isso como um problema.

Tamanho do texto em iframes

Um curioso bug do Explorer 6:

Quando você altera o tamanho do texto no menu Exibir, os tamanhos de texto em iframes são alterados corretamente. No entanto, este navegador não altera as quebras de linha no texto original, para que parte do texto fique invisível ou quebras de linha possam ocorrer enquanto a linha ainda pode conter outra palavra.

activout.se
fonte
5
Esse link parece estar quebrado.
zaphod
1
E para piorar as coisas, não consigo encontrar a página relacionada em nenhum lugar no modo quirks.
stricjux
7

Encontrei uma solução bastante elegante.

Como você disse, é bastante fácil executar o código localizado no documento pai. E essa é a base do meu código, faça exatamente o oposto.

Quando meu iframe é carregado, chamo uma função localizada no documento pai, passando como argumento uma referência a uma função local, localizada no documento do iframe. O documento pai agora tem acesso direto à função do iframe por meio dessa referência.

Exemplo:

No pai:

function tunnel(fn) {
    fn();
}

No iframe:

var myFunction = function() {
    alert("This work!");
}

parent.tunnel(myFunction);

Quando o iframe for carregado, ele chamará parent.tunnel (YourFunctionReference), que executará a função recebida no parâmetro

Simples assim, sem ter que lidar com todos os métodos fora dos padrões dos vários navegadores.

Julien L
fonte
Olá, você pode confirmar isso "Simples assim, sem precisar lidar com todos os métodos não padronizados dos vários navegadores"?
Milche Patern
1
Eu uso esse método em vários projetos, parece funcionar muito bem. Eu pessoalmente não testei todos os navegadores em circulação, mas também não recebi um relatório de erro.
Julien L
Funciona como um encanto, IE 11, Chrome ~ 50.
Augustas
1
Acho que isso não é entre domínios. É?
Tushar Shukla
@TusharShukla Infelizmente não, não é.
Jeff-h
6

Algumas dessas respostas não abordam a questão do CORS ou não tornam óbvio onde você coloca os trechos de código para tornar a comunicação possível.

Aqui está um exemplo concreto. Digamos que eu queira clicar em um botão na página pai e fazer isso dentro do iframe. Aqui está como eu faria isso.

parent_frame.html

<button id='parent_page_button' onclick='call_button_inside_frame()'></button>

function call_button_inside_frame() {
   document.getElementById('my_iframe').contentWindow.postMessage('foo','*');
}

iframe_page.html

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
    {
      if(event) {
        click_button_inside_frame();
    }
}

function click_button_inside_frame() {
   document.getElementById('frame_button').click();
}

Para ir na outra direção (clique no botão dentro do iframe para chamar o método fora do iframe), basta alternar para o local do snippet de código e alterar isso:

document.getElementById('my_iframe').contentWindow.postMessage('foo','*');

para isso:

window.parent.postMessage('foo','*')
Cibernético
fonte
4

Continuando com a resposta de JoelAnair :

Para maior robustez, use o seguinte:

var el = document.getElementById('targetFrame');

if(el.contentWindow)
{
   el.contentWindow.targetFunction();
}
else if(el.contentDocument)
{
   el.contentDocument.targetFunction();
}

Trabalho como charme :)

Nitin Bansal
fonte
3

Seguindo a resposta de Nitin Bansal

e para ainda mais robustez:

function getIframeWindow(iframe_object) {
  var doc;

  if (iframe_object.contentWindow) {
    return iframe_object.contentWindow;
  }

  if (iframe_object.window) {
    return iframe_object.window;
  } 

  if (!doc && iframe_object.contentDocument) {
    doc = iframe_object.contentDocument;
  } 

  if (!doc && iframe_object.document) {
    doc = iframe_object.document;
  }

  if (doc && doc.defaultView) {
   return doc.defaultView;
  }

  if (doc && doc.parentWindow) {
    return doc.parentWindow;
  }

  return undefined;
}

e

...
var el = document.getElementById('targetFrame');

var frame_win = getIframeWindow(el);

if (frame_win) {
  frame_win.targetFunction();
  ...
}
...
Dominique Fortin
fonte
2
       $("#myframe").load(function() {
            alert("loaded");
        });
GeverGever
fonte
2

Mesmas coisas, mas um pouco mais fácil, será Como atualizar a página pai da página no iframe . Basta chamar a função da página pai para chamar a função javascript para recarregar a página:

window.location.reload();

Ou faça isso diretamente da página no iframe:

window.parent.location.reload();

Ambos os trabalhos.

sangam
fonte
1

Se você deseja chamar a função JavaScript no pai a partir do iframe gerado por outra função, ex shadowbox ou lightbox.

Você deve tentar fazer uso do windowobjeto e chamar a função pai:

window.parent.targetFunction();
Santhanakumar
fonte
Eu acredito que o OP está tentando ir na outra direção
CommandZ 23/01
-3

Use o seguinte para chamar a função de um quadro na página pai

parent.document.getElementById('frameid').contentWindow.somefunction()
Sandy
fonte