- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。 , d' P- b5 F& Q0 o4 X0 g. V
我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。
& Z$ K0 C* B3 L$ X6 a; {5 k# c这篇文章,希望在这两个方面再做一些深入。
. t8 A8 }% X' t. g5 j6 e, z; x可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
- `; B2 b1 q( y顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。
& X/ [7 ?; D Z0 m& z传统做法大致有两个:
/ J% L r6 b6 u方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。
" x5 c5 @7 Z: }+ R方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。
; e4 C9 W8 }0 \1 [- O在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。
6 s% t" n3 f' f; E$ i: T. U两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 7 y2 _" M" H& S
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。 ( ~7 s3 b/ E8 T K
Demo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html
( x$ \ }( E+ w) S主页面代码示例: . |2 k9 [& H: B; I4 y- S
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe>
0 n, B0 B& E2 |4 Y) g6 ~<script type="text/javascript"> ! T( X l/ q- j1 h0 e7 m
function reinitIframe(){ / ]* c# F l+ M' I- h. a+ ?
var iframe = document.getElementById("frame_content");
& N G& h; I1 J; _, U$ N7 Utry{
- u2 W& ^9 Z9 qiframe.height = iframe.contentWindow.document.documentElement.scrollHeight;
8 n4 t0 H. c( |% f$ b9 q}catch (ex){}
$ ~, G; m- y1 x1 [1 A! v* R! m} . P- E" p2 t9 X1 c
window.setInterval("reinitIframe()", 200); / u3 q' r5 |- w9 x3 Z9 x/ W( d
</script>一直执行,效率会不会有问题? 1 G" t# q) R- A% o2 C# N
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。 * u8 O- X+ O1 L; A% k+ G
下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。
& [. p* U- M- v6 w<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例:
' Q$ ^& d g" C<div><button >Check Height</button></div> 2 u; x4 e9 I: o" _3 e
<script type="text/javascript"> ) U. M; A& K2 h& {( y: v F, g
function checkHeight() {
/ d6 x$ n0 H! M2 Evar iframe = document.getElementById("frame_content"); 8 ?9 {8 Z* s6 `
var bHeight = iframe.contentWindow.document.body.scrollHeight; + @* Z$ e! H2 {7 |; B6 A& [
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight; & D: j' D% u, d# f3 Q; Z! b
alert("bHeight:" + bHeight + ", dHeight:" + dHeight); / _- e0 B# d$ f0 z# u+ K8 J* T
} - \0 o- v# G' K x! N
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
; M. w, M( F# q8 \4 w. m<div>
, ?* R& {3 _6 _+ z<button >Toggle Overlay</button>
U4 |# H+ w6 U( V+ X* Q</div>
( J: G8 T% I! G" W) F<div style="height:160px;position:relative"> ' {7 o6 V/ e0 U0 D& c! }
<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div>
9 A2 x$ V4 f' x$ f5 X7 U* \</div> / _3 G1 `# `% f9 S4 l
<script type="text/javascript"> - [1 Y+ p+ j1 I- l( L6 I3 S) V
function toggleOverlay() { 1 a: m4 x. `' K; x
var overlay = document.getElementById('overlay'); ; \7 |. O' D$ V
overlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none'; 4 o* H& a- p( \# K8 M4 ~" _
}
6 z& Q2 a8 t, S4 W) x. v; `</script>下面列出以上代码在各浏览器的测试值:
9 c5 h! _% e' B: o2 S5 Z; y(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值)
' a' I3 Y2 f$ W& j1 J; v/ 层隐藏时 层展开时 7 @- x1 {/ ?! [" a
bHeight dHeight bHeight dHeight
: V3 Y3 i. u6 ZIE6 184 184 184 303
5 J1 S4 j0 r0 `0 s, r C0 G5 fIE7 184 184 184 303 c2 A4 b' U. l. {
FF 184 184 184 303 6 e8 x+ F: z Q" |
Opera 181 181 300 300 1 P! t e) E2 q
Safari 184 184 303 184
0 m1 h3 N& g" F7 d8 \2 A暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。 3 R# T1 x9 |# \. V; w8 M7 Y: y
但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了: 8 x3 ?, b8 U4 v8 c1 A+ b- I
function reinitIframe(){ & k1 c5 p8 S* V" G- [, i0 F% g
var iframe = document.getElementById("frame_content");
6 N+ _2 g8 F( n9 o: Q- ?try{
5 t& D2 i6 H5 Lvar bHeight = iframe.contentWindow.document.body.scrollHeight;
u# E! Q v% ]% Q1 A4 |0 Wvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; - h1 e8 ^" ?6 l; H0 t) L* R- h& N! D
var height = Math.max(bHeight, dHeight); 1 U. p9 F O1 k0 X0 D2 J
iframe.height = height;
4 |- \% Y* H: }' W+ q8 i! v6 {}catch (ex){} $ {. G" u' O/ I
} $ d" Q* k5 I1 _; G& T8 e+ O
window.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。
( K1 i$ q8 R- V如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。 / `- k/ `5 K6 b8 T/ z+ E C1 u
可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。
! `* O, _' {. o; S3 M( B2 B这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。
3 l. | ~0 u6 q8 }在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。 7 v0 ~3 B, H: }7 P6 ?1 B1 f3 ~9 u
这是最终的主页面的代码: $ n, ^5 A; {. x7 Y; Z' _
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"
8 W# x! m1 Z2 a/ a) h3 q></iframe> % {2 \/ f* R! ~* J
<script type="text/javascript"> 5 Y1 s% O5 ^7 K
function reinitIframe(){ % f, h% Z1 S2 ]" d3 M1 S
var iframe = document.getElementById("frame_content"); ( [# V. W' t: t; G$ P; R. H* U
try{ + N. j/ c: o0 _5 f5 P. l+ m
var bHeight = iframe.contentWindow.document.body.scrollHeight; ( c* u1 g) E* X. a4 g4 Y0 l( a8 i
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
. B6 n2 d) q j& g3 ovar height = Math.max(bHeight, dHeight);
% Y$ ]- ~) g* z, @; Q3 x, S6 J/ jiframe.height = height; / h/ o! Z# e) J4 I
}catch (ex){} 8 {- s( S8 W0 u6 _
}
& k9 ?, Q3 Z* u4 M' swindow.setInterval("reinitIframe()", 200); 8 M* G1 `0 i' N, ?* C
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|