- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。 % _8 Z2 W- h' x( ~
我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。
5 Y5 Q9 O+ I2 F: C0 f( I2 J这篇文章,希望在这两个方面再做一些深入。 " o1 l% {" \3 V$ _* f0 h, f c9 p
可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。 $ _+ V8 W; [( q& i6 T
顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。
& I' ~; S& V8 W7 ?: \传统做法大致有两个:
2 q. z' G% q' h& Y- Q% G+ k8 \& O方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。
# K. J% v5 x; x* H8 ^* i* c: W方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。 $ {- n+ L& O8 D" c9 ^3 v
在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。 p7 P- e; e6 V, q. R$ K. R
两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 $ t j% ]7 p6 M. b" D0 q4 l
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。
. ^5 o3 w" h) w6 j, |/ y8 uDemo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html
/ Y+ W1 s' U- q, \! t主页面代码示例: + {; c. B' a# Z) A' m8 J
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> - G. r; O, L: a' F) K' D( j0 `
<script type="text/javascript"> $ Q% v4 l4 v: D' p
function reinitIframe(){
1 w/ ^4 ^+ q7 w1 F; Mvar iframe = document.getElementById("frame_content");
, Q# x4 }( l* c5 }3 z2 T+ @try{ - M/ r7 |' a3 V2 C: b1 B
iframe.height = iframe.contentWindow.document.documentElement.scrollHeight; 8 B- T; E5 S- v9 Z( y
}catch (ex){} 2 x7 t) l* o4 s/ A4 \! j
} 6 A0 t* F! ]- y& J* _
window.setInterval("reinitIframe()", 200);
8 A1 Q3 V9 j h% S7 y</script>一直执行,效率会不会有问题? / k2 K r: t9 W1 |
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。 + r$ c: b' K# r: f) J& G
下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。
3 U0 b- b9 ?: Z7 G* J7 I4 s6 R<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例:
9 m9 U r: f1 Q<div><button >Check Height</button></div>
7 G) I7 V" E, Z<script type="text/javascript"> : P7 S- ?& `+ f9 S/ K- I
function checkHeight() {
* C, Z3 x$ W2 [& X B* S/ x/ Uvar iframe = document.getElementById("frame_content");
/ G/ h7 L z, {2 _ |2 m, Dvar bHeight = iframe.contentWindow.document.body.scrollHeight;
w7 T$ m0 y; U3 y" U. Uvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
6 U8 d- ^ o( v* v, s7 Kalert("bHeight:" + bHeight + ", dHeight:" + dHeight); ) R0 w" v6 v1 w5 F' B
} 4 S9 a4 v+ t$ H+ q8 e" b. V
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
& C9 A5 s" |4 U* N! r4 c<div> & t& P2 H+ Z; G! z1 j; i' s E! t
<button >Toggle Overlay</button>
( _6 H- n4 o, E/ P# J. m2 `" h</div>
% v" U1 }( V F! E, I<div style="height:160px;position:relative">
/ W: H' B, A* n \ |# Q<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div>
0 o; k# G1 x' l</div> ) p2 _/ Q, d5 e4 ~/ k; E0 o$ l+ H
<script type="text/javascript"> ! B8 J- u1 A" I1 l" q) m
function toggleOverlay() { + C& T" C; f7 A8 n
var overlay = document.getElementById('overlay'); , q1 c( G" E/ j# l) }/ ]
overlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
]! R, C2 u/ m/ F+ I! |! s2 N6 q}
: l# ^+ l9 }% |0 d, v9 {0 H</script>下面列出以上代码在各浏览器的测试值:
2 [! h; M" B+ }6 Q1 G: F! L/ i(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值) ' }! e1 @$ }! V! N
/ 层隐藏时 层展开时 " \# j2 Q8 x8 b/ m) _8 F8 Q) c4 V) M
bHeight dHeight bHeight dHeight
4 R! R- H+ K4 c, sIE6 184 184 184 303 0 h% S4 }/ z0 E- r- d( t3 b
IE7 184 184 184 303 / R/ X% K3 K: D, \/ \: L1 _7 Z
FF 184 184 184 303 - v# |6 C1 j! r$ C) g8 S
Opera 181 181 300 300 : N3 D' ~3 M3 Q) W
Safari 184 184 303 184 4 b# p# m) R* r( k0 o3 V
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。
5 y+ k' H. d! \! S. z( H但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了:
" ^6 g! M) @' ~function reinitIframe(){ / U3 ~' b5 {( p
var iframe = document.getElementById("frame_content"); 0 z5 x* m4 `. T
try{
; F* K0 Z6 v# ?3 Q- h" }4 dvar bHeight = iframe.contentWindow.document.body.scrollHeight;
& `# m0 p" u9 g* uvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; & B. u8 }" v# a
var height = Math.max(bHeight, dHeight); , X, f# q3 Y. Z! z3 M" U
iframe.height = height;
- s% O/ a) y5 L6 l9 _}catch (ex){}
* @) @: R6 y% _} 5 i. k0 o$ ^% ~) k% k- C
window.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 3 Z; _% |& [ h0 u4 k0 b
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。
/ h" n# z9 T/ u- }3 r; P可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。
) G# Z) g4 s0 Y' O: s$ D6 d7 H* q这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。 ; G, d0 K% h- v2 D) S4 p
在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。
" R& m# ?+ J; U这是最终的主页面的代码:
1 e& _$ I& ^/ [$ \% O<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0" ! {9 L) d+ n. y2 Y- T9 u
></iframe> * e' v! l- a/ c* z% f( c
<script type="text/javascript">
5 X/ f. ^; g+ L7 }; d, nfunction reinitIframe(){
' }0 v$ P2 p( e4 W. Z! ^3 |var iframe = document.getElementById("frame_content");
1 N1 m& D- i. Ftry{
/ r7 c2 g1 B3 i% ] U( Gvar bHeight = iframe.contentWindow.document.body.scrollHeight;
; y) t z3 _, k% Lvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
! v5 c [% I* w. x( W. o$ [. z- T8 uvar height = Math.max(bHeight, dHeight);
0 c$ n) T( Z/ Q, l+ Piframe.height = height; # x' d$ P _7 ]1 O! ^
}catch (ex){}
1 W+ {6 K* M0 N# j} , Y0 E6 W3 f: s$ L5 |6 B7 j
window.setInterval("reinitIframe()", 200);
7 V( G+ `3 k" ?) M% _' Y) u8 m! I- ]</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|