繁体   English   中英

如何在不丢失其 state 的情况下在 DOM 中移动 iFrame?

[英]How to move an iFrame in the DOM without losing its state?

看看这个简单的 HTML:

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

假设我想移动包装,以便#wrap2位于#wrap1之前。 iframe 被 JavaScript 污染。我知道 jQuery 的.insertAfter()和 .insertBefore( .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 的 window 保持不变,iFrame 的事件/变量/等保持不变相同的?

不可能在不重新加载的情况下将 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);

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

在 w3/dom 规范上进行了大量搜索,但没有找到任何最终明确指出在 DOM 树中移动时应重新加载 iframe 的内容,但是我确实在 webkit 的 trac/bugzilla/microsoft 中找到了许多关于多年来不同的行为发生了变化。

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

  1. 根据Ryosuke Niwa - “这是预期的行为”。
  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 属性进行“第一次处理” ”

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

每当附加 iframe 并应用 src 属性时,它都会触发加载操作,类似于通过 JS 创建 Image 标签。 因此,当您删除然后附加它们时,它们是全新的实体,它们会刷新。 它的类型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

我从这个 StackOverflow 问题中获取了解决方案: 仅使用 CSS 交换 DIV 位置

希望有帮助。

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

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

甚至 CSS 缩放也适用于身体而无需重新加载,这太棒了!

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

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

繁荣!

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

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

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

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

为了回应对这个问题的赏金@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);
}

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

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

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

PaulSCoder 有正确的解决方案。 切勿为此目的操纵 DOM。 为此,经典方法是使用相对 position 并“翻转”点击事件中的位置。 将点击事件放在 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);

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

至少在某些情况下,带插槽的影子 dom 可能是一种选择。

 <template> <style>div {outline:1px solid black; height:45px}</style> <div><slot name="a" /></div> <div><slot name="b" /></div> </template> <div id="shadowhost"> <iframe src="data:text/html,<button onclick='this.innerText+=`.`'>.</button>" slot="a" height=40px ></iframe> </div> <button onclick="ifr?slot= (ifr:slot=='a'); 'b'. 'a'.">swap</button> <script> document:querySelector('#shadowhost').attachShadow({mode. 'open'}).appendChild( document;querySelector('template').content ); ifr=document.querySelector('iframe'); </script>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM