- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。
' ?5 q7 ?+ P! r% H( l( ]) L* g我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。 " s$ [" ^* v8 G) t ~5 z& S
这篇文章,希望在这两个方面再做一些深入。 . R5 _4 O2 O3 x/ r! L1 {/ S) x
可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。 ' s6 R( u3 [% @- {8 K
顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。
+ q* }8 V2 A. G0 g4 o传统做法大致有两个: 9 o3 M- z; c( p( F a: z
方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。 1 F- A; [5 R& Y! n6 f+ z- g& |
方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。
9 O% i5 b* |% W( T) W3 q$ b8 `; t在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。
% |# Y$ T: |. n+ u& F+ V0 z3 d两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 7 z ?+ _: e: T! c' E" o- X7 O
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。
$ j& C4 K4 K/ d+ L# F* gDemo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html ; I: j9 q# c x ?8 j
主页面代码示例:
r3 ~+ V A% S! Q w( L<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe>
' ?2 A Q) v" T0 h! o' \. x<script type="text/javascript">
4 |' A6 {5 \ E+ I, x) pfunction reinitIframe(){ 1 g) [3 s9 P; N
var iframe = document.getElementById("frame_content");
t! V8 X# U. Q9 v7 w7 btry{
5 T! ^6 d/ A! e! [ N) ^' Oiframe.height = iframe.contentWindow.document.documentElement.scrollHeight; : [ N0 ~ z1 F+ a: G+ W9 C; G) R
}catch (ex){} . M, B; g! [5 v* v
} ; K7 T) m1 }" O& V
window.setInterval("reinitIframe()", 200);
) }) V5 ~. Z( _$ y7 K& A2 h</script>一直执行,效率会不会有问题? / P& d& u' q# s0 A5 P
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。 ' k, z% d! q8 q U- X8 _5 P7 U
下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。
. {3 T: W: W @/ U9 k<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例:
4 A" `. K6 B7 _# ?% r<div><button >Check Height</button></div>
( f; L4 x) `. a- |<script type="text/javascript">
( o; y4 ]. ]+ B" Y+ N& x, p: Zfunction checkHeight() { 5 G& K% l }( k8 f5 Q8 I2 A
var iframe = document.getElementById("frame_content");
" z: B, o( {7 L! }* Yvar bHeight = iframe.contentWindow.document.body.scrollHeight; 8 h) S9 A, `. t; ~
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
. y0 ?6 v" x( W1 |2 dalert("bHeight:" + bHeight + ", dHeight:" + dHeight); 9 W2 J- Z$ j+ g( ?5 c+ v8 A0 ]
} 5 v; i- U3 W( C2 Y7 W
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例: 2 ]5 Y' A, d% Z# w7 R! T; L5 L( F
<div>
8 Y" a; m# r/ l/ F/ q5 N0 O9 W2 E<button >Toggle Overlay</button>
; f! [0 Q9 ~" J# @9 b; G</div>
$ u. X8 K5 ^4 M8 G% z+ F<div style="height:160px;position:relative">
* v. p% ?4 x+ w# G8 w+ e( O<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div>
' I! s" o4 B) @. X</div>
' m. d$ m' _! a<script type="text/javascript"> 8 C6 A) c0 Z8 l- \
function toggleOverlay() {
+ o' q7 @6 x+ s- rvar overlay = document.getElementById('overlay');
* X$ `' c* y* W7 L$ Joverlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none'; 7 P& Z+ ^8 B" R
} 1 j$ K: F3 S2 J4 ?
</script>下面列出以上代码在各浏览器的测试值:
1 h* ]2 A5 m: {5 w% M" A1 R(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值)
7 V H" J7 i& k6 A! h! o( f: f' r/ 层隐藏时 层展开时
7 U; r# R" A% v/ X# U t$ tbHeight dHeight bHeight dHeight
0 u8 Q5 G0 ^5 E8 x' j8 bIE6 184 184 184 303
# u2 ^- y0 M; i yIE7 184 184 184 303 6 q7 [9 ^( \6 _, U4 ^
FF 184 184 184 303 ' `1 `/ ]' |' E- C1 S
Opera 181 181 300 300 3 n: S- ]8 P! @3 ^+ `
Safari 184 184 303 184 8 L9 N' Y- E* }' b1 S) V# \2 ]
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。
z$ L& x3 O" o1 H但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了: ) c- f) K- N, N% o' X
function reinitIframe(){
# Z: Y) T7 |8 |var iframe = document.getElementById("frame_content"); 0 I( z3 i; S# H9 U" x" c! A
try{ 7 O5 U |$ O* F0 _8 P, H$ j6 |
var bHeight = iframe.contentWindow.document.body.scrollHeight; , y( v- M/ x( v; U( ^' V
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 3 G1 U9 G3 P5 }8 k3 q* k& m+ o& k
var height = Math.max(bHeight, dHeight); 9 q' A( k" u( b0 h9 Q0 Z
iframe.height = height;
5 Y+ k/ S6 ^; @: }! W# N! X}catch (ex){}
2 O* C' r' j* D$ Y}
4 X. x! S6 C: Y/ O: |4 S6 U3 lwindow.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 $ z. T5 z. E4 g$ `7 _7 ?0 H1 @
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。 / |4 ]0 H7 K2 ]* V
可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。 $ q. C7 z) P0 e+ T6 U
这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。
# p8 }0 E8 H; N7 T- _: S2 }1 C在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。 7 {) h' m9 V9 x) m5 V l4 |
这是最终的主页面的代码: : Z) x" F3 O9 `. f# K7 S
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0" $ A& J2 c3 p: r. @
></iframe> " o2 n' \7 Y1 `/ Y5 r) d
<script type="text/javascript">
1 y! ], P# E2 G* u) |! j! B9 Zfunction reinitIframe(){
# x9 m1 Y9 o" S/ |var iframe = document.getElementById("frame_content");
0 m$ w7 b; R6 I* N6 W& q) R% }- Ctry{ . R8 U8 L5 B6 y: @
var bHeight = iframe.contentWindow.document.body.scrollHeight;
6 p; K% N% T8 F6 i; kvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; - M4 C5 N) {& P/ ^4 b4 y! ^
var height = Math.max(bHeight, dHeight);
" `( Y1 V* K: m8 uiframe.height = height; 8 o7 H4 v7 C" _. P1 E
}catch (ex){}
7 S# O: Q8 m/ b} + P% ^2 C8 X; g
window.setInterval("reinitIframe()", 200);
; G, X7 X7 S# `( n$ a7 M</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|