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

发布于 2024-12-18 19:06:02 字数 1416 浏览 1 评论 0原文

看一下这个简单的 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 的事件/变量/等保持不变?

Take a look at this simple HTML:

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

Let's say I wanted to move the wraps so that the #wrap2 would be before the #wrap1. The iframes are polluted by JavaScript. I am aware of jQuery's .insertAfter() and .insertBefore(). However, when I use those, the iFrame loses all of its HTML, and JavaScript variables and events.

Lets say the following was the iFrame's 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>

In the above code, the variable variableThatChanges would...change if the user clicked on the body. This variable, and the click event, should remain after the iFrame is moved (along with any other variables/events that have been started)

My question is the following: with JavaScript (with or without jQuery), how can I move the wrap nodes in the DOM (and their iframe childs) so that the iFrame's window stays the same, and the iFrame's events/variables/etc stay the same?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

花心好男孩 2024-12-25 19:06:03

至少在某些情况下,带有插槽的 Shadow 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>

At least in some circumstances a shadow dom with slotting might be an option.

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

傲娇萝莉攻 2024-12-25 19:06:03

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

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

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

If you are using the iframe to access pages you control, you could create some javascript to allow your parent to communicate with the iframe via postMessage

From there, you could build login inside the iframe to record state changes, and before moving dom, request that as a json object.

Once moved, the iframe will reload, you can pass the state data into the iframe and the iframe listening can parse the data back into the previous state.

调妓 2024-12-25 19:06:03

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

$("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);

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

PaulSCoder has the right solution. Never manipulate the DOM for this purpose. The classic approach for this is to have a relative position and "flip" the positions in the click event. It's only not wise to put the click event on the body, because it bubbles from other elements too.

$("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);
    }
});

If you only have content that is not cross-domain you could save and restore the HTML:

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

The advantage of the first method is, that the frame keeps on doing what it was doing. With the second method, the frame gets reloaded and starts it's code again.

小矜持 2024-12-25 19:06:03

为了回应 @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);
}

In response to the bounty @djechlin placed on this question, I have forked the jsfiddle posted by @matt-h and have come to the conclusion that this is still not 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);
}
等待我真够勒 2024-12-25 19:06:02

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

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

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

  1. 根据 Ryosuke Niwa< /a> - “这是预期的行为”。
  2. 有一个“神奇的 iframe”(webkit,2010),但它是 2012 年删除
  3. 根据 MS -“从DOM”。当您appendChild(node)现有节点时,该节点首先从dom中删除。
    这里有趣的事情 - IE<=8 没有重新加载 iframe - 此行为(在某种程度上)是新的(自 IE>=9 以来) 。
  4. 根据Hallvord RM Steen评论,这是来自iframe 规范

    <块引用>

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

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

This answer is related to the bounty by @djechlin

A lot of search on the w3/dom specs and didn't find anything final that specifically says that iframe should be reloaded while moving in the DOM tree, however I did find lots of references and comments in the webkit's trac/bugzilla/microsoft regarding different behavior changes over the years.

I hope someone will find anything specific regarding this issue, but for now here are my findings:

  1. According to Ryosuke Niwa - "That's the expected behavior".
  2. There was a "magic iframe" (webkit, 2010), but it was removed in 2012.
  3. According to MS - "iframe resources are freed when removed from the DOM". When you appendChild(node) of existing node - that node is first removed from the dom.
    Interesting thing here - IE<=8 didn't reload the iframe - this behavior is (somewhat) new (since IE>=9).
  4. According to Hallvord R. M. Steen comment, this is a quote from the iframe specs

    When an iframe element is inserted into a document that has a browsing context, the user agent must create a new browsing context, set the element's nested browsing context to the newly-created browsing context, and then process the iframe attributes for the "first time".

    This is the most close thing I found in the specs, however it's still require some interpretation (since when we move the iframe element in the DOM we don't really do a full remove, even if the browsers uses the node.removeChild method).

℡Ms空城旧梦 2024-12-25 19:06:02

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

It isn't possible to move an iframe from one place in the dom to another without it reloading.

Here is an example to show that even using native JavaScript the iFrames still reload:
http://jsfiddle.net/pZ23B/

var wrap1 = document.getElementById('wrap1');
var wrap2 = document.getElementById('wrap2');
setTimeout(function(){
    document.getElementsByTagName('body')[0].appendChild(wrap1);
},10000);
风追烟花雨 2024-12-25 19:06:02

每当附加 iframe 并应用 src 属性时,它都会触发加载操作,类似于通过 JS 创建 Image 标签。因此,当您删除然后追加它们时,它们是全新的实体并且会刷新。它是 window.location = window.location 重新加载页面的方式。

我知道重新定位 iframe 的唯一方法是通过 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 问题中找到了解决方案: 仅使用 CSS 交换 DIV 位置

希望这有帮助。

Whenever an iframe is appended and has a src attribute applied it fires a load action similarly to when creating an Image tag via JS. So when you remove and then append them they are completely new entities and they refresh. Its kind of how window.location = window.location will reload a page.

The only way I know to reposition iframes is via CSS. Here is an example I put together showing one way to handle this with flex-box:
https://jsfiddle.net/3g73sz3k/15/

The basic idea is to create a flex-box wrapper and then define an specific order for the iframes using the order attribute on each iframe wrapper.

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

As you can see in the JS fiddle these order styles are inline to simplify the flip button so rotate the iframes.

I sourced the solution from this StackOverflow question: Swap DIV position with CSS only

Hope that helps.

郁金香雨 2024-12-25 19:06:02

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

将 iFrame 附加到正文并使用高 z-index 和顶部、左侧、宽度、高度将 iFrame 放置在您想要的位置。

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

我为“小部件”维护两种状态,并且使用此方法将其注入到 DOM 中或主体中。

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

繁荣!

If you have created the iFrame on the page and simply need to move it's position later try this approach:

Append the iFrame to the body and use a high z-index and top,left,width,height to put the iFrame where you want.

Even CSS zoom works on the body without reloading which is awesome!

I maintain two states for my "widget" and it is either injected in place in the DOM or to the body using this method.

This is useful when other content or libraries will squish or squash your iFrame.

BOOM!

待天淡蓝洁白时 2024-12-25 19:06:02

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

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

单击该框可切换该值。单击“移动”来运行 JavaScript。

Unfortunately, the parentNode property of an HTML DOM element is read-only. You can adjust the positions of the iframes, of course, but you can't change their location in the DOM and preserve their states.

See this jsfiddle I created that provides a good test bed. http://jsfiddle.net/RpHTj/1/

Click on the box to toggle the value. Click on the "move" to run the javascript.

若水微香 2024-12-25 19:06:02

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

This question is pretty old... but I did find a way to move an iframe without it reloading. CSS only. I have multiple iframes with camera streams, I dont like when they reload when i swap them. So i used a combination of float, position:absolute, and some dummy blocks to move them around without reloading them and having the desired layout on demand (resizing and all).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文