Comment déplacer un iFrame dans le DOM sans perdre son état?

93

Jetez un œil à ce HTML simple:

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

Disons que je voulais déplacer les wraps pour que le #wrap2soit avant le #wrap1. Les iframes sont polluées par JavaScript. Je connais jQuery .insertAfter()et .insertBefore(). Cependant, lorsque j'utilise ceux-ci, l'iFrame perd toutes ses variables et événements HTML et JavaScript.

Disons que ce qui suit était le HTML de l'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>

Dans le code ci-dessus, la variable variableThatChangeschangerait si l'utilisateur cliquait sur le corps. Cette variable, et l'événement de clic, doivent rester après le déplacement de l'iFrame (avec toutes les autres variables / événements qui ont été démarrés)

Ma question est la suivante: avec JavaScript (avec ou sans jQuery), comment puis-je déplacer les nœuds wrap dans le DOM (et leurs enfants iframe) afin que la fenêtre de l'iFrame reste la même et que les événements / variables / etc de l'iFrame restent les même?

JCOC611
la source
Bug actuel soulevé avec Mozilla ... bugzilla.mozilla.org/show_bug.cgi?id=254144
Manse
@ManseUK: les autres questions n'ont pas vraiment aidé ... mais le bug est assez intéressant.
JCOC611
1
@SalmanA comment déplacez-vous un parent «autour» d'un enfant?
djechlin
1
@denodster, l'effet souhaité lorsque j'ai ouvert cette question était de changer l'ordre d'une liste d'éléments, chacun avec inline-blockaffichage ... à ce stade, je devine une solution générique (ou effectivement l'affirmation que cela est impossible) serait le plus approprié, compte tenu de l'intérêt des autres.
JCOC611

Réponses:

46

Il n'est pas possible de déplacer une iframe d'un endroit du dom à un autre sans qu'elle ne soit rechargée.

Voici un exemple pour montrer que même en utilisant JavaScript natif, les iFrames se rechargent toujours: 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
la source
4
Eh bien, si les iFrames se rechargent, ce n'est pas une option.
JCOC611
3
À droite, le but de l'exemple est de montrer que même en utilisant JavaScript natif, les iFrames se rechargent toujours.
Kevin B
Eh bien, en fait, je ne me soucie vraiment pas de savoir s'il se recharge ou non, car leurs emplacements le sont about:blank, mais ce qui m'importe, c'est de perdre le contenu à l'intérieur du cadre.
JCOC611
1
s'il se recharge, cela lui ferait perdre le contenu de l'iframe. Je suppose que vous pouvez récupérer le contenu à l'intérieur de l'iframe avant de le déplacer en utilisant jquery, $("#iframeid").contents()mais cela ne recevra pas de données javascript stockées, il devrait être envoyé à la page parente par l'iframe. (Ou la page parente devrait le récupérer à partir de l'iframe)
Kevin B
1
DelightedD0D, la prime a été ouverte par @djechlin - "Je me demande si cela est encore vrai en 2016" pour voir si quelque chose a changé au cours des 5 dernières années concernant ce problème. Vous pouvez consulter le résumé de ma réponse concernant les changements au cours de ces années.
Dekel
40

Cette réponse est liée à la prime de @djechlin

Beaucoup de recherches sur les spécifications w3 / dom et je n'ai rien trouvé de final indiquant spécifiquement que l'iframe devrait être rechargé tout en se déplaçant dans l'arborescence DOM, mais j'ai trouvé beaucoup de références et de commentaires dans le trac / bugzilla / microsoft du webkit concernant différents changements de comportement au fil des ans.

J'espère que quelqu'un trouvera quelque chose de spécifique concernant ce problème, mais pour l'instant, voici mes conclusions:

  1. Selon Ryosuke Niwa - "C'est le comportement attendu".
  2. Il y avait un «iframe magique» ( webkit, 2010 ), mais il a été supprimé en 2012 .
  3. Selon MS - "les ressources iframe sont libérées lorsqu'elles sont supprimées du DOM ". Lorsque vous appendChild(node)du nœud existant - qui nodeest d'abord supprimé du dom.
    Chose intéressante ici - IE<=8n'a pas rechargé l'iframe - ce comportement est (un peu) nouveau (depuis IE>=9).
  4. Selon le commentaire de Hallvord RM Steen , ceci est une citation des spécifications iframe

    Lorsqu'un élément iframe est inséré dans un document qui a un contexte de navigation, l'agent utilisateur doit créer un nouveau contexte de navigation, définir le contexte de navigation imbriqué de l'élément sur le contexte de navigation nouvellement créé, puis traiter les attributs iframe pour la "première fois " .

    C'est la chose la plus proche que j'ai trouvée dans les spécifications, mais cela nécessite toujours une certaine interprétation (puisque lorsque nous déplaçons l' iframeélément dans le DOM, nous ne faisons pas vraiment un plein remove, même si les navigateurs utilisent la node.removeChildméthode).

Dekel
la source
7
J'apprécierai une réponse du downvoter avec des explications.
Dekel
14

Chaque fois qu'un iframe est ajouté et qu'un attribut src est appliqué, il déclenche une action de chargement de la même manière que lors de la création d'une balise Image via JS. Ainsi, lorsque vous les supprimez puis les ajoutez, ce sont des entités complètement nouvelles et elles s'actualisent. Son genre de comment window.location = window.locationrechargera une page.

La seule façon que je connais de repositionner iframesest via CSS. Voici un exemple que j'ai rassemblé montrant une façon de gérer cela avec flex-box: https://jsfiddle.net/3g73sz3k/15/

L'idée de base est de créer un flex-boxwrapper, puis de définir un ordre spécifique pour les iframes en utilisant l'attribut order sur chaque wrapper 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>

Comme vous pouvez le voir dans le violon JS, ces orderstyles sont en ligne pour simplifier le flipbouton, alors faites-le pivoter iframes.

J'ai trouvé la solution à partir de cette question StackOverflow: Swap DIV position avec CSS uniquement

J'espère que cela pourra aider.

PaulSCoder
la source
1
Prime pour le premier paragraphe et expliquant qu'il est similaire à img et à la création d'une nouvelle entité. pas le css: P
djechlin
Si simple et efficace! Merci beaucoup!
Stan
8

Si vous avez créé l'iFrame sur la page et avez simplement besoin de déplacer sa position plus tard, essayez cette approche:

Ajoutez l'iFrame au corps et utilisez un z-index élevé et haut, gauche, largeur, hauteur pour placer l'iFrame où vous le souhaitez.

Même le zoom CSS fonctionne sur le corps sans recharger ce qui est génial!

Je maintiens deux états pour mon "widget" et il est soit injecté en place dans le DOM soit dans le corps en utilisant cette méthode.

Ceci est utile lorsque d'autres contenus ou bibliothèques écrasent ou écrasent votre iFrame.

BOOM!

mattdlockyer
la source
5

Malheureusement, la parentNodepropriété d'un élément HTML DOM est en lecture seule. Vous pouvez ajuster les positions des iframes, bien sûr, mais vous ne pouvez pas changer leur emplacement dans le DOM et conserver leurs états.

Voir ce jsfiddle que j'ai créé qui fournit un bon banc d'essai. http://jsfiddle.net/RpHTj/1/

Cliquez sur la case pour changer la valeur. Cliquez sur "déplacer" pour exécuter le javascript.

Matt H
la source
4

Cette question est assez ancienne ... mais j'ai trouvé un moyen de déplacer une iframe sans qu'elle ne se recharge. CSS uniquement. J'ai plusieurs iframes avec des flux de caméra, je n'aime pas quand ils se rechargent lorsque je les échange. J'ai donc utilisé une combinaison de flotteur, de position: absolu et de certains blocs factices pour les déplacer sans les recharger et avoir la mise en page souhaitée à la demande (redimensionnement et tout).

moeiscool
la source
1
Sonne comme la seule option viable. Vous devriez publier la solution complète que vous avez proposée! Au moins un code de base pour en démarrer d'autres. Merci!
JCOC611
1

En réponse à la prime @djechlin placée sur cette question, j'ai bifurqué le jsfiddle posté par @ matt-h et suis arrivé à la conclusion que ce n'est toujours pas possible.

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
la source
0

Si vous utilisez l'iframe pour accéder aux pages que vous contrôlez, vous pouvez créer du javascript pour permettre à vos parents de communiquer avec l'iframe via postMessage

À partir de là, vous pouvez créer une connexion à l'intérieur de l'iframe pour enregistrer les changements d'état, et avant de déplacer dom, demander cela en tant qu'objet json.

Une fois déplacé, l'iframe se rechargera, vous pouvez passer les données d'état dans l'iframe et l'écoute de l'iframe peut analyser les données dans l'état précédent.

Drew Major
la source
Certes, c'est beaucoup de code, suppose que vous contrôlez toutes les données qui seront affichées dans le cadre et que techniquement ne "maintient toujours pas l'état lors du déplacement dans le DOM"
Drew Major