这文章早在2个月前就想写了,我还记得刚入行的时候,无数的人和我说,能别用它就别用,还有无数的人说这货各种难调试,各种复杂,当时自己确实也惧怕过一段时间,暂且不说iframe性能上的种种,抛开性能层面,单就跨域和安全限制与互相调用同步数据那一块,在开始的时候也一直头疼。
经过之前在淘宝的一个项目还有在新公司的一段时间,发现这东西其实,真心不复杂,做点个人的笔记,也分享下经验教训。
首先,从基础说起,开始就怕这货最主要的原因就是根本不知道这货到底是什么。
尽量简洁的表达就是,我们都知道每个窗口都有一个window全局对象,其下会有多个对象其中就有frames对象,好吧,这里给大家写全了。
current window->self,parent,top,window.navigation.frames.location.history.document.screen.其他先不说,单说frames,里面保存了当前window对象中所嵌入的iframe帧数组。嗯哼,分为2种查询形式:frames[i],frames[name];那么好,我们抽象出来,写过css的人肯定知道一个属性z-index.其实这里我们可以把所有iframe,或者说每个window帧,当成z轴上的一个图层片,把0当成最底层,ok。自己想一下就明白了,无论你页面里的iframe又套了多少个iframe或者多级iframe,其实只不过是在每个不同针里叠加这种z轴性质的东西而已了。
其中parent是访问上一层的方法,比如parent.document;其中self和window所指就是当前自身,而top就是快速查找最顶层的方法了。理解到这里,一目了然,iframe无论你怎么套嵌就都不可怕了,我做过最复杂的是4层iframe的深度,其中从第2层开始跨域,从第3层里又跨回来,或者多个外域互相调用【由于淘宝应用的复杂,这种情况有时候真的是挺常见】。
跨域问题下面再说,我会给出多种解决方案。就iframe本身来说,概念上就是这么点东西了。
然后我想说一下,我们经常惧怕的iframe的一些地方,1,自适应高宽,2方法或者类或者数据的共享。
其实解决起来如果明白了他们之间互相的关系,不管跨域不跨域,是完全跨域还是跨子域,都是可以完美解决的。
首先我来说一般的iframe应用,一个iframe挂页面里,几个链接操作切换那个iframe实现局部刷新,很简单,a链接的target对应上iframe的name即可,js都不用写。
然后是我们切换iframe,不同的iframe高度不一样,大家都知道iframe被嵌入页面,高度一开始可以写死,但是我们不知道它变化后的高度了,需要让iframe自身告诉我们它自己的高,再进行2次设置。那么好,这个操作一般是操作高度,我们这里分3种情况来做:
1,同域。2,夸子域。3,完全跨域。
1.同域名的iframe,是没有安全限制的,之间是可以尽情互相访问的,简单来说.写入的iframe那一帧,在onload时,触发一个函数,读自己的高宽,怎么读?在父页面是这样的:
<iframe name=”myiframe” src=”xx”>
window.frames['myiframe'].onload=function(){alert(this.contentWindow.document.offsetHeight);}
暂时不考虑兼容性,就是不同浏览器里的offsetHeight读取是有差异的,单简单举例,就是在父页面,等我的这个iframe load完,我去读他的window对象下的document下的offsetHeight属性而已。
如果从子页面往父页面去读怎么写呢?window.onload=function(){if(parent) parent.alert(document.offsetHeight);}也很简单了,在子页面写一个onload监听,如果他有parent了,就调用parent下的alert方法,弹一个自身的offsetHeight。
嗯,那么跨域了怎么办?首先这里说明,js里怎么判断域的,这里有一个document.domain属性是关键。如果仅仅是xxx.name.com和ooo.name.com互相2个iframe互相引用控制,我们依然按照2个情况方法来举例:
1,父去取子的属性。这个时候如果你依然用上面的contentWindow去读,是被拒绝访问的,因为你们子域名不一样。一个是xxx一个是ooo。那么怎么办。好,我们这个时候在子页面写一句document.domain=’name.com’; ok,这时候父去读子域的时候,子的domain已经被我们改成了和父一样的域,name.com,是不被禁止的,可以自由读或者改了。2,子去读父,方法和道理一样。
所以我们往往在一些站点的全局js里看到会显式的声明一下docuemnt.domain,原因其实也就是这个了。
3,完全跨域。ooo.name1.com和xxx.name2.com2个窗口之间如何交互,这时候你再设置domain也白搭了。解决办法也有,我们可以染过安全限制这块,方法也非常经典,肯定很多人都会了,就是在2个iframe中间创建一个专门的代理页面。
为什么呢?既然ooo.name1.com读不了xxx.name2.com的页面,他只能读xxx.name1.com的帧页面,那么我们在xxx.name2.com里创建一个ooo.name1.com的iframe就好了么。这个页面呢,是和xxx.name2.com平级的一个iframe,他的创建过程是在xxx.name2.com里的脚本完成的,在创建他的时候,我们把src写上我们要传递给最终父的数据或者方法名,之后通过这个代理iframe里的脚本访问他的top,实现跨域的2个窗口通讯。
说的有点绕了。我们这里用代码简单解释一下:
A,B,C 3个页面: A套B,B动态创建C,C和A同域名。A写一个方法,等待被C调用,调用用top的方法。B写一个创建C这个iframe的方法,在动态插入的时候把height或者其他东西写入C的src里。C写一个读自己src的方法,再写一个访问自己top下的页面的方法的方法。。比如重新设置B的iframe标签的高宽~
OK搞定……。我觉得有点基础的人都能看明白了。
数据啊,方法名字啊,同样可以用这种方法传递,比如我们一个页面有多个B-iframe,那么A怎么知道调整哪一个B的高度?这里就需要一些技巧了,解决办法A在创建B的时候写入固定的排序id名,比如iframe-01,02,03,然后C再反回A这个01,02,03,就可以设置相应的B-iframe了。。比较猥琐和累,可是没办法了……
不过其实说白了,理解安全限制在哪一块和iframe之间互相的关系,无论页面放多少页面,都是会有办法解决的了,重在理解。
之后我再说一个关于iframe高度的恶心问题,上面刚才的做法,只是在页面onload的时候一次性设置了高度,可是如果iframe页面里有动态的效果需要随时改变document里面内容的高度呢?
没办法了,如果是你写的交互,你可以在需要改高度的地方都增加一次onload时候的方法,重新设置,如果不是你做的交互,那么如果为了方便,setInterval吧。。= =||
再之后iframe还有一些其他的功效吧。比如持久化的长连接,比如用来异步加载js模块,比如完成异步的表单提交,图片上传,比如没有ajax时候一些替代ajax的猥琐方案,比如用它在ie6里来遮挡select,比如实现富文本编辑器,等等等等了。
不得不说,你总会在各种地方面对它,理解它,不能怕它,而且短时间内它也不可能被淘汰,哪怕它每次被加载都会造成一次服务器端的请求,或者查询,但是目前来看,让它退出html标签舞台还只是个梦想。。
Read More
Comments