Web前端之iframe详解
iframe基本内涵
通常我们使⽤iframe直接直接在页⾯嵌套iframe标签指定src就可以了。
<iframe src="demo_iframe_sandbox.htm"></iframe>
但是,有追求的我们,并不是想要这么low的iframe. 我们来看看在iframe中还可以设置些什么属性
iframe常⽤属性:
1.frameborder:是否显⽰边框,1(yes),0(no)
2.height:框架作为⼀个普通元素的⾼度,建议在使⽤css设置。
3.width:框架作为⼀个普通元素的宽度,建议使⽤css设置。
4.name:框架的名称,window.frames[name]时专⽤的属性。
5.scrolling:框架的是否滚动。yes,no,auto。
6.src:内框架的地址,可以使页⾯地址,也可以是图⽚的地址。
7.srcdoc , ⽤来替代原来HTML body⾥⾯的内容。但是IE不⽀持, 不过也没什么卵⽤
8.sandbox: 对iframe进⾏⼀些列限制,IE10+⽀持
上⾯⼀些tag,会在下⽂进⾏穿插说明,单个不好说。
我们通常使⽤iframe最基本的特性,就是能⾃由操作iframe和⽗框架的内容(DOM). 但前提条件是同域. 如果跨域顶多只能实现页⾯跳转window.location.href.
那什么是同域/ 什么是跨域呢?
就是判断你的url⾸部是否⼀样,下⾯会有讲解,这⾥只是提及。
同域不同域的问题:
A:<iframe id="mainIframe" name="mainIframe" src="/main.html" frameborder="0" scrolling="auto" ></iframe>
B:<iframe id="mainIframe" name="mainIframe" src="www.baidu" frameborder="0" scrolling
="auto" ></iframe>
使⽤A时,因为同域,⽗页⾯可以对⼦页⾯进⾏改写,反之亦然。
使⽤B时,不同域,⽗页⾯没有权限改动⼦页⾯,但可以实现页⾯的跳转
这⾥,我们先从简单的开始,当主页⾯和iframe同域时,我们可以⼲些什么。
获取iframe⾥的内容
主要的两个API就是contentWindow,和contentDocument
这两个API只是DOM节点提供的⽅式(即getELement系列对象)
var iframe = ElementById("iframe1");
var iwindow = tWindow;
var idoc = iwindow.document;
console.log("window",iwindow);//获取iframe的window对象
console.log("document",idoc);  //获取iframe的document
console.log("html",idoc.documentElement);//获取iframe的html
console.log("head",idoc.head);  //获取head
console.log("body",idoc.body);  //获取body
另外更简单的⽅式是,结合Name属性,通过window提供的frames获取.
<iframe src ="/index.html" id="ifr1" name="ifr1" scrolling="yes">
<p>Your browser does not support iframes.</p>
</iframe>
<script type="text/javascript">
console.log(window.frames['ifr1'].window);
console.ElementById("ifr1").contentWindow);
</script>
其实window.frames['ifr1']返回的就是window对象,即
window.frames['ifr1']===window
这⾥就看你想⽤哪⼀种⽅式获取window对象,两者都⾏,不过本⼈更倾向于第⼆种使⽤frames[xxx].因为,字母少啊喂~ 然后,你就可以操控iframe⾥⾯的DOM内容。
在iframe中获取⽗级内容
同理,在同域下,⽗页⾯可以获取⼦iframe的内容,那么⼦iframe同样也能操作⽗页⾯内容。在iframe中,可以通过在window上挂载的⼏个API进⾏获取.
window.parent 获取上⼀级的window对象,如果还是iframe则是该iframe的window对象

window.self 返回⾃⾝window的引⽤。可以理解 window===window.self(脑残)
iframe的轮询
话说在很久很久以前,我们实现异步发送请求是使⽤iframe实现的~!
怎么可能
真的史料为证(⾃⾏google), 那时候为了不跳转页⾯,提交表单时是使⽤iframe提交的。现在,前端发展真快,websocket,SSE,ajax等,逆天skill的出现,颠覆了iframe, 现在基本上只能活在IE8,9的浏览器内了。但是,宝宝以为这样就可以不⽤了解iframe了,⽽现实就是这么残酷,我们⽬前还需要兼容IE8+。所以,iframe 实现长轮询和长连接的trick 我们还是需要涉猎滴。
iframe长轮询
如果写过ajax的童鞋,应该知道,长轮询就是在ajax的readyState = 4的时,再次执⾏原函数即可。这⾥使⽤iframe也是⼀样,异步创建iframe,然后reload, 和后台协商好, 看后台哥哥们将返回的信息放在,然后获取⾥⾯信息即可. 这⾥是直接放在body⾥.
var iframeCon = docuemnt.querySelector('#container'),
text; //传递的信息
var iframe = ateElement('iframe'),
iframe.id = "frame",
iframe.style = "display:none;",
iframe.name="polling",
iframe.src="target.html";
iframeCon.appendChild(iframe);
var iloc = tWindow.location,
idoc  = tDocument;
setTimeout(function(){
text = ElementsByTagName('body')[0].textContent;
console.log(text);
},2000);
}
这样就可以实现ajax的长轮询的效果。当然,这⾥只是使⽤reload进⾏获取,你也可以添加iframe和删除iframe的⽅式,进⾏发送信息,这些都是根据具体场景应⽤的。另外在iframe中还可以实现异步加载js⽂件,不过,iframe和主页是共享连接池的,所以还是很蛋疼的,现在基本上都被XHR和hard calllback取缔了,这⾥也不过多介绍了。
⾃适应iframe之蜜汁⼴告
⽹页为了赚钱,引⼊⼴告是很正常的事了。通常的做法就是使⽤iframe,引⼊⼴告地址就可以了,然后根据⼴告内容设置相应的显⽰框。但是,为什么是使⽤iframe来进⾏设置,⽽不是在某个div下嵌套就⾏了呢?
要知道,⼴告是与原⽂⽆关的,这样硬编码进去,会造成⽹页布局的紊乱,⽽且,这样势必需要引⼊额外的css和js⽂件,极⼤的降低了⽹页的安全性。这些所有的弊端,都可以使⽤iframe进⾏解决。
我们通常可以将iframe理解为⼀个沙盒,⾥⾯的内容能够被top window 完全控制,⽽且,主页的css样式是不会⼊侵iframe⾥⾯的样式,这些都给iframe的⼴告命运埋下伏笔。可以看⼀下各⼤站点是否都在某处放了些⼴告,以维持⽣计⽐如:
但,默认情况下,iframe是不适合做展⽰信息的,所以我们需要对其进⾏decorate.
⾃适应iframe
默认情况下,iframe会⾃带滚动条,不会全屏.如果你想⾃适应iframe的话:第⼀步:去掉滚动条
<iframe src="./iframe1.html" id="iframe1" scrolling="no"></iframe>
第⼆步,设置iframe的⾼为body的⾼
var iwindow = tWindow;
var idoc = iwindow.document;
iframe.height = idoc.body.offsetHeight;
另外,还可以添加其它的装饰属性:
属性效果
allowtransparency true or false
是否允许iframe设置为透明,默认为false
allowfullscreen true or false
是否允许iframe全屏,默认为false
看个例⼦:
<iframe id="google_ads_frame2" name="google_ads_frame2" width="160" height="600" frameborder="0" src="target.html" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true">你可以直接写在内联⾥⾯,也可以在css⾥⾯定义,不过对于⼴告iframe来说,样式写在属性中,是best pratice.
iframe安全性探索
iframe出现安全性有两个⽅⾯,⼀个是你的⽹页被别⼈iframe,⼀个是你iframe别⼈的⽹页。当你的⽹页被别⼈iframe时,⽐较蛋疼的是被钓鱼⽹站利⽤,然后victim+s留⾔
你。真.简直了。所以为了防⽌页⾯被⼀些不法分⼦利⽤,我们需要做好安全性措施。
防嵌套⽹页
⽐如,最出名的就是使⽤iframe来拦截click事件。因为iframe享有着click的最优先权,当有⼈在伪造的主页中进⾏点击的话,如果点在iframe上,则会默认是在操作iframe的页
⾯。所以,钓鱼⽹站就是使⽤这个技术,通过诱导⽤户进⾏点击,⽐如,设计⼀个"妹妹寂寞了"等之类的⽹页,诱导⽤户点击,但实际结果,你看到的不是"妹妹",⽽是被恶意微
博吸粉。
所以,为了防⽌⽹站被钓鱼,可以使⽤p来防⽌你的⽹页被iframe.
//iframe2.html
if(window != p){
}
这段代码的主要⽤途是限定你的⽹页不能嵌套在任意⽹页内。如果你想引⽤同域的框架的话,可以判断域名。
if (top.location.host != window.location.host) {
  top.location.href = window.location.href;
}
当然,如果你⽹页不同域名的话,上述就会报错。
所以,这⾥可以使⽤进⾏错误捕获。如果发⽣错误,则说明不同域,表⽰你的页⾯被盗⽤了。可能有些浏览器这样写是不会报错,所以需要降级处理。
这时候再进⾏跳转即可.
try{
  top.location.hostname;  //检测是否出错
  //如果没有出错,则降级处理
  if (top.location.hostname != window.location.hostname) {
    top.location.href =window.location.href;
  }
}
catch(e){
  top.location.href = window.location.href;
}
这只是浏览器端,对iframe页⾯的权限做出相关的设置。我们还可以在服务器上,对使⽤iframe的权限进⾏设置.
X-Frame-Options
X-Frame-Options是⼀个相应头,主要是描述服务器的⽹页资源的iframe权限。⽬前的⽀持度是IE8+(已经很好了啊喂)有3个选项:
DENY:当前页⾯不能被嵌套iframe⾥,即便是在相同域名的页⾯中嵌套也不允许,也不允许⽹页中有嵌套iframe
SAMEORIGIN:iframe页⾯的地址只能为同源域名下的页⾯
ALLOW-FROM:可以在指定的origin url的iframe中加载
1. X-Frame-Options: DENY
2. 拒绝任何iframe的嵌套请求
3. X-Frame-Options: SAMEORIGIN
4. 只允许同源请求,例如⽹页为 foo/123.php,則 foo 底下的所有⽹页可以嵌⼊此⽹页,但是 foo 以外的⽹页不能嵌⼊
5. X-Frame-Options: ALLOW-FROM s3131212
6. 只允许指定⽹页的iframe请求,不过兼容性较差Chrome不⽀持
X-Frame-Options其实就是将前端js对iframe的把控交给服务器来进⾏处理。
//js
if(window != p){
}
//等价于
X-Frame-Options: DENY
//js
if (top.location.hostname != window.location.hostname) {
    top.location.href =window.location.href;
}
//等价于
X-Frame-Options: SAMEORIGIN
该属性是对页⾯的iframe进⾏⼀个主要限制,不过,涉及iframe的header可不⽌这⼀个,另外还有⼀个Content Security Policy, 他同样也可以对iframe进⾏限制,⽽且,他应该是
以后⽹页安全防护的主流。
CSP之页⾯防护
和X-Frames-Options⼀样,都需要在服务器端设置好相关的Header. CSP 的作⽤,真的是太⼤了,他能够极⼤的防⽌你的页⾯被XSS攻击,⽽且可以制定js,css,img等相关资源的origin,防⽌被恶意注⼊。不过他的兼容性,也是渣的⼀逼。⽬前⽀持Edge12+ 以及 IE10+。
⽽且⽬前市⾯上,流⾏的是3种CSP头,以及各种浏览器的兼容性
使⽤主要是在后端服务器上配置,在前端,我们可以观察Response Header ⾥是否有这样的⼀个Header:
Content-Security-Policy: default-src 'self'
这就表明,你的⽹页是启⽤CSP的。通常我们可以在CSP后配置各种指定资源路径,有
html document是什么default-src,
script-src,
style-src,
img-src,
connect-src,
font-src,
object-src,
media-src,
sandbox,
child-src,
...
如果你未指定的话,则是使⽤default-src规定的加载策略.
默认配置就是同域: default-src "self".
这⾥和iframe有⼀点⽠葛的就是 child-src 和 sandbox.
child-src就是⽤来指定iframe的有效加载路径。其实和X-Frame-Options中配置allow-origin是⼀个道理。不过,allow-origin 没有得到⼚商们的⽀持。
⽽,sandbox其实就和iframe的sandbox属性(下⽂介绍),是⼀样⼀样的,他可以规定来源能够带有什么权限.
来个demo:
Content-Security-Policy: child-src 'self' example; sandbox allow-forms allow-same-origin
此时,iframe的src就只能加载同域和example页⾯。最后再补充⼀点: 使⽤CSP 能够很好的防⽌XSS攻击,原理就是CSP会默认escape掉内联样式和脚本,以及eval执⾏。但是,你可以使⽤srcipt-src进⾏降低限制.
Content-Security-Policy: script-src 'unsafe-inline'
如果想更深⼊的了解CSP,可以参阅:,,
ok, 上⾯基本上就是防⽌⾃⼰页⾯被嵌套⽽做的⼀些安全防护⼯作。当然,我们⾯临的安全问题还有⼀个,就是当iframe别⼈的页⾯时,我们需要对其进⾏安全设限,虽然,跨域时iframe的安全性会⼤很多,但是,互联⽹是没有安全的地⽅。在以前,我们会进⾏各种trick来防⽌⾃⼰的页⾯被污染,现在h5提供的⼀个新属性sandbox可以很好的解决这个问题。
sandbox
sandbox就是⽤来给指定iframe设置⼀个沙盒模型限制iframe的更多权限.
sandbox是h5的⼀个新属性,IE10+⽀持(md~).
启⽤⽅式就是使⽤sandbox属性:
<iframe sandbox src="..."></iframe>
这样会对iframe页⾯进⾏⼀系列的限制:
1. script脚本不能执⾏
2. 不能发送ajax请求
3. 不能使⽤本地存储,即localStorage,cookie等
4. 不能创建新的弹窗和window
5. 不能发送表单
6. 不能加载额外插件⽐如flash等
看到这⾥,我也是醉了。好好的⼀个iframe,你这样是不是有点过分了。不过,你可以放宽⼀点权限。在sandbox⾥⾯进⾏⼀些简单设置
<iframe sandbox="allow-same-origin" src="..."></iframe>
常⽤的配置项有:
配置效果
allow-forms允许进⾏提交表单
allow-scripts运⾏执⾏脚本
allow-same-origin允许同域请求,⽐如ajax,storage
allow-top-navigation允许iframe能够主导p进⾏页⾯跳转
allow-popups允许iframe中弹出新窗⼝,⽐如,window.open,target="_blank"
allow-pointer-lock在iframe中可以锁定⿏标,主要和有关
可以通过在sandbox⾥,添加允许进⾏的权限.
<iframe sandbox="allow-forms allow-same-origin allow-scripts" src="..."></iframe>
这样,就可以保证js脚本的执⾏,但是禁⽌iframe⾥的javascript执⾏top.location = self.location。
哎,其实,iframe的安全问题还是超级有的。⽐如l等。不过⽬前⽽⾔,知道怎么简单的做⼀些安全措施就over了,⽩帽⼦们会帮我们测试的。
resolve iframe跨域
iframe就是⼀个隔离沙盒,相当于我们在⼀个页⾯内可以操控很多个标签页⼀样。如果踩坑的童鞋应该知道,iframe的解决跨域也是很有套套的。
⾸先我们需要明确什么是跨域。
浏览器判断你跨没跨域,主要根据两个点。⼀个是你⽹页的协议(protocol),⼀个就是你的host是否相同,即,就是url的⾸部:
window.location.protocol +window.location.host
需要强调的是,url⾸部必须⼀样,⽐如:⼆级域名或者IP地址,都算是跨域.
//域名和域名对应ip, 跨域
www.a/a.js
70.32.92.74/b.js
//统⼀域名,不同⼆级域名。跨域
www.a/a.js
a/b.js
对于第⼆种⽅式的跨域(主域相同⽽⼦域不同),可以使⽤iframe进⾏解决。
在两个不同⼦域下(某⼀⽅使⽤iframe嵌套在另⼀⽅),
即:
http: //www.foo/a.html和http: //script.foo/b.html
两个⽂件中分别加上document.domain = ‘foo’,指定相同的主域,然后,两个⽂档就可以进⾏交互。
//b.html是以iframe的形式嵌套在a.html中
//www.foo上的a.html
document.domain = 'foo';
var ifr = ateElement('iframe');
ifr.src = 'script.foo/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
var doc = tDocument || tWindow.document;
// 在这⾥操纵b.html
ElementsByTagName("h1")[0].childNodes[0].nodeValue);
};
//script.foo上的b.html
document.domain = 'foo';
默认情况下document.domain 是指window.location.hostname. 你可以⼿动更改,但是最多只能设置为
主域名。通常,主域名就是指不带www的hostname, ⽐如: foo , baidu 。如果,带上www或者其他的前缀,就是⼆级域名或者多级域名。通过上述设置,相同的domain之后,就可以进⾏同域的相关操作。另外还可以使⽤iframe和location.hash,不过由于技术out了,这⾥就不做介绍了。
H5的CDM跨域与iframe
如果你设置的iframe的域名和你top window的域名完全不同。则可以使⽤CDM(cross document messaging)进⾏跨域消息的传递。该API的兼容性较好 ie8+都⽀持.
发送消息: 使⽤postmessage⽅法
接受消息: 监听message事件
postmessage
该⽅法挂载到window对象上,即,使⽤window.postmessage()调⽤.
该⽅法接受两个参数:postMessage(message, targetOrigin):
message: 就是传递给iframe的内容, 通常是string,如果你想传object对象也可以。不过使⽤前请参考这⼀句话:
意思就是,希望亲爱的不要直接传Object。如果有条件,可以使⽤是JSON.stringify进⾏转化。这样能保证不会出bug.
targetOrigin: 接受你传递消息的域名,可以设置绝对路径,也可以设置""或者"/"。表⽰任意域名都⾏,"/"表⽰只能传递给同域域名。
来个栗⼦:
<iframe src="tuhao" name="sendMessage"></iframe>
//当前脚本
let ifr = window.frames['sendMessage'];
//使⽤iframe的window向iframe发送message。
ifr.postmessage('give u a message', "tuhao");
//tuhao的脚本
window.addEventListener('message', receiver, false);
function receiver(e) {
if (e.origin == 'tuhao') {
if (e.data == 'give u a message') {
e.source.postMessage('received', e.origin);  //向原⽹页返回信息
} else {
alert(e.data);
}
}
}
当targetOrigin接受到message消息之后,会触发message事件。 message提供的event对象上有3个重要的属性,data,origin,source.
data:postMessage传递进来的值
origin:发送消息的⽂档所在的域
source:发送消息⽂档的window对象的代理,如果是来⾃同⼀个域,则该对象就是window,可以使⽤其所有⽅法,如果是不同的域,则window只能调⽤postMessage()⽅法返回信息

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。