- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。 8 A3 ]6 F* D5 K
我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。
$ u) x$ x) [. [9 A( w/ X. r* A1 c这篇文章,希望在这两个方面再做一些深入。
I4 O' \# {6 _* h2 o0 q5 h7 M可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
) ^: W; O6 u$ d- |顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。
" R5 j8 G' q5 x1 f3 |) s: f传统做法大致有两个:
. q- {9 _ g o' ~方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。 ; `1 r! M) `' I: ~
方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。
+ F% Z- g5 S+ W5 `$ _% _在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。
- b3 q4 L4 ?) o. F- k两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。
4 L3 x1 N9 T* ~* N4 }1 ^8 p% K如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。 3 A& ]4 u1 N$ l- U) q5 E8 [, U: b
Demo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html * o' q7 Z$ T9 i) h) ~
主页面代码示例: 1 o9 E' i2 @1 p
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> 1 `! j$ B4 i4 |* c2 _9 a- l
<script type="text/javascript">
4 \9 n2 ~. I$ k! X- M) Wfunction reinitIframe(){
# m$ }" @4 i4 M: avar iframe = document.getElementById("frame_content");
; d% h K+ y4 [2 itry{ ! K6 C2 M/ Q ?
iframe.height = iframe.contentWindow.document.documentElement.scrollHeight; ' Y, M* b( l) D- l6 h0 G
}catch (ex){}
# C! H6 N4 u2 g* P}
" G0 P7 e6 [4 q, p! V; N5 A. nwindow.setInterval("reinitIframe()", 200); 1 n4 J8 _1 P3 w0 R# {
</script>一直执行,效率会不会有问题? ) O1 G4 h1 n6 K
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。 4 t. Q% w- w5 g9 i; O9 s* \/ m% [
下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。 ' L1 Q5 ^# V( b% y2 b, X
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例:
8 W( w3 h$ L4 a! x2 X. d @0 s<div><button >Check Height</button></div> $ M/ ~1 @1 g% U' X: q7 w
<script type="text/javascript">
3 |" w3 l6 o7 y8 [+ h% C1 }5 Tfunction checkHeight() {
5 D. {7 n/ C3 Y) N" p5 v: Xvar iframe = document.getElementById("frame_content"); ' U) z! h3 E! v8 X
var bHeight = iframe.contentWindow.document.body.scrollHeight; $ `' H& Z) |$ k i5 ^; Z8 M# ~
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
; p- d. }# A( R" B8 ~7 T8 w% Zalert("bHeight:" + bHeight + ", dHeight:" + dHeight);
# j+ a7 U: B, _6 z' R6 w} : _1 B: L3 y8 V' R- j6 A9 M% G
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
n( A: D9 j3 R6 h8 C0 F9 ~% N8 G$ s: s<div>
) k4 x; V- M, l& q9 e, }8 N<button >Toggle Overlay</button>
4 L, b* d% s3 D' ~) n% @</div>
9 v; I1 n8 Y0 d7 g7 W<div style="height:160px;position:relative">
4 Z8 P! t* d4 R& n<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div> * v4 x k& e G9 ^5 O* V0 A- t
</div>
' p, W! G) I% v0 C* l$ ^/ o<script type="text/javascript">
) Y/ R$ T5 w5 M& R1 nfunction toggleOverlay() { 2 u& O! Y+ G- `2 }- V- Y- e
var overlay = document.getElementById('overlay'); # w% h; s$ ?4 p/ P2 W
overlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
0 _+ z" U; D+ a* V, Q. Y' s}
% e4 R$ c5 f( r</script>下面列出以上代码在各浏览器的测试值:
. K! L! i* U* { x9 d6 p* x(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值) % a! \& _6 t6 t2 ~
/ 层隐藏时 层展开时
! S7 f$ Z% C9 r7 MbHeight dHeight bHeight dHeight
8 k( c! e: n, C' mIE6 184 184 184 303
, o- V/ {6 b5 ^' C9 j, Y6 X8 TIE7 184 184 184 303 1 b7 o4 u/ b- ?
FF 184 184 184 303
# J( @& _: e) dOpera 181 181 300 300 ! q. F* r i! g2 N0 E
Safari 184 184 303 184 9 ?* i, W2 \5 Q6 \ z* a* [' |
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。
5 @/ C8 x& ~3 C/ Q4 }但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了:
" |1 S) b9 D8 }& F# o7 q% j4 Rfunction reinitIframe(){ 1 X0 ~# T( H& S, L: a8 H- S0 z/ q# R
var iframe = document.getElementById("frame_content");
5 e5 }5 ]* {" S6 h( q! i7 ]3 f7 A3 Atry{
3 h7 I: k1 i+ ~9 l0 I4 z$ qvar bHeight = iframe.contentWindow.document.body.scrollHeight; 5 u Q9 p& Q3 n3 a. g
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
3 y& {5 ^: v% O2 F: [" Nvar height = Math.max(bHeight, dHeight); 2 p% D% `" B% B9 ?' L
iframe.height = height; . J, o+ J9 Z8 ^. m
}catch (ex){}
+ |6 E4 O- g) W} 3 {+ P* t9 n. {0 b
window.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 , H9 E6 ]! m7 X" g9 G1 z
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。 8 r/ ~- l0 O. Z, f9 ^) O2 s- e
可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。
, T9 w5 x8 g* F, x" k, V, u' w& t这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。
# c# Z' N% B& [5 }. W1 z9 m在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。
2 E2 q% q& V0 J3 o8 e ]# y7 `这是最终的主页面的代码:
4 ]( g: Y7 h* F0 o4 `<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0" 5 x0 _" P4 f1 |! M/ r
></iframe> # K# k$ |6 T/ i
<script type="text/javascript">
, Y# F! Z& E9 V# ^ Z! c% \function reinitIframe(){ ; o6 a9 ~# n$ H. u1 a) ]1 i0 m
var iframe = document.getElementById("frame_content"); 8 k0 ^( ?8 G' F9 G, e
try{
# T4 r# p, g/ [' [# y( zvar bHeight = iframe.contentWindow.document.body.scrollHeight; ) Y' v' v/ g2 D3 w1 D" K5 T
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
1 A1 q% S1 b- k. C/ gvar height = Math.max(bHeight, dHeight);
! V& C8 m8 @( j+ `) Biframe.height = height;
0 c8 `( R) P3 k" q9 M}catch (ex){}
# D) R7 K9 S( w) o' X! v+ u, r: x/ o}
! O D) T& l5 k& s1 _5 \window.setInterval("reinitIframe()", 200); ( @2 {1 U6 K2 ^ Y4 r3 T7 z5 N
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|