- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。 . {% \! m' M/ p0 F
我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。 , C1 c' J2 e: [
这篇文章,希望在这两个方面再做一些深入。
2 ]- Y/ c; j$ v3 J可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
- B' ] U2 A3 K4 V% v8 A) D顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。
( A [1 O/ U: Y, U. z传统做法大致有两个:
! R4 j- P) z* X* ^- B* x- S方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。 b$ ?$ ?' U; h( x* I; R9 v/ V& A
方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。 $ a9 R, H' I3 f, q/ [& k
在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。
- w4 A; O4 `, q J两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 ; H5 _7 T. n3 K
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。
5 `# _* _) T) qDemo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html * \; \( ^8 P$ S# l, Z# C
主页面代码示例: $ _8 a9 F8 k% _. s% b/ x$ y1 K
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> + O! E$ S1 p2 g' w2 A1 e+ i# @0 r5 }
<script type="text/javascript"> ; g# N% F0 B- _$ A, v
function reinitIframe(){ ) G* P2 {$ X9 k3 O$ U
var iframe = document.getElementById("frame_content");
4 C. J, L0 A" C% }try{
( J+ I, W/ R" piframe.height = iframe.contentWindow.document.documentElement.scrollHeight; $ k( m, m, m& r( r
}catch (ex){} 9 e' ?, ]% f! U$ G2 ?- v' N
}
. P3 o1 U; ?# b& Fwindow.setInterval("reinitIframe()", 200);
`' d) e3 F6 b1 \" _: M</script>一直执行,效率会不会有问题?
5 X. L2 [8 A2 L1 e$ \我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。
' ]) Q4 G* k4 X: a; U+ r下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。 , p2 {( V4 S7 [2 K( X; r
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例:
, O! t/ @2 J6 @9 q# P<div><button >Check Height</button></div>
1 Z: i5 s# _$ r, I# G6 f<script type="text/javascript">
; _$ k' I. \5 v C' ` w0 G+ Gfunction checkHeight() {
: o' O/ e; g4 ?3 z8 i* C [9 pvar iframe = document.getElementById("frame_content");
6 z0 x7 C( r9 w5 lvar bHeight = iframe.contentWindow.document.body.scrollHeight;
2 W7 l. _# d. Evar dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
% W1 i: C' w0 D- l% dalert("bHeight:" + bHeight + ", dHeight:" + dHeight); ! ^$ {; S: Z* s& [) N# s1 E
} ' d: p2 E3 @# b8 d
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
! ?- |$ q1 S' e K' w: Y9 {<div> ' D4 d6 }/ G U, p! ~- [& s- N C
<button >Toggle Overlay</button>
& k- S+ m1 N& y$ o4 K</div>
8 V$ \5 Y( B7 A5 S1 G<div style="height:160px;position:relative"> & |1 u8 f. K5 N- k( [+ B' m6 b
<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div>
) A+ ]' e4 j7 P( k& _</div>
# ]& r% f3 q7 y<script type="text/javascript"> & S' I! c+ u; b. _7 \
function toggleOverlay() {
) D! h, j% L; a* @5 ?var overlay = document.getElementById('overlay'); " M4 ^6 ^# R* N6 J2 q' T
overlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
: g; Q& ~3 d7 O3 b} $ G @8 z, s; b S. f5 J
</script>下面列出以上代码在各浏览器的测试值:
+ x4 g; C/ D2 ]7 }$ @(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值) ( `0 [! ^% g/ G
/ 层隐藏时 层展开时 ! J' h; Z' d: Q0 L: _3 m+ a
bHeight dHeight bHeight dHeight
( W# @+ Y' u6 ~; u. yIE6 184 184 184 303
6 k& A8 [9 {0 K5 l* q8 g' xIE7 184 184 184 303
& X! w6 |7 N/ j7 E* J( RFF 184 184 184 303
7 X" r+ b' d7 ?" ]Opera 181 181 300 300
/ ]& g' ^7 f: p7 ~ {Safari 184 184 303 184
! S4 ?# x5 q2 p- P" s暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。
/ n9 |" b& w% q! e; D7 D但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了:
# h& g$ V( l( q) ^9 ]( Ofunction reinitIframe(){ 7 i) d9 _# W9 J% e2 l' w# p" `
var iframe = document.getElementById("frame_content"); ) m' H9 J- r" Z
try{
T$ X* X( C9 j! H; U2 ?4 lvar bHeight = iframe.contentWindow.document.body.scrollHeight;
- T5 i6 C7 e7 i! B" j1 [var dHeight = iframe.contentWindow.document.documentElement.scrollHeight; + _5 r8 m" ^' R
var height = Math.max(bHeight, dHeight);
6 X6 j, q9 O/ i" v" Kiframe.height = height;
: c) ~' H" ~" |2 e}catch (ex){} 5 }; d3 f7 |* }8 U
} 5 i: V: |/ P m; \- z1 K. y) o) m
window.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。
2 L" }8 z* ^+ N8 ]$ K如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。
$ g- v4 y) w+ ?6 U: ^! M3 x可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。 3 e0 T1 E" [% L3 K8 t& C9 y
这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。 4 j# l$ S* x% g. j
在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。 7 r) `, I3 v) V7 G
这是最终的主页面的代码:
6 C4 \* e. F( H$ m. Y$ k<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0" $ L D! [8 M: V! r2 g+ g
></iframe> 5 D2 X3 `4 |6 _6 i
<script type="text/javascript">
+ n( q( g9 X4 G; Jfunction reinitIframe(){
" i9 Q8 T- J1 K" z. `. g# @var iframe = document.getElementById("frame_content"); , [7 R, k/ @0 C, q+ E7 N) _
try{
" y# m/ _ v9 h4 `9 E Gvar bHeight = iframe.contentWindow.document.body.scrollHeight; ! }+ G1 }- Q c, q* B) s- ~
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
8 H6 c p9 ^, X( }/ S. Hvar height = Math.max(bHeight, dHeight);
9 ?4 w% }3 v/ K, B0 I3 a9 w9 W% n6 fiframe.height = height; : E8 O; l- S& ~/ y' j' \
}catch (ex){}
* J I' B* {# b# k7 l M8 F$ A4 J} 3 z# g' f/ N* T& R
window.setInterval("reinitIframe()", 200); 5 U- q1 X: Z" l! h/ |
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|