- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。
' q* N1 ]$ x( W7 @2 Y我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。 7 W# |: n& r" F3 O/ D7 g7 `3 G
这篇文章,希望在这两个方面再做一些深入。 8 }2 T& M, ?$ j- \- Y5 ^6 @& Y
可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
5 X$ L& z( n" H/ z9 K- n1 f: x顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。
1 z- T. H2 ^: a; [1 o传统做法大致有两个:
0 @# u3 Q( D4 G7 l' }方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。 ' l' Y' n7 T1 I- D! ?1 P1 `
方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。 ! _. A* H+ u. X2 l9 j& x8 b
在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。 7 ?8 ~4 o8 j6 u2 z, G
两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 + `1 ^, U7 Q2 O1 {$ k, {
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。 ! `$ I! \( X* i1 V1 J$ a' `' w
Demo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html % F1 r$ O8 c9 _& s5 e
主页面代码示例:
1 _7 H9 w! ~/ ^& [3 `<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> : Z u* W$ q e K# X4 \
<script type="text/javascript">
`0 e, s& v2 I0 |. J+ mfunction reinitIframe(){ 8 M: S& d/ `- F
var iframe = document.getElementById("frame_content"); & y4 K9 C2 f6 L- B7 n! E6 Q5 [
try{
; P* ^6 M+ U4 Ciframe.height = iframe.contentWindow.document.documentElement.scrollHeight; ' c, h9 z7 Y; s
}catch (ex){}
6 @) j# \. d! J" }* Q7 p, a}
. M+ U( G& z/ U8 j" [$ D8 ewindow.setInterval("reinitIframe()", 200); ) E, q5 R/ B3 O" h; Y
</script>一直执行,效率会不会有问题? ) p/ r4 k$ G, m" G2 Z6 M. b1 d- x
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。
; f# L4 \( v; N, Y下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。 5 G! ?1 `4 e. x( x g4 h
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例:
5 o. n0 q Z. \0 v l' G: b; p<div><button >Check Height</button></div> 5 r0 l# q7 z) A6 k) C" X6 W8 W- F, F
<script type="text/javascript">
8 M+ ] M8 L7 d) y; Vfunction checkHeight() {
- M$ y `7 }' M+ h+ Tvar iframe = document.getElementById("frame_content"); ' g: `9 ^' o8 z( @* B4 \
var bHeight = iframe.contentWindow.document.body.scrollHeight; O4 g2 G( i1 r5 O( l
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
p J0 j" S" H V2 oalert("bHeight:" + bHeight + ", dHeight:" + dHeight);
2 i, M# O1 P. N} ! x6 U9 L; s$ @" X7 f" w
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
* d5 ^9 I* T* m& f% b( ]<div>
( _' F {' l* r# X6 l/ D/ I' W<button >Toggle Overlay</button> 5 Q9 }4 h9 t4 R- n7 P$ u, f0 f
</div> 4 w; B) q+ I$ `2 i" {- A1 ]
<div style="height:160px;position:relative">
" Y* A* Y( S! J( ?6 y6 p<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div>
$ p% @' r7 w$ I0 Y* F3 q</div>
8 f, ]- {. i5 G7 {* [, T<script type="text/javascript"> * j6 `1 o0 n% @0 c
function toggleOverlay() {
* D, u8 g$ I5 ~ F4 b- H, }var overlay = document.getElementById('overlay');
9 ^; F% Y- n4 X, xoverlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
6 l: L- e( F8 G/ U- l% z}
3 y9 [0 L) e% l: l1 V; e8 H5 y</script>下面列出以上代码在各浏览器的测试值:
0 _( y7 ]0 J- V(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值) " K& r" \7 n3 X. _, P
/ 层隐藏时 层展开时 ) B/ u N ~( A# H* n3 e) ^5 W. i' d
bHeight dHeight bHeight dHeight 3 L% R* |" h$ {5 p& y6 a
IE6 184 184 184 303
M8 {2 g" n. d2 @IE7 184 184 184 303
7 r+ S1 g- z! q! S" vFF 184 184 184 303 0 [$ z, k; c' O
Opera 181 181 300 300 2 g% _6 |. U+ b' [
Safari 184 184 303 184 ) Q3 `- Y4 C: `4 \
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。 H' N! A5 V) m$ R3 C. M
但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了: ! r- Y1 R z4 K2 N
function reinitIframe(){ . ^5 s( O9 a: j% a+ [* A
var iframe = document.getElementById("frame_content");
' {4 v7 Y7 D: g" _* q# b G: ~) Wtry{ P4 b, C7 ?/ ]0 G3 b
var bHeight = iframe.contentWindow.document.body.scrollHeight;
6 [# p- S; U: y8 c/ ?# X" _var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
6 n. c1 }$ r$ P( y/ B# e0 mvar height = Math.max(bHeight, dHeight);
+ |, x/ P, b6 a( g3 B" p$ X9 R7 @iframe.height = height; - G4 m4 s5 z# ^9 U$ d0 S6 T' v) g
}catch (ex){}
" M' _- A) r9 K/ V% k5 |7 s. L}
( y% W; N% A- @& Vwindow.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 7 Y; {' S( H7 v' C) Z1 l
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。
7 ?# c. p/ z& t" A# n0 ~可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。 5 ]! @# ^5 n; u k
这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。 * e2 M1 j! p0 ~$ I0 x
在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。 & b2 Y# s% u4 w) b
这是最终的主页面的代码: & G( b8 C0 P5 y- ?* o
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"
; [$ a" Q" s, \></iframe> 0 d5 L# o6 M+ k' B9 q- H+ e
<script type="text/javascript"> % @ d n! k/ ?5 L7 X! n
function reinitIframe(){
1 M" t6 ?& ]! N/ V3 V7 Gvar iframe = document.getElementById("frame_content");
# ?1 D( A* |+ X# ?& N8 Z; d+ o9 itry{ ) z4 g c% p. [ S
var bHeight = iframe.contentWindow.document.body.scrollHeight;
% @9 r2 s) ~! Gvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 5 S7 L( `4 R2 z' }8 c; _
var height = Math.max(bHeight, dHeight);
9 I+ p+ |% ~# R9 h9 biframe.height = height; 5 D' }4 F P+ |9 N+ l9 @# U; u
}catch (ex){}
! M0 a/ i& W6 ~/ g- V8 M1 F. B} 7 G. X W( K$ o
window.setInterval("reinitIframe()", 200); # f8 g( e+ z* ~' C; c
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|