如何在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()
。然而,当我使用这些,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
将…如果用户单击正文,则更改。这个变量和click事件应该在iFrame被移动后保留(以及已经启动的任何其他变量/事件)
我的问题是:与JavaScript(有或没有jQuery),我怎么能移动包装节点在DOM(和他们的iframe子),使iframe的窗口保持不变,和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规范上进行了大量的搜索,并没有找到任何最终的具体说明iframe应该在dom树中移动时重新加载,但是我确实在webkit的trac/bugzilla/microsoft中找到了许多关于多年来不同行为变化的参考和评论。
我希望有人能找到关于这个问题的任何具体的东西,但现在这里是我的发现:
- 根据Ryosuke Niwa -"这是预期的行为"。
- 有一个"魔法iframe"(webkit, 2010),但它在2012年被删除。
- 根据MS -"iframe资源被释放时从DOM中删除"。当你的
appendChild(node)
的现有节点-node
是首先从dom中删除。
有趣的是——IE<=8
没有重新加载iframe——这个行为是(有点)新的(自IE>=9
以来)。 -
根据Hallvord R. M. 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中看到的,这些order
样式是内联的,以简化flip
按钮,所以旋转iframes
。
我从这个StackOverflow问题中找到了解决方案:只使用CSS交换DIV位置
希望有帮助。
如果您已经在页面上创建了iFrame,只是需要稍后移动它的位置,请尝试以下方法:
将iFrame附加到body,并使用高z-index和top,left,width,height来放置iFrame。
甚至CSS缩放也可以在不重新加载的情况下在主体上工作,这真是太棒了!
我为我的"小部件"维护了两种状态,它要么被注入到DOM中,要么使用这个方法注入到主体中。
当其他内容或库将挤压或挤压您的iFrame时,这是有用的。
繁荣!
不幸的是,HTML DOM元素的parentNode
属性是只读的。当然,您可以调整iframe的位置,但是您不能改变它们在DOM中的位置并保留它们的状态。
查看我创建的这个jsfiddle,它提供了一个很好的测试平台。http://jsfiddle.net/RpHTj/1/
点击方框来切换值。点击"move"来运行javascript
这个问题很老了…但我确实找到了一种方法来移动iframe而不重新加载。CSS。我有多个iframe与相机流,我不喜欢当他们重新加载时,我交换他们。所以我使用了float, position:absolute和一些虚拟块的组合来移动它们,而不需要重新加载它们,并根据需要进行所需的布局(调整大小等)。
至少在某些情况下,带插槽的阴影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>
如果你使用iframe访问你控制的页面,你可以创建一些javascript来允许你的父类通过postMessage与iframe通信
从那里,你可以在iframe中构建login来记录状态变化,在移动dom之前,请求它作为一个json对象。
一旦移动,iframe将重新加载,您可以将状态数据传递给iframe,并且iframe侦听可以将数据解析回以前的状态。
PaulSCoder有正确的解决方案。永远不要为此目的而操纵DOM。经典的方法是有一个相对位置和"翻转";单击事件中的位置。把click事件放在主体上是不明智的,因为它也会从其他元素中冒泡出来。
$("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);
第一个方法的优点是,框架继续做它正在做的事情。使用第二个方法,帧被重新加载并再次启动它的代码。
为了回应@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窗体在新窗口中打开
- 将样式表插入iframe
- 事件和状态
- 在Twitter上用ie9中的空白src访问iframe的contentWindow
- 获取选择框的状态
- Internet Explorer:可以't访问IFrame内容,除非状态代码为200
- 如何使用 iframe.src 下载文件时获取返回状态代码
- 保留 iframe 页面的状态
- jquery如何在每次iframe更改状态时收集iframe的内容
- 我们如何从dojo.io.iframe.send调用中提取状态代码?
- 通过Javascript在各种浏览器窗口/选项卡中维护iFrame页面的状态
- 如何在DOM中移动iFrame而不丢失其状态
- 从iframe改变父元素的状态
- 如何在javascript中捕捉母版页Iframe中的鼠标空闲状态
- 在IFRAME中动态更新JavaScript后重置状态
- IE11在iFrame的Ajax POST操作期间返回状态0 (XMLHttpRequest: Network Erro
- 使用Rails上传AJAX文件后检查iframe状态
- 在iPad上播放playVideo()命令后,YouTube iFrame API处于缓冲状态
- iframe和Chrome气泡状态栏可以做些什么
- 使用YouTube Iframe API内部的jquery插件来获取状态