如何在 DOM 中移动 iFrame 而不会丢失其状态?

Posted

技术标签:

【中文标题】如何在 DOM 中移动 iFrame 而不会丢失其状态?【英文标题】:How to move an iFrame in the DOM without losing its state? 【发布时间】:2012-01-09 05:39:14 【问题描述】:

看看这个简单的 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 变量和事件。

假设以下是 iFrame 的 HTML:

<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 的事件/variables/etc 保持不变?

【问题讨论】:

之前(很久以前)问过 -> ***.com/questions/2885504/… 和 ***.com/questions/3029871/… Mozilla 提出的当前错误 ...bugzilla.mozilla.org/show_bug.cgi?id=254144 @ManseUK:其他问题并没有真正帮助......但这个错误很有趣。 @SalmanA 如何让父母“围绕”孩子? @denodster 当我打开这个问题时,想要的效果是改变元素列表的顺序,每个元素都带有inline-block 显示......在这一点上,我猜一个通用的考虑到他人的利益,解决方案(或实际上是不可能的声明)将是最合适的。 【参考方案1】:

如果不重新加载,就无法将 iframe 从 dom 中的一个位置移动到另一个位置。

下面是一个示例,说明即使使用原生 JavaScript,iFrame 仍会重新加载: http://jsfiddle.net/pZ23B/

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

【讨论】:

好吧,如果 iFrame 重新加载,那么它不是一个选项。 对,这个例子的目的是展示即使使用原生 JavaScript,iFrame 仍然会重新加载。 嗯,实际上,我真的不在乎它是否重新加载,因为它们的位置是about:blank,但我关心的是丢失框架内的内容。 如果它重新加载,会导致它丢失 iframe 中的内容。我想您可以在使用 jquery $("#iframeid").contents() 移动 iframe 之前获取 iframe 中的内容,但是这不会获取存储的 javascript 数据,它必须由 iframe 发送到父页面。(或者父页面必须从 iframe 中抓取) DelightedD0D,赏金是由@djechlin 打开的——“我想知道这在 2016 年是否仍然如此”,看看过去 5 年在这个问题上是否有任何变化。您可以查看我的回答中关于这些年的变化的摘要。【参考方案2】:

这个答案与@djechlin 的赏金有关

在 w3/dom 规范上进行了大量搜索,但没有找到任何明确说明 iframe 在 DOM 树中移动时应该重新加载的内容,但是我确实在 webkit 的 trac/bugzilla 中找到了很多引用和 cmets /microsoft 关于这些年来不同的行为变化。

我希望有人能找到有关此问题的任何具体信息,但现在这里是我的发现:

    根据Ryosuke Niwa - “这是预期的行为”。 有一个“神奇的 iframe”(webkit, 2010),但它是 removed in 2012。 根据 MS -“iframe resources are freed when removed from the DOM”。当您 appendChild(node) 现有节点时 - 首先从 dom 中删除 node。 这里很有趣 - IE&lt;=8 没有重新加载 iframe - 这种行为(有点)新(自 IE&gt;=9 以来)。

    根据Hallvord R. M. Steen的评论,这是iframe specs的引用

    当 iframe 元素插入到具有浏览上下文的文档中时,用户代理必须创建新的浏览上下文,将元素的嵌套浏览上下文设置为新创建的浏览上下文,然后 处理 iframe “第一次”的属性

    这是我在规范中找到的最接近的东西,但是它仍然需要一些解释(因为当我们在 DOM 中移动 iframe 元素时,我们并没有真正做完整的 remove,即使浏览器使用node.removeChild 方法)。

【讨论】:

将感谢投票者的回复并提供解释。 真的希望在 dom 中有通过引用传递,这样你就可以在多个位置拥有相同的元素【参考方案3】:

每当附加 iframe 并应用 src 属性时,它都会触发加载操作,类似于通过 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 fiddle 中看到的,这些 order 样式是内联的,以简化 flip 按钮,因此请旋转 iframes

我从这个 *** 问题中找到了解决方案:Swap DIV position with CSS only

希望对您有所帮助。

【讨论】:

第 1 段的赏金并解释它类似于 img 并创建新实体。不是css:P 如此简单有效!非常感谢! 哈利路亚!我花了很长时间才找到这个,这正是我想要做的,只需重新排列框架。【参考方案4】:

如果您已经在页面上创建了 iFrame,并且稍后只需要移动它的位置,请尝试以下方法:

将 iFrame 附加到正文并使用高 z-index 和 top,left,width,height 将 iFrame 放置在您想要的位置。

即使 CSS 缩放也可以在 body 上工作而无需重新加载,这太棒了!

我为我的“小部件”维护两种状态,它要么被注入到 DOM 中,要么使用这种方法注入到主体中。

当其他内容或库将挤压或挤压您的 iFrame 时,这很有用。

轰隆隆!

【讨论】:

【参考方案5】:

不幸的是,HTML DOM 元素的parentNode 属性是只读的。当然,您可以调整 iframe 的位置,但不能更改它们在 DOM 中的位置并保留它们的状态。

查看我创建的这个 jsfiddle,它提供了一个很好的测试平台。 http://jsfiddle.net/RpHTj/1/

单击框以切换值。点击“移动”运行javascript。

【讨论】:

【参考方案6】:

这个问题已经很老了......但我确实找到了一种无需重新加载即可移动 iframe 的方法。仅限 CSS。我有多个带有相机流的 iframe,我不喜欢在交换它们时重新加载它们。因此,我使用了浮动、位置:绝对和一些虚拟块的组合来移动它们,而无需重新加载它们并按需拥有所需的布局(调整大小和所有)。

【讨论】:

听起来是唯一可行的选择。您应该发布您提出的完整解决方案!至少有一些基本代码可以让其他人开始。谢谢!【参考方案7】:

如果您使用 iframe 访问您控制的页面,您可以创建一些 javascript 以允许您的父级通过 postMessage 与 iframe 通信

从那里,您可以在 iframe 中构建登录以记录状态更改,并在移动 dom 之前,将其作为 json 对象请求。

一旦移动,iframe 将重新加载,您可以将状态数据传递到 iframe,iframe 监听可以将数据解析回之前的状态。

【讨论】:

当然,这是很多代码,假设您控制将在框架中显示的所有数据,但从技术上讲仍然不能“在 DOM 中移动时保持状态”【参考方案8】:

PaulSCoder 有正确的解决方案。切勿为此目的操纵 DOM。对此的经典方法是在点击事件中设置一个相对位置并“翻转”这些位置。将 click 事件放在 body 上是不明智的,因为它也会从其他元素中冒泡。

$("body").click(function () 
    var frame1Height = $(frame1).outerHeight(true);
    var frame2Height = $(frame2).outerHeight(true);
    var pos = $(frame1).css("top");
    if (pos === "0px") 
        $(frame1).css("top", frame2Height);
        $(frame2).css("top", -frame1Height);
     else 
        $(frame1).css("top", 0);
        $(frame2).css("top", 0);
    
);

如果您只有非跨域内容,您可以保存和恢复 HTML:

var htmlContent = $(frame).contents().find("html").children();
// do something
$(frame).contents().find("html").html(htmlContent);

第一种方法的优点是,框架继续做它正在做的事情。使用第二种方法,框架被重新加载并再次启动它的代码。

【讨论】:

【参考方案9】:

为了回应@djechlin 在这个问题上的赏金,我已经分叉了@matt-h 发布的 jsfiddle 并得出结论,这仍然是不可能的。

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

【讨论】:

以上是关于如何在 DOM 中移动 iFrame 而不会丢失其状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Jquery DataTable 中添加新行而不会在从一页移动到另一页时丢失新行

在Excel VBA中,如何测试Excel.Range对象变量是否丢失其引用而不会引发运行时错误424 ..?

转换分层 DOM 树 XML 文件:XSLT 能否转换为“扁平化”XML 文件而不会丢失数据?

当我更改其在 DOM 中的位置时,如何停止 iframe 重新加载?

如何扩展 iFrame/将内容从 iFrame 移动到 div? [复制]

iFrame javascript 影响父 DOM