标签:
实习不久接到一个任务,在网页中嵌套另一个工程的网页。本以为这是轻而易举的一件事情,结果被测试姐姐折腾得够呛。多次和我谈心说到这个高度固定导致iframe出现滚动条有多么不好看,对于工程整体的影响有多么恶劣。因为跨域的原因,这个需求被拖了许久,真是很痛苦的一件事。最终在我离开公司之前搞定了这个单。
这里就把我的研究过程写下来以供大家参考。
首先需要了解一下何为同域,何为跨域:
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操作子页面。若跨域,则此时浏览器会报错。
子页面获得父页面window:
parent.window
但是仅仅如此并不能获得测试姐姐的认可,后来又仔细研究了跨域的高度自适应。
其原理并不复杂,在父页面嵌套子页面,在子页面嵌套与父页面同域的代理页面,因为父页面与代理页面同域,故父页面与代理页面可实现通信。
子页面将自身高度定时赋值到代理页面的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> $(document).ready(function() { //此处为midway.js的URL //注意:midway.js与midway.html应在同一文件夹 var midway_url = "http://.../midway.js"; //此处将midway.js地址添加到目标页面的URL后 $("#myframe").attr("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
标签:
原文地址:http://www.cnblogs.com/1984s/p/5308177.html