- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。
D3 `8 @" s7 y2 X2 L/ @5 |- F8 G我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。
! `- e. F0 w w7 r6 h. r) D R) w这篇文章,希望在这两个方面再做一些深入。 1 \) q: r5 K9 \& }
可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
' B- l; H) e* a顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。 # E5 ]3 x! K7 ?: d
传统做法大致有两个:
: D7 k6 [8 D7 S; c. T9 z0 l/ C& _方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。
' ^0 ?- h- p! _+ k, H. p方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。
' }* i5 D8 G$ q7 X/ `( ]3 t在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。 8 C4 l- T% X2 p; p
两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 1 O! O1 Y, m/ R# _3 q
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。
- ^, J7 u1 ?! u8 M- X( l( D" }2 C5 fDemo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html $ q5 \( b3 ^# s/ Z) E T; o
主页面代码示例:
1 H/ c2 e4 N9 Y# A( Z' ~/ M<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> / R0 A$ ^+ e ]) [* k' l/ U
<script type="text/javascript"> / D$ y5 S9 q% P
function reinitIframe(){ : S2 p0 R# N% {* j" e' m# `
var iframe = document.getElementById("frame_content");
8 {2 O! W: q6 `8 Vtry{ 1 g8 Q; r& X0 O
iframe.height = iframe.contentWindow.document.documentElement.scrollHeight; 4 f; _6 T4 F) I/ h9 Z' q8 d
}catch (ex){} 1 K( F6 I! Y: \: @. J
}
: C* g9 o s0 kwindow.setInterval("reinitIframe()", 200); % J7 u- y2 ^' M
</script>一直执行,效率会不会有问题? 5 W2 D( b0 u/ Q `9 H
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。 3 M1 s& ^" H7 w. P- | p: d4 V
下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。
. v4 G+ c D) }- t- v8 t2 f<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例:
3 z$ s6 f. ?. K$ h1 e<div><button >Check Height</button></div> " n% k R% }2 x6 t2 R5 z
<script type="text/javascript">
& V0 r$ q3 `" T' [) z( c4 l7 z* Xfunction checkHeight() {
( E$ D/ @- o! l/ cvar iframe = document.getElementById("frame_content"); - i- a. [2 p2 m- P) X+ e# ~- z
var bHeight = iframe.contentWindow.document.body.scrollHeight;
+ d0 @- p1 F' F9 A1 i/ V# avar dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
# y1 z9 Q. ^1 l' g* f# L) ~& yalert("bHeight:" + bHeight + ", dHeight:" + dHeight);
, z# O h _" z U) \) n r, n4 G} 9 s# k! R1 f x. l
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
$ K$ {! f5 m) m4 E<div> # P% O( z& {/ {0 M6 \8 H
<button >Toggle Overlay</button> 5 g; K* D- M; Q$ h6 n. N
</div> 0 g" `- u. v& M3 }! n. o3 z5 z+ h
<div style="height:160px;position:relative">
0 s5 i: [% b! V5 f) ~5 R8 T2 r4 q) E4 e<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div> ( L' y+ A8 [) O U! f
</div>
+ y- M1 Y; h7 @! d2 L4 G<script type="text/javascript">
U* X8 X; {8 o" W+ S6 dfunction toggleOverlay() { & ^1 ^ [0 p h1 }
var overlay = document.getElementById('overlay');
# y0 [3 s' J! B/ |overlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none'; ( b9 z4 G& T) l; H; F
} " q3 D- {( k; h; Q
</script>下面列出以上代码在各浏览器的测试值: ' S0 n( }# f" {" q
(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值) 3 |2 `: p% j' [6 p) t
/ 层隐藏时 层展开时
* w4 T/ k! u/ gbHeight dHeight bHeight dHeight
! T+ v; d @3 q% n$ U9 O1 iIE6 184 184 184 303 . Q W3 [' O# J3 \
IE7 184 184 184 303 3 W$ R) j3 S& E! [
FF 184 184 184 303 + j) O8 t8 q6 @5 R* B$ N8 n7 U& T
Opera 181 181 300 300 ; U. g6 t6 c$ Q, n$ h8 ]& b6 n$ v
Safari 184 184 303 184 9 K3 k# V! D4 ]& l! q) {3 x
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。 5 b7 O4 u) K7 y* E3 _7 n
但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了: & s1 N! t/ z0 \ d& x0 [
function reinitIframe(){
- }& Y- s# T2 c! X! r4 Q0 qvar iframe = document.getElementById("frame_content"); 9 I2 a1 y, n& I$ n3 I; w
try{ ' X. U7 ~4 `7 ]# e
var bHeight = iframe.contentWindow.document.body.scrollHeight; ! x# p0 o7 U- [" Y5 M! f9 Q: @
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 2 j! A6 O/ n _: x S) m
var height = Math.max(bHeight, dHeight); 4 B. p4 D* g) v( b6 [3 k2 n4 O
iframe.height = height;
; D, q" i1 a/ z/ ]}catch (ex){} 7 h# O6 h0 i C9 }1 [ F9 }
}
: P& a( ^+ r. [2 E8 a0 a, Iwindow.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。
: z2 B# e8 N9 s1 v如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。 + j' i7 L6 e& e* D: {
可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。 ; y- T/ j1 z0 C( q! r3 j! H
这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。 ' q5 F& m: g9 s0 k$ q0 d
在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。 " s4 j: D+ N; M/ [# A6 e2 n
这是最终的主页面的代码:
) ?+ E! u7 u. x" ^, \ t. w<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0" , ]$ S% L, o8 }+ o0 P* K6 z
></iframe> ! {' {% C( l* O8 C5 N
<script type="text/javascript"> - j# {* t" [* b
function reinitIframe(){ + M2 J5 I0 k1 U) c
var iframe = document.getElementById("frame_content"); 9 E- Z: V( b' c$ s+ z% | w* }1 M
try{
6 a( a1 @) X* d( K, _var bHeight = iframe.contentWindow.document.body.scrollHeight;
- O# Q3 S8 r' \$ rvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; ; H3 O, N- p( T1 X4 e- W, ^# O
var height = Math.max(bHeight, dHeight); $ _! a& ?4 n1 H' i, o5 j
iframe.height = height; 7 z; e8 B) o' k9 A+ K
}catch (ex){}
( s/ ~+ e% t4 g2 U! G' U1 |' V Z} 6 E4 Z3 x6 a! a# h( _+ ~# _
window.setInterval("reinitIframe()", 200); 8 C- M+ O- _ {$ i+ J8 {' S
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|