Como mover um iframe no DOM sem perder seu estado?

93

Dê uma olhada neste HTML simples:

<div id="wrap1">
  <iframe id="iframe1"></iframe>
</div>
<div id="warp2">
  <iframe id="iframe2"></iframe>
</div>

Digamos que eu queira mover os envoltórios para que o #wrap2seja antes do #wrap1. Os iframes são poluídos por JavaScript. Estou ciente de jQuery's .insertAfter()e .insertBefore(). No entanto, quando eu os uso, o iFrame perde todas as variáveis ​​e eventos HTML e JavaScript.

Digamos que o seguinte fosse o HTML do iframe:

<html>
  <head>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">
      // The variable below would change on click
      // This represents changes on variables after the code is loaded
      // These changes should remain after the iFrame is moved
      variableThatChanges = false;
      $(function(){
        $("body").click(function(){ 
          variableThatChanges = true; 
        });
      });
    </script>
  </head>
  <body>
    <div id='anything'>Illustrative Example</div>
  </body>
</html>

No código acima, a variável variableThatChanges... mudaria se o usuário clicasse no corpo. Esta variável, e o evento de clique, devem permanecer após o iFrame ser movido (junto com quaisquer outras variáveis ​​/ eventos que foram iniciados)

Minha pergunta é a seguinte: com JavaScript (com ou sem jQuery), como posso mover os nós de wrap no DOM (e seus filhos iframe) para que a janela do iframe permaneça a mesma e os eventos / variáveis ​​/ etc do iframe permaneçam os mesmo?

JCOC611
fonte
Perguntado antes (muito tempo atrás) -> stackoverflow.com/questions/2885504/… e stackoverflow.com/questions/3029871/…
Manse
Bug atual gerado com Mozilla ... bugzilla.mozilla.org/show_bug.cgi?id=254144
Manse
@ManseUK: as outras questões não ajudaram muito ... mas o bug é bem interessante.
JCOC611
1
@SalmanA como você move um pai "perto" de um filho?
Djechlin
1
@denodster o efeito desejado quando abri esta questão era mudar a ordem de uma lista de elementos, cada um com inline-blockexibição ... neste ponto, estou supondo uma solução genérica (ou efetivamente a afirmação de que tal é impossível) seria mais apropriado, dado o interesse de outros.
JCOC611

Respostas:

46

Não é possível mover um iframe de um lugar no dom para outro sem recarregar.

Aqui está um exemplo para mostrar que mesmo usando JavaScript nativo, os iFrames ainda recarregam: http://jsfiddle.net/pZ23B/

var wrap1 = document.getElementById('wrap1');
var wrap2 = document.getElementById('wrap2');
setTimeout(function(){
    document.getElementsByTagName('body')[0].appendChild(wrap1);
},10000);
Kevin B
fonte
4
Bem, se os iframes recarregam, não é uma opção.
JCOC611
3
Certo, o objetivo do exemplo é mostrar que mesmo usando JavaScript nativo, os iframes ainda recarregam.
Kevin B
Bem, na verdade, eu realmente não me importo se ele recarrega ou não, já que suas localizações são about:blank, mas o que me importa é perder o conteúdo dentro do quadro.
JCOC611
1
se recarregar, perderá o conteúdo dentro do iframe. Eu acho que você poderia pegar o conteúdo dentro do iframe antes de movê-lo usando jquery, $("#iframeid").contents()mas isso não obterá dados javascript armazenados, ele teria que ser enviado para a página pai pelo iframe. (Ou a página pai teria que pegá-lo de o iframe)
Kevin B
1
DelightedD0D, a recompensa foi aberta por @djechlin - "Eu me pergunto se isso ainda é verdade em 2016" para ver se algo mudou nos últimos 5 anos em relação a esse assunto. Você pode conferir o resumo na minha resposta sobre as mudanças durante esses anos.
Dekel,
40

Esta resposta está relacionada à recompensa de @djechlin

Muitas pesquisas nas especificações w3 / dom e não encontrei nada final que diga especificamente que o iframe deve ser recarregado enquanto se move na árvore DOM, no entanto, encontrei muitas referências e comentários no trac / bugzilla / microsoft do webkit sobre comportamento diferente muda ao longo dos anos.

Espero que alguém encontre algo específico sobre esse problema, mas por enquanto, aqui estão minhas conclusões:

  1. De acordo com Ryosuke Niwa - "Esse é o comportamento esperado".
  2. Havia um "iframe mágico" ( webkit, 2010 ), mas foi removido em 2012 .
  3. De acordo com a MS - "os recursos do iframe são liberados quando removidos do DOM ". Quando você appendChild(node)do nó existente - que nodeprimeiro é removido do dom.
    Coisa interessante aqui - IE<=8não recarregue o iframe - este comportamento é (um pouco) novo (desde IE>=9).
  4. De acordo com o comentário de Hallvord RM Steen , esta é uma citação das especificações do iframe

    Quando um elemento iframe é inserido em um documento que tem um contexto de navegação, o agente do usuário deve criar um novo contexto de navegação, definir o contexto de navegação aninhado do elemento para o contexto de navegação recém-criado e, em seguida, processar os atributos do iframe pela "primeira vez " .

    Esta é a coisa mais próxima que encontrei nas especificações, no entanto, ainda requer alguma interpretação (já que quando movemos o iframeelemento no DOM, não fazemos um full remove, mesmo se o navegador usar o node.removeChildmétodo).

Dekel
fonte
7
Agradecemos uma resposta do downvoter com uma explicação.
Dekel,
14

Sempre que um iframe é anexado e tem um atributo src aplicado, ele dispara uma ação de carregamento de forma semelhante ao criar uma tag de imagem via JS. Portanto, quando você os remove e os anexa, eles são entidades completamente novas e são atualizados. É uma espécie de como window.location = window.locationrecarregar uma página.

A única maneira que conheço de reposicionar iframesé via CSS. Aqui está um exemplo que criei mostrando uma maneira de lidar com isso flex-box: https://jsfiddle.net/3g73sz3k/15/

A ideia básica é criar um flex-boxwrapper e, em seguida, definir uma ordem específica para os iframes usando o atributo order em cada wrapper de iframe.

<style>
  .container{
    display: flex;
    flex-direction: column;
  }
</style>
<div class="container">
  <div id="wrap1" style="order: 0" class="iframe-wrapper">
    <iframe id="iframe1" src="https://google.com"></iframe>
  </div>
  <div id="warp2" style="order: 1" class="iframe-wrapper">
    <iframe id="iframe2" src="https://bing.com"></iframe>
  </div>
</div>

Como você pode ver no violino JS, esses orderestilos são embutidos para simplificar o flipbotão, então gire o iframes.

Eu obtive a solução desta questão StackOverflow: Trocar posição DIV com CSS apenas

Espero que ajude.

PaulSCoder
fonte
1
Recompensa para o primeiro parágrafo e explicar é semelhante a img e criar uma nova entidade. não o css: P
djechlin
Tão simples e eficaz! Muito obrigado!
Stan
8

Se você criou o iframe na página e simplesmente precisa mover sua posição mais tarde, tente esta abordagem:

Anexe o iframe ao corpo e use um índice z alto e alto, esquerdo, largura e altura para colocar o iframe onde quiser.

Até o zoom CSS funciona no corpo sem recarregar, o que é incrível!

Eu mantenho dois estados para meu "widget" e ele é injetado no local no DOM ou no corpo usando este método.

Isso é útil quando outro conteúdo ou bibliotecas irão esmagar ou esmagar seu iFrame.

ESTRONDO!

Mattdlockyer
fonte
5

Infelizmente, a parentNodepropriedade de um elemento HTML DOM é somente leitura. Você pode ajustar as posições dos iframes, é claro, mas não pode alterar sua localização no DOM e preservar seus estados.

Veja este jsfiddle que criei que fornece uma boa base de teste. http://jsfiddle.net/RpHTj/1/

Clique na caixa para alternar o valor. Clique em "mover" para executar o javascript.

Matt H
fonte
4

Esta pergunta é muito antiga ... mas encontrei uma maneira de mover um iframe sem recarregar. CSS apenas. Tenho vários iframes com streams de câmera, não gosto quando eles recarregam quando eu os troco. Então eu usei uma combinação de float, position: absolute e alguns blocos fictícios para movê-los sem recarregá-los e ter o layout desejado sob demanda (redimensionamento e tudo).

Moeiscool
fonte
1
Parece a única opção viável. Você deve postar a solução completa que encontrou! Pelo menos algum código básico para iniciar outros. Obrigado!
JCOC611
1

Em resposta à recompensa que @djechlin colocou sobre esta questão, eu fiz um bifurcação no jsfiddle postado por @ matt-h e cheguei à conclusão de que isso ainda não é possível.

http://jsfiddle.net/gr3wo9u6/

//this does not work, the frames reload when appended back to the DOM
function swapFrames() {
    var w1 = document.getElementById('wrap1');
    var w2 = document.getElementById('wrap2');
    var f1 = w1.querySelector('iframe');
    var f2 = w2.querySelector('iframe');

    w1.removeChild(f1);
    w2.removeChild(f2);
    w1.appendChild(f2);
    w2.appendChild(f1);
    //f1.parentNode = w2;
    //f2.parentNode = w1;

    //alert(f1.parentNode.id);
}
denodster
fonte
0

Se você estiver usando o iframe para acessar as páginas que você controla, pode criar algum javascript para permitir que seus pais se comuniquem com o iframe via postMessage

A partir daí, você pode construir o login dentro do iframe para registrar as mudanças de estado e, antes de mover o dom, solicitá-lo como um objeto json.

Depois de movido, o iframe será recarregado, você pode passar os dados de estado para o iframe e a escuta do iframe pode analisar os dados de volta ao estado anterior.

Drew Major
fonte
Concedido, isso é muito código, presume que você controle todos os dados que serão exibidos no quadro e ainda tecnicamente não "mantém o estado ao mover no DOM"
Desenhou Principal