iframe跨域导致的高度自适应问题一网打尽

1、 什么是iframe跨域导致的高度自适应问题
网页A中嵌套了iframe页面B, 如果A和B不在同一个域名下,就形成跨域, 两者之间的通信就会受到浏览器的某种限制。 一种十分常见的场景是, A需要知道B的高度值以确定iframe的高度, 由于跨域的限制,A又不能通过dom直接地读到B的高度。 这就是常见的iframe高度自适应问题。
如何界定是否同域呢?只有域名和端口号都相同时,才算是同域。 比如说 www.a.com:80www.a.com:8080 就不是同域。 www.a.comwww.c.com也不是同域 www.a.comwww.a.com才算同域。

2、 桥文件方案
在B页面中嵌套一个与A同域的iframeC。那么, 上述场景中的高度值的传递可以这样进行:
a)B获取到自身的高度值h
b)B将"#h"附加到C的网址后面(即修改hash值)
c)C从自身的hash值中分离出h
d)C通过parent.parent.document. getElementById(id) 来修改A中iframe的高度为h
可见,A是通过C获得B的高度, C就像是在A和B之间搭的一座桥,所以常把C称作桥文件。 桥文件方案是酷讯各站点最常用的跨域方案。
示例:
A页面( www.a.com/index.html)主要代码:
<iframe src=" www.b.com/index.html" id="iframe"></iframe>

B页面( www.b.com/index.html)主要代码
<iframe src=" www.a.com/bridge.html" id="iframe_bridge"></iframe>
<div style="height:80px">test<div>

C页面( www.a.com/bridge.html)主要代码
var iObj = parent.parent.document.getElementById('iframe');
var iObjH = window.location.hash;
var hashValue = iObjH.split("#");
iObj.style.height = (hashValue[1] != 0 ? hashValue[1] : "0") + "px";

另外,还有使用flash/Json等方式实现的跨域, 没有仔细研究过,在此不作介绍。

3、 postMessage+window.name方案
大多数新的浏览器(IE8+/firefox/chrome/ opera等)支持js的postMessage方法, 可以直接在不同域之间通信。ie6/ ie7浏览器则可以使用js轮询自身 window.name的变 化来实现通信。两者结合起来可组成一种跨浏览器的跨域方案。 这在网友三水清的博文中有详细介绍( http://js8.in/752.html)。博文末尾还提供了一个不错的示例( http://js8.in/mywork/crossdomain/xdomain.html)。
这种方案的关键在于 window.name其原理可以用下面一张图来简单说明:
图在附件中
可见,子窗口修改了父窗口的 window.name后, 如包含高度值的信息,父窗口定时查询自身 window.name 是否发生变化,一旦变化,就能获取到高度值。
同理,父窗口修改了子窗口的 window.name后, 子窗口也能通过轮询获得相应的信息。

容易发现,当一个父窗口中包含多个子窗口, 这些子窗口向父窗口传递消息时,都必须修改父窗口的 window.name,这就存在通道冲突。 所以该方案在处理同一页面多个iframe时不能适用。 这就要看下面要介绍的升级版pm+ window.name了。

4、 postMessage+window.name升级版
桥文件的优点是方便易实现, 缺点是难以应对一个项目中存在多个域名之间跨域的情况。
postMessage+ window.name方案一般情况下 可代替桥文件方案,而且能处理多个域名的情况。
如果一个项目中包含多个域名, 而且一个页面中嵌套不同域名的多个iframe时, 上述两种方式都难以处理, 这正是我最近做机票seo页面项目时遇到的情况。 该项目涉及10几个域名,而且有nginx反向代理, 同一个页面中嵌有酒店的iframe,还有GA广告, 一个页面最多出现3个iframe。通过桥文件的方式非常麻烦, 而且不易于项目的维护。

升级版的方案仍然后是利用3中的原理实现的, 只是增加了父窗口对子窗口的流程控制, 使得子窗口的消息是按一个对列来传递的,从而避免了通道冲突。 这种方案支持任意多域名和一个页面中任意数量iframe的情况 。使用这种方案,只需要很简单的两步设置:
第一步、父窗口 www.a.com/index.html加入i frame和pm_parent.js
<iframe width="728" scrolling="no"  src="" link="www.b.com/index.html#ads1" frameborder="0" marginheight="0" marginwidth="0" style="height:0" id="ads1"></iframe>
<iframe width="710" scrolling="no"  src="" link="www.c.oom/index.html#ads2" frameborder="0" marginheight="0" marginwidth="0" style="height:0" id="ads2"></iframe>
<iframe width="728" scrolling="no"  src="" link="http://www.d.com/index.html#ads3" frameborder="0" marginheight="0" marginwidth="0" style="height:0" id="ads3"></iframe>
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="pm_parent.js"></script>

注意:link中的hash值就是iframe的id; iframe的初始height为0, 而且height的display必须为block(默认值)。
pm_parent.js代码如下,KXLY.showAd() 函数中的数组是由iframeid组成的, iframeid不存在时会自动忽略。
(function(win,doc){
	var dt = [],
	usePM = (typeof win.postMessage !== 'undefined'),
	show = function(data){
		data = parseFormat(data);
		var success=false;
		if(data.height && data.ifr_id){
			var height = parseInt(data.height,10);
			var ifr_id = data.ifr_id;
			if(ifr_id == 'hotel_bottom_recommend'){
				height += 48;
			}
			$('#'+ifr_id).css('height',height+'px').show();
			success = true;
		}
		return success;
	},
	parseFormat = function(responseText) {
		var users = [],
		usersEncoded = responseText.split(';'),
		userArray,key,value;
		for (var i = 0, len = usersEncoded.length; i < len; i++) {
		userArray = usersEncoded[i].split(':');
		key = userArray[0] || '';
		value = userArray[1] || '';
		if(!key || !value){
		continue;
		}else{
		users[key] = value;
		}
		}
		return users;
	},
	getIfr = function(id){
		if(id == undefined){
		return '';
		}
		var ifr;
		try{
		var ifr = doc.getElementById(id).contentWindow;
		}catch(e){
		}
		return ifr;
	},
	setSrc = function(ads){
		var adstrue = [];
		for(var i=0,len=ads.length;i<len;i++){
		var $obj = $('#'+ads[i]);
		if($obj.length > 0){
		$obj.attr('src',$obj.attr('link'));
		adstrue.push(ads[i]);
		}
		}
		return adstrue;
	},
	api = {
		showAd : function(ads){
			var len = ads.length;
			if(len < 1){
				return ;
			}
			var adstrue = setSrc(ads);
			if(usePM){
				if (win.addEventListener) {
				win.addEventListener("message",function(e){
				show(e.data);
				},false);
				}else if(win.attachEvent) {
				win.attachEvent("onmessage",function(e){
				show(e.data);
				});
				}
			}else{
				var iframeid = adstrue.pop();
				var ifr = getIfr(iframeid);
				var hash = '',tmp;
				var start = 'ready|'+iframeid;
				win.name = '';
				var interval = setInterval(function(){
				tmp = win.name;
				if(typeof ifr != 'undefined'){
				ifr.name = start;
				}else{
				clearInterval(interval);
				}
				if(tmp !== hash){
				hash = tmp;
				//获得该iframe的高度后给下一个iframe发通知
				if(show(hash)){
				ifr.name = 'end|'+iframeid;
				iframeid = adstrue.pop();
				ifr = getIfr(iframeid);
				start = 'ready|'+iframeid;
				}
				}
				},50);
			}
		}
	}
	win.KXLY = api;
	KXLY.showAd(['ads1','ads2','ads3','ads4']);
})(window,document);

第二步、任何一个iframe页面中,只需要引用同一个pm_client.js即可,pm_client.js代码如下:
(function(win){
	var VERSION="1.0.1";
	var ifr = win.parent,
	ifr_id = location.hash.slice(1);
	if(!ifr_id){
		return ;
	}
	//将obj转换为普通格式:a:b;c:d
	var buildFormat = function(obj){
		var result = [];
		result.push('ifr_id:'+ifr_id);
		for(var p in obj){
			result.push(p+':'+obj[p]);
		}
		result = result.join(';');
		return result;
	},
	getHeight = function(){
		var doc = document;
		var win = doc.defaultView || doc.parentWindow,
		mode = doc.compatMode,
		root = (mode == 'CSS1Compat' ? doc.documentElement:doc.body),
		h = win.innerHeight || 0,
		scrollH = root.scrollHeight;
		scrollH = Math.max(scrollH, h);
		offsetH = root.offsetHeight;
		return (scrollH == h ? offsetH:scrollH);
	},
	api = {
		adjustHeight : function(){
			height = getHeight();
			var obj = {height:height};
			if(win.postMessage){
				ifr.postMessage(buildFormat(obj),'*');
			}else{
				var hash='',tmp;
				var start='ready|'+ifr_id;
				setInterval(function(){
					tmp = win.name;
					if(tmp != hash && tmp == start){
					hash = tmp;
					ifr.name = buildFormat(obj);
					}
				},50);
			}
		}
	}
	window.KXLY = api;
	window.KXLY.VERSION = VERSION;
	KXLY.adjustHeight();
})(window);

来源: http://www.fenglinblog.com/?p=6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值