Как переместить iFrame в DOM без потери состояния?

95

Взгляните на этот простой HTML:

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

Допустим, я хотел переместить обертки так, #wrap2чтобы перед #wrap1. Окна iframe загрязнены JavaScript. Я знаю jQuery .insertAfter()и .insertBefore(). Однако, когда я их использую, iFrame теряет все свои переменные и события HTML и JavaScript.

Допустим, HTML-код 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>

В приведенном выше коде переменная variableThatChanges... изменится, если пользователь щелкнет тело. Эта переменная и событие щелчка должны оставаться после перемещения iFrame (вместе с любыми другими переменными / событиями, которые были запущены).

Мой вопрос заключается в следующем: с помощью JavaScript (с jQuery или без него), как я могу переместить узлы обертывания в DOM (и их дочерние элементы iframe), чтобы окно iFrame оставалось прежним, а события / переменные iFrame / и т.д. тем же?

JCOC611
источник
На вопрос раньше (давно) -> stackoverflow.com/questions/2885504/… и stackoverflow.com/questions/3029871/…
Manse
Текущая ошибка, обнаруженная с помощью Mozilla ... bugzilla.mozilla.org/show_bug.cgi?id=254144
Manse
@ManseUK: другие вопросы особо не помогли ... но ошибка довольно интересная.
JCOC611
1
@SalmanA, как вы двигаете родителя "вокруг" ребенка?
djechlin 05
1
@denodster желаемый эффект назад, когда я открыл этот вопрос, заключался в изменении порядка списка элементов, каждый с inline-blockотображением ... на данный момент я предполагаю общее решение (или фактически утверждение, что это невозможно) было бы наиболее подходящим, учитывая интерес других.
JCOC611

Ответы:

46

Невозможно переместить iframe из одного места в домене в другое без его перезагрузки.

Вот пример, показывающий, что даже при использовании встроенного JavaScript iFrames все равно перезагружаются: http://jsfiddle.net/pZ23B/

var wrap1 = document.getElementById('wrap1');
var wrap2 = document.getElementById('wrap2');
setTimeout(function(){
    document.getElementsByTagName('body')[0].appendChild(wrap1);
},10000);
Кевин Б.
источник
4
Что ж, если iFrames перезагружаются, то это не вариант.
JCOC611
3
Правильно, цель примера - показать, что даже при использовании встроенного JavaScript iFrames все равно перезагружаются.
Kevin B
Ну, на самом деле, меня действительно не волнует, перезагружается он или нет, поскольку их расположение есть about:blank, но меня действительно волнует потеря содержимого внутри фрейма.
JCOC611
1
если он перезагружается, это приведет к потере содержимого внутри iframe. Я предполагаю, что вы могли бы захватить содержимое внутри iframe, прежде чем перемещать его с помощью jquery, $("#iframeid").contents()однако это не будет хранить сохраненные данные javascript, его нужно будет отправить на родительскую страницу с помощью iframe. (Или родительская страница должна будет захватить его из the iframe)
Kevin B
1
DelightedD0D, награду открыл @djechlin - «Интересно, правда ли это в 2016 году», чтобы узнать, изменилось ли что-нибудь за последние 5 лет в отношении этой проблемы. Вы можете проверить резюме в моем ответе об изменениях за эти годы.
Dekel
41

Этот ответ связан с наградой @djechlin

Много искал по спецификациям w3 / dom и не нашел ничего окончательного, в котором конкретно говорилось бы, что iframe следует перезагружать при перемещении в дереве DOM, однако я нашел много ссылок и комментариев в trac / bugzilla / microsoft webkit относительно различное поведение меняется с годами.

Надеюсь, кто-то найдет что-нибудь конкретное по этому поводу, но пока вот мои выводы:

  1. По словам Рёске Нивы - «Это ожидаемое поведение».
  2. Был «волшебный iframe» ( webkit, 2010 ), но он был удален в 2012 году. .
  3. Согласно MS - « ресурсы iframe освобождаются при удалении из DOM ». Когда вы appendChild(node)из существующего узла - nodeон сначала удаляется из dom.
    Интересная вещь здесь - IE<=8не перезагружал iframe - это поведение (несколько) новое (с тех пор IE>=9).
  4. Согласно комментарию Hallvord RM Steen , это цитата из спецификаций iframe.

    Когда элемент iframe вставляется в документ, имеющий контекст просмотра, пользовательский агент должен создать новый контекст просмотра, установить для вложенного контекста просмотра элемента вновь созданный контекст просмотра, а затем обработать атрибуты iframe в первый раз " .

    Это самое близкое, что я нашел в спецификациях, однако он все еще требует некоторой интерпретации (поскольку, когда мы перемещаем iframeэлемент в DOM, мы на самом деле не делаем его полностью remove, даже если браузеры используют этот node.removeChildметод).

Декель
источник
7
Будем признательны за ответ отрицательного голоса с объяснением.
Dekel
14

Каждый раз, когда добавляется iframe и к нему применен атрибут src, он запускает действие загрузки, как при создании тега Image через JS. Итак, когда вы удаляете и добавляете их, они становятся совершенно новыми объектами и обновляются. Это вроде как window.location = window.locationперезагрузит страницу.

Единственный известный мне способ изменить положение iframes- через CSS. Вот пример, который я собрал, показывающий, как с этим справиться flex-box: https://jsfiddle.net/3g73sz3k/15/

Основная идея состоит в том, чтобы создать flex-boxоболочку, а затем определить определенный порядок для окон iframe с помощью атрибута order в каждой оболочке 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>

Как вы можете видеть в скрипте JS, эти orderстили встроены, чтобы упростить flipкнопку, поэтому поверните файл iframes.

Я нашел решение из этого вопроса StackOverflow: поменять местами DIV только на CSS

Надеюсь, это поможет.

PaulSCoder
источник
1
Баунти за 1-й абзац и его объяснение похоже на img и создание новой сущности. not the css: P
djechlin
Так просто и эффективно! Огромное спасибо!
Stan
8

Если вы создали iFrame на странице и вам просто нужно переместить его положение позже, попробуйте этот подход:

Добавьте iFrame в основной текст и используйте высокий z-index и верхнюю, левую, ширину и высоту, чтобы разместить iFrame там, где вы хотите.

Даже CSS-масштабирование работает с телом без перезагрузки, и это здорово!

Я поддерживаю два состояния своего «виджета», и он либо вводится в DOM, либо в тело с помощью этого метода.

Это полезно, когда другой контент или библиотеки будут сдавливать ваш iFrame.

БУМ!

Mattdlockyer
источник
5

К сожалению, parentNodeсвойство элемента HTML DOM доступно только для чтения. Вы, конечно, можете настроить положение фреймов, но вы не можете изменить их расположение в DOM и сохранить их состояния.

См. Этот созданный мной jsfiddle, который обеспечивает хороший тестовый стенд. http://jsfiddle.net/RpHTj/1/

Щелкните поле, чтобы переключить значение. Нажмите «переместить», чтобы запустить javascript.

Мэтт Х
источник
4

Этот вопрос довольно старый ... но я нашел способ переместить iframe без его перезагрузки. Только CSS. У меня есть несколько фреймов с потоками камеры, мне не нравится, когда они перезагружаются, когда я их меняю. Поэтому я использовал комбинацию блоков float, position: absolute и некоторых фиктивных блоков, чтобы перемещать их, не перезагружая их и не имея желаемого макета по запросу (изменение размера и все).

Moeiscool
источник
1
Похоже, это единственный жизнеспособный вариант. Вы должны опубликовать полное решение, которое придумали! По крайней мере, какой-то базовый код для запуска других. Благодарность!
JCOC611
1

В ответ на награду @djechlin за этот вопрос я разветвил jsfiddle, опубликованный @ matt-h, и пришел к выводу, что это все еще невозможно.

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

Если вы используете iframe для доступа к контролируемым вами страницам, вы можете создать некоторый javascript, чтобы ваш родитель мог общаться с iframe через postMessage.

Оттуда вы можете создать логин внутри iframe для записи изменений состояния и перед перемещением dom запросить это как объект json.

После перемещения iframe будет перезагружен, вы можете передать данные состояния в iframe, а прослушивание iframe может проанализировать данные обратно в предыдущее состояние.

Дрю Мэйджор
источник
Конечно, это большой объем кода, предполагается, что вы контролируете все данные, которые будут отображаться в кадре, и все еще технически не «поддерживает состояние при перемещении в DOM»
Дрю Мейджор