实习不久接到一个任务,在网页中嵌套另外一个工程的网页。本觉得这是垂手可得的一件事情,结果被测试姐姐折腾得够呛。屡次和我谈心说到这个高度固定致使iframe出现滚动条有多么很差看,对于工程总体的影响有多么恶劣。由于跨域的缘由,这个需求被拖了许久,真是很痛苦的一件事。最终在我离开公司以前搞定了这个单。html
这里就把个人研究过程写下来以供你们参考。segmentfault
首先须要了解一下何为同域,何为跨域:跨域
URL | 说明 | 是否容许通讯 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js |
同一域名下 | 容许 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js |
同一域名下不一样文件夹 | 容许 |
http://www.a.com:8000/a.js http://www.a.com/b.js |
同一域名,不一样端口 | 不容许 |
http://www.a.com/a.js https://www.a.com/b.js |
同一域名,不一样协议 | 不容许 |
http://www.a.com/a.js http://70.32.92.74/b.js |
域名和域名对应ip | 不容许 |
http://www.a.com/a.js http://script.a.com/b.js |
主域相同,子域不一样 | 不容许 |
http://www.a.com/a.js http://a.com/b.js |
同一域名,不一样二级域名(同上) | 不容许 |
http://www.cnblogs.com/a.js http://www.a.com/b.js |
不一样域名 | 不容许 |
在最开始,因为没法完成跨域的高度自适应,我只能放弃跨域的状况,只是作了在同域下的高度自适应,就是表格中第一种和第二种状况。浏览器
在同域前提下,如下代码可得到iframe页面的window:服务器
document.getElementById("myframe").contentWindow
得到子页面的window后便可在父页面js操做子页面。若跨域,则此时浏览器会报错。cookie
子页面得到父页面window:app
parent.window
可是仅仅如此并不能得到测试姐姐的承认,后来又仔细研究了跨域的高度自适应。dom
其原理并不复杂,在父页面嵌套子页面,在子页面嵌套与父页面同域的代理页面,由于父页面与代理页面同域,故父页面与代理页面可实现通讯。post
子页面将自身高度定时赋值到代理页面的URL后,代理页面拿到自身URL中的数据传递给父页面,父页面改变子页面所在iframe高度。从而达到iframe的高度跨域自适应。测试
说得比较绕,如下为原理图:
图片来自这里。
固然,要作到跨域高度自适应还有一个前提,你必须能操做子页面,也就是说子页面也是你能控制的,如果想把baidu.com做为子页面而达到高度自适应这个就没法实现了。
在子页面加入如下代码:
/** * midway 页面高度自适应代码 * 在父页面iframe的src地址后加上midway_url做为线索 * 一可实现功能,二亦是实现动态加载js,实现此功能的通用 * midway_url 为须要嵌入目标页面实现功能的js地址 */ ;(function() { var str_midway_url = "midway_url"; //从URL中获取midway_url var midway_reg = new RegExp(str_midway_url + "=([^&]*)(&|$)"); var midway_res = window.location.search.match(midway_reg); //从Cookie中获取midway_url var cookies = document.cookie; var offset = cookies.indexOf(str_midway_url); //若URL中存在,则得到并存入Cookie //若Cookie中存在,则获取 var midway_url = ''; if (midway_res) { midway_url = unescape(midway_res[1]); document.cookie = str_midway_url + "=" + midway_url; } else if (offset != -1) { offset += str_midway_url.length + 1; var end = cookies.indexOf(";", offset); end = (end != -1) ? end : cookies.length; midway_url = cookies.substring(offset, end); } //将目标地址的js引入页面中,实现功能 if (midway_url) { var dom_script = document.createElement("script"); dom_script.src = midway_url; document.body.appendChild(dom_script); }; })();
父页面经过iframe标签引入子页面, 在子页面的URL后加上额外的一个参数midway_url。在子页面的js中加入以上代码,其核心代码为一个判断语句:若url或者cookie中存在此参数,则继续下一步,若不存在则再也不继续。
通常关于iframe跨域的文章都是在子页面直接经过iframe引入代理页面,这样作有几个弊端:
经过URL参数将midway.js传至子页面,子页面以此判断本身被是被引用的。子页面建立<script>标签引入midway.js。
某些页面由于某些关系须要自我刷新,使得URL中的参数丢失,故将midway_url存入cookie中持久化保存。
被引入子页面的midway.js为如下内容:
/** * 这是被子页面引用的js - midway.js * midway.js 必须与 midway.html 在同一目录 */ ;(function() { var midway = { init: function() { var str_midway_id = "midwayAgentPage"; var midway_url = this.getMidwayUrl(); this.createMidwayIfr(str_midway_id, midway_url); this.setHeight2Ifr(str_midway_id, midway_url); }, //在子页面中建立代理iframe,其指向与父页面同域的midway.html createMidwayIfr: function(str_midway_id, midway_url) { var midway_ifr = document.createElement("iframe"); midway_ifr.id = str_midway_id; midway_ifr.src = midway_url; midway_ifr.style.display = "none"; document.body.appendChild(midway_ifr); }, //定时将目标高度值附加到代理Iframe的src地址#以后 setHeight2Ifr: function(str_midway_id, midway_url) { setInterval(function() { //此处获取目标高度,不宜直接取body高度,应在内容以外套一层div置于body内 var target_height = document.getElementsByTagName("div")[0].scrollHeight; var midway_ifr = document.getElementById(str_midway_id); if (midway_ifr) { midway_ifr.src = midway_url + '#' + target_height; } }, 66); }, //从cookie中获得midway_url return http://.../midway.html getMidwayUrl: function() { var str_midway_url = "midway_url"; var cookies = document.cookie; var offset = cookies.indexOf(str_midway_url); var midway_url = ''; if (offset != -1) { offset += str_midway_url.length + 1; var end = cookies.indexOf(";", offset); end = (end != -1) ? end : cookies.length; midway_url = cookies.substring(offset, end); } if (midway_url) { //原midway_url为...midway.js,此处改成...mindway.html return midway_url.slice(0, midway_url.lastIndexOf("/")) + "/midway.html"; } } }; midway.init(); })();
midway.js的主要功能在于根据midway_url,在子页面建立iframe引入midway.html,定时将子页面的实际高度赋值到iframe的src以后。
子页面中被引入的midway.html的代码以下:
<!DOCTYPE html> <html> <head> <title>Midway</title> </head> <body> <script> window.onload = function() { //得到父页面中的iFrame var ifr = parent.parent.document.getElementById('myframe'); //定时从url中获取到目标高度 //并赋予目标iFrame相应高度,实现自适应 setInterval(function() { var h = location.hash ? location.hash.substring(1) : 0; if(h) ifr.style.height = h + 'px'; }, 66); }; </script> </body> </html>
midway.html至关于一个中转站,因为midway.html与父页面同域可互相通讯,故经过midway.html实时改变父页面中iframe的高度。
父页面中关于iFrame的代码以下:
<iframe id="myframe" name="myframe" width=100% height=100% frameborder=0 marginheight=0 marginwidth=0 scrolling="no"> </iframe> <script> ;(function(){ //此处为midway.js的URL //注意:midway.js与midway.html应在同一文件夹 var midway_url = "http://.../midway.js"; //此处将midway.js地址添加到目标页面的URL后 document.getElementById("myframe").src("http://...&midway_url=" + midway_url); })(); </script>
虽然是比较绕,但实现了测试妹妹须要的功能,也算是完美解决了任务。
问题解决的同时,也作了其余的一些研究,也一并写在下面。
如果在同一主域不一样子域的状况下,例如news.a.com与a.com或者news.a.com与blog.a.com之间,能够经过js对于主域的设置来实现相互通讯:
document.domain = "a.com"
如此设置即可使用第一种方法实现两个页面的自由通讯。
window.name 属性可设置或返回存放窗口的名称的一个字符串。利用这个属性作iframe跨域传值传值是很方便的。
具体操做参看这里。
其原理与midway的类似,midway利用一个与父页面同域的代理页面,经过改变其location.hash实现跨域传值,而这里这是经过改变window.name实现跨域传值,本质上都是经过代理iframe做为中介来实现的。
location.hash能传递的数据很是有限。
window.name能够传递更多的数据(大小通常为2M,IE和firefox下能够大至32M左右),数据格式可自定义(JSON)。
有空再谈。
参考:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/name
https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/hash