- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。
* r: c) R4 ]; t0 v0 h我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。
2 p0 p# g) n G. I3 N% ^这篇文章,希望在这两个方面再做一些深入。
8 \1 k8 R a8 Z0 G' e& @可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
: p5 L% p% a2 r6 M- U顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。 , w1 z9 b/ }. i( e6 l
传统做法大致有两个: ! x9 V: n" \7 F [( ^- W
方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。
; q/ N, |/ w) e/ I( S4 G# H1 g方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。 + R* d5 r( A6 o
在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。 & M9 G8 D0 [6 e; q A0 j& Z6 [
两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 ; K S6 K3 l" `! B0 W8 {4 B6 s
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。 ( ]$ T. e0 e) ~7 h6 c- x$ K# Y
Demo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html D G( J' |- U; F8 {" L
主页面代码示例:
?8 @2 K, y$ w1 p" M; w* g<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> & M$ ^5 L5 ]9 v) J* N9 j
<script type="text/javascript">
9 o' g5 u7 I6 \9 u! t3 P' o) H. t. Yfunction reinitIframe(){ " r4 O' Z/ l/ [# [, p# w5 x
var iframe = document.getElementById("frame_content");
# L$ l/ g" | A/ u6 U, U8 Jtry{
) @: x/ L$ V5 Y: s# oiframe.height = iframe.contentWindow.document.documentElement.scrollHeight;
7 K# I, Z+ l) V3 Y) s! o- u}catch (ex){} , z* H! N# I3 O8 t4 ]+ [
}
* j% o- k5 f5 p- z C7 [3 ewindow.setInterval("reinitIframe()", 200); - K6 r& C6 z8 v' t7 m4 `
</script>一直执行,效率会不会有问题? & I) o0 `4 L6 [* j' j' {# Y- @- D# ^
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。 4 n) e1 c3 n4 s3 [4 L* K# z+ W9 m
下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。
! Q0 r' u0 t/ p5 h" {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例: " a5 \2 `' i; K+ |; _* K; s5 n
<div><button >Check Height</button></div> & w; [0 j& Q' u! b
<script type="text/javascript">
' r7 r' _4 o. Pfunction checkHeight() {
, ?8 x( L" E; A7 o- Nvar iframe = document.getElementById("frame_content"); 5 v. O9 g5 o0 P; b
var bHeight = iframe.contentWindow.document.body.scrollHeight;
% v2 M, T( M! Q+ B2 ]var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
! g) S# r, o8 ~$ Talert("bHeight:" + bHeight + ", dHeight:" + dHeight); 1 a* r3 ^' E9 ^
}
% C; B5 S6 ?- @, c+ Z</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例: 2 e6 A6 z# r$ f0 h: l
<div>
; H3 u) ?7 C, u8 |<button >Toggle Overlay</button>
: u4 i- u* r3 Q7 G$ b5 U</div>
- I o6 N6 x! M& i/ o; N6 U, G<div style="height:160px;position:relative">
2 u, V4 y$ C ~5 Z<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div> 4 O4 h/ z, Y2 P* t) F
</div>
% `4 o7 g1 v# X; ^7 v, ~<script type="text/javascript"> 7 T& x- K) P, k2 T1 @! K
function toggleOverlay() {
0 T1 q5 ]" W, J% lvar overlay = document.getElementById('overlay');
7 U, p" H$ Y7 j+ S; C9 v+ xoverlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
0 y5 {- W9 {6 K3 \ I}
0 f$ U( g. J/ n6 G- y+ Z! `% V</script>下面列出以上代码在各浏览器的测试值:
~. A! b( B9 w' f4 H* Z5 v(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值) 1 ~5 q# i1 y, @# b- F( [
/ 层隐藏时 层展开时 0 W% b/ m6 J% j" ?
bHeight dHeight bHeight dHeight
% o a& K6 \1 p, zIE6 184 184 184 303
4 v# f) ]; u# N& Y# G0 HIE7 184 184 184 303
# r$ e8 S' D# w! {. NFF 184 184 184 303
* p' p/ k7 L) m- X) ~: j2 T5 e! _3 g/ WOpera 181 181 300 300
% F7 l- l4 y& R |( [( y) NSafari 184 184 303 184 9 ^! \% j, n( B+ h: u! S0 u
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。 2 A( N9 c& j$ l- c# W
但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了:
8 C1 j+ |2 k) T; }; ?function reinitIframe(){ : r" W% g- u2 y. E* Q- d# E
var iframe = document.getElementById("frame_content");
0 Z: M. T; ?$ ltry{
/ o' ]! k; M3 Q3 Wvar bHeight = iframe.contentWindow.document.body.scrollHeight;
2 [) T' Q9 g2 U7 Gvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
" D& A3 h# U/ bvar height = Math.max(bHeight, dHeight);
9 N. q$ G0 ]- I& jiframe.height = height;
9 V5 k! r. | x3 e2 ~}catch (ex){} 5 a* q& w7 C, G* N
} : F3 X6 W5 ]' `, L8 l
window.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 ( ?8 v9 M* r4 ~! E1 L- P8 e
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。
0 ^$ M+ i% ?/ F7 ~8 d% j可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。 5 U' Y; c) R8 h- _
这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。
; ]# a- @5 ?* y8 S3 t9 M b在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。 / t6 j; i( C' D. m
这是最终的主页面的代码:
, }; R% G8 R* ~; E% K6 o, G<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0" , j; t2 s7 g; t% U! ~7 Q
></iframe> 1 r5 i% R9 Y; F- M$ r: N& } }- Z7 U
<script type="text/javascript"> 1 R: L+ T0 [5 A3 ^
function reinitIframe(){
5 R* h. u! k# n7 U+ qvar iframe = document.getElementById("frame_content");
0 U x9 Q0 s9 Z8 b0 wtry{
6 t5 R1 |5 [1 y) O1 ~var bHeight = iframe.contentWindow.document.body.scrollHeight;
$ Y4 |2 p* \) `/ t) z* pvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
; _" V5 ?$ E0 N# o) Nvar height = Math.max(bHeight, dHeight); 1 _. W# |2 X$ N- u
iframe.height = height; 7 u8 T% j" s% j# R, G% ]9 @1 w5 j
}catch (ex){}
- i y- C3 `' g) G. E}
' u+ i4 l5 M* ?window.setInterval("reinitIframe()", 200);
g0 d5 c6 e# Y$ z: j' r' x5 w, L- v</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|