¿Cómo mover un iFrame en el DOM sin perder su estado?

93

Eche un vistazo a este sencillo HTML:

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

Digamos que quería mover las envolturas para que #wrap2estuvieran antes de #wrap1. Los iframes están contaminados por JavaScript. Soy consciente de jQuery .insertAfter()y .insertBefore(). Sin embargo, cuando los uso, el iFrame pierde todos sus eventos y variables HTML y JavaScript.

Digamos que lo siguiente fue el HTML de 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>

En el código anterior, la variable variableThatChanges... cambiaría si el usuario hiciera clic en el cuerpo. Esta variable y el evento de clic deben permanecer después de mover el iFrame (junto con cualquier otra variable / evento que se haya iniciado)

Mi pregunta es la siguiente: con JavaScript (con o sin jQuery), ¿cómo puedo mover los nodos de envoltura en el DOM (y sus hijos iframe) para que la ventana de iFrame permanezca igual, y los eventos / variables / etc. de iFrame permanezcan como ¿mismo?

JCOC611
fuente
Error actual planteado con Mozilla ... bugzilla.mozilla.org/show_bug.cgi?id=254144
Manse
@ManseUK: las otras preguntas realmente no ayudaron ... pero el error es bastante interesante.
JCOC611
1
@SalmanA ¿cómo se mueve a un padre "alrededor" de un niño?
djechlin
1
@denodster, el efecto deseado cuando abrí esta pregunta era cambiar el orden de una lista de elementos, cada uno con inline-blockpantalla ... en este punto, supongo que una solución genérica (o efectivamente la afirmación de que eso es imposible) sería lo más apropiado, dado el interés de los demás.
JCOC611

Respuestas:

46

No es posible mover un iframe de un lugar a otro en el dom sin que se vuelva a cargar.

Aquí hay un ejemplo para mostrar que incluso usando JavaScript nativo, los iFrames aún se recargan: 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
fuente
4
Bueno, si los iFrames se recargan, entonces no es una opción.
JCOC611
3
Correcto, el propósito del ejemplo es mostrar que incluso usando JavaScript nativo, los iFrames aún se recargan.
Kevin B
Bueno, en realidad, no me importa si se recarga o no, ya que sus ubicaciones lo son about:blank, pero lo que sí me importa es perder el contenido dentro del marco.
JCOC611
1
si se vuelve a cargar, eso haría que pierda el contenido dentro del iframe. Supongo que podría tomar el contenido dentro del iframe antes de moverlo usando jquery, $("#iframeid").contents()sin embargo, eso no obtendrá datos de JavaScript almacenados, el iframe tendría que enviarlo a la página principal. (O la página principal tendría que tomarlo de el iframe)
Kevin B
1
DelightedD0D, @djechlin abrió la recompensa: "Me pregunto si esto seguirá siendo cierto en 2016" para ver si algo ha cambiado en los últimos 5 años con respecto a este problema. Puede consultar el resumen en mi respuesta sobre los cambios durante estos años.
Dekel
40

Esta respuesta está relacionada con la recompensa de @djechlin

Realicé muchas búsquedas en las especificaciones de w3 / dom y no encontré nada final que diga específicamente que el iframe debe recargarse mientras se mueve en el árbol DOM, sin embargo, encontré muchas referencias y comentarios en el webkit's trac / bugzilla / microsoft con respecto a diferentes cambios de comportamiento a lo largo de los años.

Espero que alguien encuentre algo específico con respecto a este tema, pero por ahora aquí están mis hallazgos:

  1. Según Ryosuke Niwa - "Ese es el comportamiento esperado".
  2. Había un "iframe mágico" ( webkit, 2010 ), pero se eliminó en 2012 .
  3. Según MS, "los recursos de iframe se liberan cuando se eliminan del DOM ". Cuando eres appendChild(node)de un nodo existente, nodeprimero se elimina del dom.
    Algo interesante aquí - IE<=8no recargué el iframe - este comportamiento es (algo) nuevo (desde IE>=9).
  4. Según el comentario de Hallvord RM Steen , esta es una cita de las especificaciones de iframe

    Cuando se inserta un elemento de iframe en un documento que tiene un contexto de navegación, el agente de usuario debe crear un nuevo contexto de navegación, establecer el contexto de navegación anidado del elemento en el contexto de navegación recién creado y luego procesar los atributos de iframe por primera vez " .

    Esto es lo más cercano que encontré en las especificaciones, sin embargo, todavía requiere cierta interpretación (ya que cuando movemos el iframeelemento en el DOM realmente no lo hacemos por completo remove, incluso si los navegadores usan el node.removeChildmétodo).

Dekel
fuente
7
Agradeceré una respuesta del votante negativo con una explicación.
Dekel
14

Siempre que se agrega un iframe y se le aplica un atributo src, se activa una acción de carga de manera similar a cuando se crea una etiqueta de imagen a través de JS. Entonces, cuando los elimina y luego los agrega, son entidades completamente nuevas y se actualizan. Es como window.location = window.locationvolver a cargar una página.

La única forma que conozco de reposicionar iframeses a través de CSS. Aquí hay un ejemplo que reuní que muestra una forma de manejar esto con flex-box: https://jsfiddle.net/3g73sz3k/15/

La idea básica es crear un flex-boxcontenedor y luego definir un orden específico para los iframes utilizando el atributo de orden en cada contenedor 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 puede ver en el violín JS, estos orderestilos están en línea para simplificar el flipbotón, así que gire el iframes.

Obtuve la solución de esta pregunta de StackOverflow: intercambie la posición DIV solo con CSS

Espero que ayude.

PaulSCoder
fuente
1
Recompensa por el primer párrafo y explicando que es similar a img y creando una nueva entidad. no el css: P
djechlin
¡Tan simple y efectivo! ¡Muchas gracias!
Stan
8

Si ha creado el iFrame en la página y simplemente necesita mover su posición más tarde, pruebe este enfoque:

Agregue el iFrame al cuerpo y use un índice Z alto y arriba, izquierda, ancho, alto para colocar el iFrame donde desee.

Incluso el zoom CSS funciona en el cuerpo sin recargar, ¡lo cual es increíble!

Mantengo dos estados para mi "widget" y se inyecta en su lugar en el DOM o en el cuerpo usando este método.

Esto es útil cuando otros contenidos o bibliotecas aplastan o aplastan su iFrame.

¡AUGE!

mattdlockyer
fuente
5

Desafortunadamente, la parentNodepropiedad de un elemento DOM de HTML es de solo lectura. Puede ajustar las posiciones de los iframes, por supuesto, pero no puede cambiar su ubicación en el DOM y conservar sus estados.

Vea este jsfiddle que creé que proporciona un buen banco de pruebas. http://jsfiddle.net/RpHTj/1/

Haga clic en el cuadro para alternar el valor. Haga clic en "mover" para ejecutar el javascript.

Matt H
fuente
4

Esta pregunta es bastante antigua ... pero encontré una manera de mover un iframe sin que se recargue. Solo CSS. Tengo varios iframes con transmisiones de cámara, no me gusta cuando se recargan cuando los cambio. Así que utilicé una combinación de flotante, posición: absoluta y algunos bloques ficticios para moverlos sin recargarlos y tener el diseño deseado a pedido (cambio de tamaño y todo).

moeiscool
fuente
1
Suena como la única opción viable. ¡Debería publicar la solución completa que se le ocurrió! Al menos un código básico para que otros comiencen. ¡Gracias!
JCOC611
1

En respuesta a la recompensa que @djechlin puso en esta pregunta, he bifurcado el jsfiddle publicado por @ matt-h y he llegado a la conclusión de que esto todavía no es posible.

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
fuente
0

Si está utilizando el iframe para acceder a las páginas que controla, puede crear un javascript para permitir que sus padres se comuniquen con el iframe a través de postMessage

A partir de ahí, puede crear un inicio de sesión dentro del iframe para registrar los cambios de estado y, antes de mover dom, solicítelo como un objeto json.

Una vez movido, el iframe se recargará, puede pasar los datos de estado al iframe y el iframe que escucha puede analizar los datos de nuevo al estado anterior.

Dibujó mayor
fuente
Por supuesto, esto es mucho código, asume que usted controla todos los datos que se mostrarán en el marco, y todavía técnicamente no "mantiene el estado cuando se mueve en DOM"
Drew Major