iframe使用于移动端其实不少,只是可见的iframe很少。以往我都是用iframe来实现无刷新登录和跨域通信,用来显示内容的地方就是登录那一块。而对于登录来说,登录界面无非,3-4个输入框和1~2个按钮,手机一屏内完全可以显示,所以其实对iframe显示多内容的问题没有过多研究,只知道有以下特性:
1.android 2.3的原生浏览器下iframe不仅不可以被控制高度,它同时不会被它外围的div的overflow:hidden 剪裁!
最近由于微信禁止了分享接口的任意调用,需要在规定的域名内才可以调用分享接口。因为公司有公众号,可以正常调用接口,但是测试环境的域名却在允许之外,为保持测试的质量,想通过iframe嵌套的方式实现分享接口调用(其实也可以实现其它接口的调用,同时也可以实现手Q的分享接口调用)。
我的思路如下:
测试的结果是,接口是可以完美调用的。不过,却意外发现一个问题。在ios下,iframe内的页面不能滚动(也不存在滚动条)!!!!
如果只是针对轻APP的H5页是可以完美兼容的,但是对于普通带滚动条的页面来说却是个坏消息。需要写个应对策略。首先,要弄清楚,iframe对页面的实际影响有多大,会不会对iframe的所有元素的内部滚动都造成影响了,如果没有没有,就可以考虑把滚动的任务将于节点body完成,而非window完成。
测试的结果是,iframe只对iframe的contentwindow产生不可滚动不带滚动条的影响,而不对contentwindow内的元素造成任何影响。这是个好消息,我可以通过以下代码实现在ios下iframe页面也可以滚动了:
html,body{width: 100%; height: 100%; overflow: hidden;}
body{-webkit-overflow-scrolling:touch; overflow-y:scroll;}
有了以上代码基本上就解决了ios下iframe不能滚动的问题了。不过,这里会有一个问题,就是如果iframe页面里使用了window.onscroll事件的话,这个onscroll事件会失效。所以需要写一个统一方法来实现在iframe页面下window.onscroll事件可以被激活,其实,只要判断条件,重写一下window.onscroll事件即可
,以下是重写onscroll的代码段
if(top!=window){
//处于iframe中
if(navigator.userAgent.indexOf('AppleWebKit')>=0){
//ios系统,需要兼容一下window没有滚动条和不能滚动的问题
var iosStyle=document_createElement_x_x_x_x_x_x_x('style');
iosStyle.setAttribute('type','text/css');
iosStyle.innerHTML='\
html,body{width: 100%; height: 100%; overflow: hidden;}\
body{-webkit-overflow-scrolling:touch; overflow-y:scroll;}\
';
document.getElementsByTagName_r('head')[0].a(iosStyle);
//当body.onscroll时,触发window.onscroll
var scrollEv=document_createEvent('HTMLEvents');
scrollEv.initEvent('scroll',true,true);
document.body.addEventListener('scroll',function(e){
window.dispatchEvent(scrollEv);
window.onscroll&&window.onscroll();
});
}
}
以上代码可以实现document.body.onscroll时,触发window.onscroll事件。不过,很可惜,测试时发现document.body,document.documentElement对象的scrollLeft,scrollTop属性无论怎么读写结果都是0(非iframe下也有这个问题)。对于,需要判断document.body.scrollTop的滚动加载来说,这是个问题。
造成,document.body.scrollTop和document.documentElement.scrollTop读写为0的原因恰恰是:
html,body{width: 100%; height: 100%; overflow: hidden;}
body{-webkit-overflow-scrolling:touch; overflow-y:scroll;}
其实,主要原因是:html,body{width: 100%; height: 100%; overflow: hidden;}
可以写一个长页面,然后加上上述的样式,你会发现,window.onscroll永远不会被触发。
继续观察,还会发现一个有意思的地方,如下代码
html,body{width: 100%; height: 100%; overflow: hidden;}
body{-webkit-overflow-scrolling:touch; overflow-y:scroll;}
.tt{position:absolute; left:0; top:1000px;}
window.οnscrοll=function(){
console.log(document.body.scrollTop);
}
setTimeout(function(){document.body.scrollTop=300},3000);
当sto触发scrollTop赋值时,window.onscroll事件会被触发两次,可以观测到的数字如下:
300
0
但是观察的情况是看不到网页有跳动,这说明一个问题,就是当html的样式被设置为height:100%; overflow:hidden;时,ios是强制性地让document.body.scrollTop值保持0,一旦外部js改变document.body.scrollTop值时,onscroll事件就会被触发,但是ios不会执行滚动而只是将值由非零改为0,这个时候因为document.body.scrollTop值的改变,window.onscroll事件再试被触发,这时,document.body.scrollTop保持在0,window.onscroll事件不被触发。这就是上面观测到的window.onscroll被触发两次的根本原因。
要改变上述情况的办法是,把html属性的overflow:hidden;去掉即可,因为body那里设置也height:100%; overflow:hidden; overflow-y:scroll;了,html的overflow:hidden;本身就是多余的,即:
html{width: 100%; height: 100%;}
body{width: 100%; height: 100%; overflow: hidden;-webkit-overflow-scrolling:touch; overflow-y:scroll;}
但是这种方法只能解决在非iframe环境下的问题,在iframe下,用上述样式,页面无法滚动。来分析原因,先在页面中加入
setTimeout(function(){
alert(document.documentElement.clientHeight);
},3000);
观察输出会发现,输出的数字远大于手机屏幕的高度,document.body.clientHeight和document.body.scrollHeight是一样大的,这表明了,html和body都没有滚动条存在。因为iframe是用iframe元素来表示原来用浏览器窗口的window,浏览器的window会强制document.documentElement.clientHeight为可见区域的高度,而iframe就没有这个限制了,它可以是大于0的任意值。解决方案目前是有一个,不过是我最不喜欢的一个方案,把body节点内的所有节点都放在一个div中,
html,body{width: 100%; height: 100%; overflow: hidden;}\
.DOMWrap{width: 100%; height: 100%; -webkit-overflow-scrolling:touch; overflow-y:scroll;}
js代码如下:
if(top!=window){
//处于iframe中
if(navigator.userAgent.indexOf('AppleWebKit')>=0){
//ios系统,需要兼容一下window没有滚动条和不能滚动的问题
var iosStyle=document_createElement_x_x_x_x_x_x_x('style');
iosStyle.setAttribute('type','text/css');
iosStyle.innerHTML='\
html,body{width: 100%; height: 100%; overflow: hidden;}\
.DOMWrap{width: 100%; height: 100%; -webkit-overflow-scrolling:touch; overflow-y:scroll;}\
';
document.getElementsByTagName_r('head')[0].a(iosStyle);
//因为给document.body.scrollTop赋值时,window.onscroll会被触发,所以createEvent可以不用了
// var scrollEv=document_createEvent('HTMLEvents');
//scrollEv.initEvent('scroll',true,true);
DOMWrap.addEventListener('scroll',function(e){
document.body.scrollTop=this.scrollTop;
//window.dispatchEvent(scrollEv);
//window.onscroll&&window.onscroll();
});
}
}
以上代码可以解决ios下的iframe滚动和iframe里的window.onscroll事件兼容问题。不过,还是希望可以不用到DOMWrap这个节点,直接在html,body上就可以完成的方式。--在ios5下不行,因为document.body.scrollTop永远为0
为了更深入了解iframe在ios和android下的显示行为,我写了一个单例来观测记录iframe的各位行为,以下是观测的记录:
http://blog.sina.com.cn/s/blog_86acf48e0102vhg7.html
通过这个观测记录的总结,竟然得出一个结果:通过createEvent可以触发window.onscroll事件,但是却没办法强改 document.body.scrollTop的值!所以带document.body.scrollTop判断的内嵌页会出问题(ios8还好,因为document.body.scrollTop会有数值0->x->0变动)。
虽然没办法做出一个完全兼容的iframe内嵌组件,但是,如果一开始就知道自己做的是一个内嵌页面,那么上述问题就可以解决了,只需要把document.body.scrollTop或window.scrollY改一下就可以了。像如果是使用一个DIV把所有元素包括进去的写法,只需要把document.body.scrollTop改成DOMWrap.scrollTop即可。