Jak przenieść element iFrame w DOM bez utraty jego stanu?


95

Spójrz na ten prosty kod HTML:

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

Powiedzmy, że chciałem przesunąć folie, aby #wrap2były przed #wrap1. Elementy iframe są zanieczyszczane przez JavaScript. Znam jQuery .insertAfter()i .insertBefore(). Jednak gdy ich używam, element iFrame traci wszystkie zmienne i zdarzenia HTML i JavaScript.

Powiedzmy, że kod HTML elementu iFrame przedstawia się następująco:

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

W powyższym kodzie zmienna variableThatChanges... zmieniłaby się, gdyby użytkownik kliknął treść. Ta zmienna i zdarzenie kliknięcia powinny pozostać po przeniesieniu elementu iframe (wraz z innymi zmiennymi / zdarzeniami, które zostały uruchomione)

Moje pytanie jest następujące: w przypadku JavaScript (z jQuery lub bez), jak mogę przenieść węzły zawijania w DOM (i ich elementy potomne iframe), aby okno iFrame pozostało takie samo, a zdarzenia / zmienne / itp. Elementu iFrame pozostały podobnie?



Bieżący błąd zgłoszony przez Mozillę ... bugzilla.mozilla.org/show_bug.cgi?id=254144
Manse

@ManseUK: inne pytania tak naprawdę nie pomogły ... ale błąd jest całkiem interesujący.
JCOC611

1
@SalmanA jak przenieść rodzica „wokół” dziecka?
djechlin

1
@denodster pożądanym efektem dawno temu, kiedy otworzyłem to pytanie, była zmiana kolejności listy elementów, każdy z inline-blockwyświetlaniem ... w tym miejscu zgaduję rozwiązanie ogólne (a właściwie stwierdzenie, że takie jest niemożliwe) byłoby najbardziej odpowiednie, biorąc pod uwagę zainteresowanie innych.
JCOC611,

Odpowiedzi:


46

Nie można przenieść elementu iframe z jednego miejsca w dom do drugiego bez jego ponownego ładowania.

Oto przykład pokazujący, że nawet przy użyciu natywnego JavaScript ramki iFrames nadal ładują się ponownie: http://jsfiddle.net/pZ23B/

var wrap1 = document.getElementById('wrap1');
var wrap2 = document.getElementById('wrap2');
setTimeout(function(){
    document.getElementsByTagName('body')[0].appendChild(wrap1);
},10000);

4
Cóż, jeśli elementy iFrames ponownie się załadują, nie ma takiej opcji.
JCOC611

3
Tak, przykład ma na celu pokazanie, że nawet przy użyciu natywnego języka JavaScript elementy iFrame nadal ładują się ponownie.
Kevin B

Cóż, właściwie nie obchodzi mnie, czy ładuje się ponownie, czy nie, ponieważ ich lokalizacje są about:blank, ale to, na czym mi zależy, to utrata zawartości wewnątrz ramki.
JCOC611

1
jeśli się ponownie załaduje, spowoduje to utratę zawartości wewnątrz elementu iframe. Wydaje mi się, że możesz pobrać zawartość wewnątrz elementu iframe przed przeniesieniem go za pomocą jquery, $("#iframeid").contents()jednak nie spowoduje to zapisania danych javascript, musiałby zostać wysłany na stronę nadrzędną przez iframe. (Lub strona nadrzędna musiałaby to pobrać z iframe)
Kevin B

1
DelightedD0D, nagrodę otworzył @djechlin - „Zastanawiam się, czy to nadal prawda w 2016 roku”, aby zobaczyć, czy w ciągu ostatnich 5 lat coś się zmieniło w tej kwestii. W mojej odpowiedzi możesz sprawdzić podsumowanie zmian na przestrzeni tych lat.
Dekel

42

Ta odpowiedź jest związana z nagrodą autorstwa @djechlin

Dużo wyszukiwałem specyfikacji w3 / dom i nie znalazłem niczego ostatecznego, co konkretnie mówi, że ramka iframe powinna być przeładowywana podczas poruszania się po drzewie DOM, jednak znalazłem wiele odniesień i komentarzy w trac / bugzilla / microsoft w webkicie dotyczących różne zmiany zachowań na przestrzeni lat.

Mam nadzieję, że ktoś znajdzie coś konkretnego na ten temat, ale na razie oto moje ustalenia:

  1. Według Ryosuke Niwa - „To jest oczekiwane zachowanie”.
  2. Istniał „magiczny iframe” ( webkit, 2010 ), ale został usunięty w 2012 roku .
  3. Według MS - „ zasoby iframe są zwalniane po usunięciu z DOM ”. Kiedy masz appendChild(node)istniejący węzeł - to nodejest najpierw usuwany z domeny.
    Ciekawostka tutaj - IE<=8nie przeładowano iframe - to zachowanie jest (trochę) nowe (od IE>=9).
  4. Według komentarza Hallvord RM Steen , jest to cytat ze specyfikacji iframe

    Gdy element iframe jest wstawiany do dokumentu, który ma kontekst przeglądania, klient użytkownika musi utworzyć nowy kontekst przeglądania, ustawić zagnieżdżony kontekst przeglądania elementu na nowo utworzony kontekst przeglądania, a następnie przetworzyć atrybuty iframe po raz pierwszy ” .

    Jest to najbardziej zbliżona rzecz, jaką znalazłem w specyfikacji, jednak nadal wymaga pewnej interpretacji (ponieważ kiedy przenosimy iframeelement w DOM, tak naprawdę nie robimy pełnego remove, nawet jeśli przeglądarki używają tej node.removeChildmetody).


7
Doceni odpowiedź przeciwnika z wyjaśnieniem.
Dekel

14

Za każdym razem, gdy dołączany jest element iframe i ma zastosowany atrybut src, uruchamia akcję ładowania, podobnie jak podczas tworzenia tagu Image za pomocą JS. Więc kiedy je usuniesz, a następnie dołączysz, są to zupełnie nowe encje i odświeżają się. To rodzaj window.location = window.locationponownego załadowania strony.

Jedyny znany mi sposób zmiany pozycji iframesto użycie CSS. Oto przykład, który stworzyłem, pokazujący jeden sposób rozwiązania tego problemuflex-box : https://jsfiddle.net/3g73sz3k/15/

Podstawowym pomysłem jest utworzenie flex-boxopakowania, a następnie zdefiniowanie określonej kolejności elementów iframe przy użyciu atrybutu order w każdym opakowaniu 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>

Jak widać na skrzypcach JS, te orderstyle są wbudowane, aby uprościć flipprzycisk, więc obróć iframes.

Rozwiązanie pochodzi z tego pytania StackOverflow: Zamień pozycję DIV tylko za pomocą CSS

Mam nadzieję, że to pomoże.


1
Nagroda za pierwszy akapit i wyjaśnienie, że jest podobne do img i tworzenia nowego bytu. nie css: P
djechlin

Tak proste i skuteczne! Dziękuję bardzo!
Stan

8

Jeśli utworzyłeś element iFrame na stronie i chcesz po prostu przesunąć jego pozycję później, wypróbuj następujące podejście:

Dołącz element iFrame do treści i użyj wysokiego indeksu Z oraz góry, lewej, szerokości, wysokości, aby umieścić element iFrame w wybranym miejscu.

Nawet zoom CSS działa na ciele bez ponownego ładowania, co jest niesamowite!

Utrzymuję dwa stany dla mojego „widżetu” i jest on albo wstrzykiwany na miejscu w DOM, albo do ciała przy użyciu tej metody.

Jest to przydatne, gdy inna zawartość lub biblioteki zgniatają lub zgniatają element iFrame.

BUM!


5

Niestety, parentNodewłaściwość elementu HTML DOM jest tylko do odczytu. Oczywiście możesz dostosować pozycje elementów iframe, ale nie możesz zmienić ich położenia w DOM i zachować ich stanów.

Zobacz ten jsfiddle, który stworzyłem, który zapewnia dobre łóżko testowe. http://jsfiddle.net/RpHTj/1/

Kliknij pole, aby przełączyć wartość. Kliknij „przenieś”, aby uruchomić javascript.


4

To pytanie jest dość stare ... ale znalazłem sposób na przeniesienie ramki iframe bez jej ponownego ładowania. Tylko CSS. Mam wiele ramek iframe ze strumieniami z kamery, nie lubię, gdy ładują się ponownie, gdy je zamieniam. Użyłem więc kombinacji pływających, pozycji: bezwzględnych i kilku fikcyjnych bloków, aby przenieść je bez ponownego ładowania i posiadania żądanego układu na żądanie (zmiana rozmiaru i wszystko).


1
Brzmi jak jedyna możliwa opcja. Powinieneś opublikować pełne rozwiązanie, które wymyśliłeś! Przynajmniej trochę podstawowego kodu, aby inni zaczęli. Dzięki!
JCOC611

1

W odpowiedzi na bounty @djechlin umieszczone na tym pytaniu rozwidliłem jsfiddle zamieszczone przez @ matt-h i doszedłem do wniosku, że nadal nie jest to możliwe.

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);
}

0

Jeśli używasz elementu iframe do uzyskiwania dostępu do stron, które kontrolujesz, możesz utworzyć skrypt javascript, aby umożliwić rodzicowi komunikowanie się z elementem iframe za pośrednictwem wiadomości postMessage

Stamtąd możesz zbudować login wewnątrz elementu iframe, aby zarejestrować zmiany stanu, a przed przeniesieniem dom, zażądać go jako obiektu json.

Po przeniesieniu element iframe zostanie ponownie załadowany, możesz przekazać dane stanu do elementu iframe, a element nasłuchujący elementu iframe może przeanalizować dane z powrotem do poprzedniego stanu.


To prawda, jest to dużo kodu, przy założeniu, że kontrolujesz wszystkie dane, które będą wyświetlane w ramce, i nadal technicznie nie "utrzymuje stanu podczas poruszania się w DOM"
Drew Major
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.