- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。
3 r& l7 c: O* k3 ]" m0 w我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。
0 ~8 ]; T/ t6 N1 H0 Q; f7 Y这篇文章,希望在这两个方面再做一些深入。
2 q2 q5 Q5 P8 v( O可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
& p: ^3 r0 V* ?; L" w顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。 # q* I9 n. [& C. }; J2 P9 E2 D
传统做法大致有两个:
) w' j0 J. |2 ^1 B# Z方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。
! E# _" N' ^$ [$ T方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。
9 }& }+ M* T4 @在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。
, u0 b$ C" \! F' ]& t, i9 J5 _两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。
% w3 ]- e- ^1 \+ S( Q1 L如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。
2 p0 k: ^) c, s: G( rDemo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html
1 r# O% [/ H" A! b, R; `. |主页面代码示例:
3 N# i7 W# R5 ~# R% a7 N9 ~0 g<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe>
+ T, K1 t- C6 V. d0 }1 @; Q<script type="text/javascript"> ) a) W, a$ q W, q# |! a
function reinitIframe(){
" N/ P2 p! i$ ]var iframe = document.getElementById("frame_content");
5 |; e/ A) E$ n3 K8 q0 [try{ . b j6 A' H! p
iframe.height = iframe.contentWindow.document.documentElement.scrollHeight;
! ^2 S' x @+ B1 D J! E}catch (ex){} 1 b2 B5 _- U+ b; _9 g6 E
} 1 G( U6 y8 E* A: J4 S/ u
window.setInterval("reinitIframe()", 200);
2 V' G3 `* s2 D" I, {</script>一直执行,效率会不会有问题? 5 V# v1 ^6 P: y
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。
4 V( k$ e. m/ H" b0 H4 V8 W下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。 5 f8 `& I* M4 {) m$ j
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例: ( }0 L* M# B3 P3 W2 G/ ?$ L3 |
<div><button >Check Height</button></div>
1 M' q# ~* \* E1 L; A: w* |" D<script type="text/javascript">
& u1 X$ a7 n! y6 L' o% O( B |function checkHeight() {
5 F. w G+ c0 d4 V4 jvar iframe = document.getElementById("frame_content");
* @: u S, [) Z" F6 z Qvar bHeight = iframe.contentWindow.document.body.scrollHeight;
% g. p R0 M3 Y, l" P. e1 xvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 2 g1 g- q/ p/ r- }% [1 C
alert("bHeight:" + bHeight + ", dHeight:" + dHeight);
# I5 E' Y. S$ I1 j' b} 8 h, N3 T; @4 D% v: l
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
2 X! A5 e0 `* n9 b<div>
1 F7 Y' e# T9 F7 x0 W- O. u$ Q<button >Toggle Overlay</button>
, Q4 ? y/ A& g/ ^! @" U</div> , v' O& }0 X. Y; o" h% ^3 i1 ?6 ~
<div style="height:160px;position:relative"> - }, l) d! u+ L1 c
<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div>
5 I$ o2 X: x+ |3 T1 l! U% Y+ ?</div> 9 E: p/ N3 S9 j. y2 F3 f4 w
<script type="text/javascript"> 1 i* [: b7 b5 D- k
function toggleOverlay() { 9 I" Q. N3 K( m* C, G0 i+ ^$ z
var overlay = document.getElementById('overlay');
+ H$ g9 G3 \1 E' U5 Voverlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
, {/ W" K5 o$ d$ f* N: c}
3 S' J" D/ e O, U! j$ ?</script>下面列出以上代码在各浏览器的测试值: - \# g4 c: f6 _. `2 g
(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值) 7 j' I( h r- ?) Z
/ 层隐藏时 层展开时
( P: h! P8 ?5 y; D. tbHeight dHeight bHeight dHeight
/ h m) L# ~$ `8 w4 ZIE6 184 184 184 303 7 j# N+ _, O# N& K& v5 Y. K% `7 S
IE7 184 184 184 303 + k! n! G4 F* ^" q/ Z$ H
FF 184 184 184 303
- J9 @4 T- ~) n* x% }* NOpera 181 181 300 300 7 h5 z. q) u4 b3 M; o
Safari 184 184 303 184 % u9 Y4 L6 ~4 I
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。
' z( | x. w7 {) L' q8 N _9 K- v. | _但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了: " B" E y+ A* i- K- L M1 \6 S
function reinitIframe(){ + T! {4 g( R9 S5 y
var iframe = document.getElementById("frame_content"); : R6 w" U m% I1 Y V! ]* r. t
try{ + q' R1 `- @% n( w9 D4 X7 x
var bHeight = iframe.contentWindow.document.body.scrollHeight; 1 [# O$ P5 m$ a0 ?8 Q5 P
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
% W, J1 v# y, P& R& G7 @var height = Math.max(bHeight, dHeight);
- c H' m! I" y7 f: niframe.height = height; 2 _+ u5 n* o4 _( [5 j: d
}catch (ex){}
+ N. X5 e" q& r4 [; C}
# D5 i1 M3 L. {- w9 Jwindow.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 7 }. g8 c" Q2 Z) e
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。 9 M0 T% c7 d5 ~2 a2 |
可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。 ) j* {2 r2 i5 x" C
这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。
& h7 Z* {. G# y4 l# Q- K R# Y在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。 . r j8 @, x, X$ q, t
这是最终的主页面的代码: * r3 B% v9 w5 l4 L9 T# F* w' x
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"
' S/ U9 v! D% o1 t( J></iframe> ! s0 A& b) z6 A
<script type="text/javascript"> # ^% h. ]3 }. }
function reinitIframe(){
B' S) v% g, P& c! ?var iframe = document.getElementById("frame_content"); 8 }5 ^8 X4 d, C$ B( B
try{ & s: C; C0 K1 Z
var bHeight = iframe.contentWindow.document.body.scrollHeight;
6 U$ i: F# H' E7 l; a3 R9 L* Wvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
) D, h- |. E+ e2 ~; R, K. P, kvar height = Math.max(bHeight, dHeight); " G) @2 Q4 ], w" x, B) E! s
iframe.height = height; y+ M' [! L& [. N7 d/ U
}catch (ex){} 5 X+ `1 Q: C/ H9 [ [( a
}
& B4 }. W4 X8 o! V" g' Zwindow.setInterval("reinitIframe()", 200); / I% Q- b4 w7 n- V8 u- k4 w
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|