- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。 5 L) M$ Q9 l! T$ U9 u( s
我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。
8 i/ \+ Q2 s# n+ t9 d这篇文章,希望在这两个方面再做一些深入。 " y2 T# o M! R& s) l9 \
可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。 $ ~8 ]( x2 J8 l. g
顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。 + t6 j% ]+ `8 V
传统做法大致有两个:
3 b8 S5 i7 K- _方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。 * p. Q/ V8 D! D: w
方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。 ) x) @0 M/ m$ A! g9 L G
在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。 2 x: Y( i3 ?% j& f% M. Y
两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。 # J: h6 [2 o, I: i( B& h
如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。
# M/ F$ i& j" a9 L, ^Demo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html
4 M& R( y# w5 M- h6 i主页面代码示例:
) o6 w, B. d: y" U8 e w3 U8 w<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> " K* {# V% j5 m2 y; C9 \
<script type="text/javascript">
7 ]5 H5 n. z8 d: D0 @function reinitIframe(){
! I. r4 U: C3 Z, J% Q: n8 T% y8 }var iframe = document.getElementById("frame_content");
7 |+ M0 [* m U6 E+ b/ M# h- Qtry{
X4 b/ H$ T; e. j! v' Niframe.height = iframe.contentWindow.document.documentElement.scrollHeight;
( }' M1 P7 J V ~}catch (ex){}
- U) g7 K# K- j! u5 h( [3 _0 C} 9 p4 C- S1 M0 D }' O9 ]
window.setInterval("reinitIframe()", 200);
+ H8 X- x8 Q4 e- J/ `$ A/ ?% ^</script>一直执行,效率会不会有问题? ( L$ p+ F. C9 J
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。 3 L+ g2 K' [+ l1 p( Q
下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。 ) N/ Z6 B! q, t1 I/ A
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例: ' b. \/ H% n. H0 x% @
<div><button >Check Height</button></div> # `* _) t1 u6 {0 H2 k
<script type="text/javascript"> % m. v+ l2 I" C9 x4 l5 V
function checkHeight() { 0 k; |, O3 ~5 D
var iframe = document.getElementById("frame_content");
# p+ W" O9 P: Gvar bHeight = iframe.contentWindow.document.body.scrollHeight;
$ s8 M$ `/ S; q) q5 H( p' \var dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 5 c$ s3 N, h- o8 @" w" |3 E
alert("bHeight:" + bHeight + ", dHeight:" + dHeight); ' X! I# u& P' b5 `6 w2 p
} $ S7 d( H+ l& `) l; T T
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例:
4 k5 ?# {3 h. y<div> * ~4 r$ s- ^3 l+ q6 Q
<button >Toggle Overlay</button>
* Z& I+ [" t* ~</div>
4 }) D1 p8 c. w) F<div style="height:160px;position:relative">
, O- u/ r$ L0 F<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div>
8 _3 q5 F8 G, Z6 Y</div>
, n: [# X/ ]1 ~# ]- |4 _<script type="text/javascript">
, J* _5 x g, K" j+ I+ Efunction toggleOverlay() { ( j8 [' m+ ^+ r7 l1 T k0 y) @1 J
var overlay = document.getElementById('overlay');
' ~) [, H2 x2 P* ]5 Y) Q! z% Uoverlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
& {# `9 [2 l$ Z% J0 R}
Y U! r* v# `</script>下面列出以上代码在各浏览器的测试值:
) T5 a4 G6 @7 ?, Q5 n(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值)
" }/ @ M+ E8 Z+ J/ 层隐藏时 层展开时 + `& w1 k+ ~- ]
bHeight dHeight bHeight dHeight
% |; L3 Z& C; F) k+ r3 T# gIE6 184 184 184 303
* O W. p, Z {7 r2 e) HIE7 184 184 184 303 1 @2 T' k) X1 }# M
FF 184 184 184 303 2 @, L# I9 J% c2 _1 b0 R6 i
Opera 181 181 300 300
0 {) |2 b1 J& @: o2 ESafari 184 184 303 184 6 s6 F. ~+ u+ ~, I/ Z, R
暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。 ; ]( ?1 ~8 e3 e* i2 t
但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了:
/ n; q8 M/ ^9 r+ A5 e: c. s6 S7 @function reinitIframe(){
# \3 x7 O) Y* f: Z4 yvar iframe = document.getElementById("frame_content"); 5 C8 l2 _. M% I0 @8 |, F
try{
& ^" I- A$ v& A9 _var bHeight = iframe.contentWindow.document.body.scrollHeight;
/ |! r7 Y7 |5 `+ _* @' K+ Hvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 8 z; ]/ W& N/ V9 i1 F9 _
var height = Math.max(bHeight, dHeight);
% A/ r+ E1 O1 j* {) tiframe.height = height; 3 w& p' c0 s4 @$ E- n& { `* J
}catch (ex){}
) F% I1 u) f( l- Y0 m! O# V}
6 C- E) | `7 |4 D2 vwindow.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 + }+ s) b: G$ R. Y5 v: X9 s
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。
8 ?3 Q: n1 n8 s& N2 [ K9 T可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。
: S, m5 }8 v* C D( J这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。
) T e$ @' k; I. g' P, A在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。
* ?5 ^2 `' q' N; E4 W' g; W这是最终的主页面的代码:
$ k; N$ {" k9 y<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"
- |/ y, X2 s E/ v+ Q7 F></iframe>
/ `3 G7 y& y* F5 y. {3 Z<script type="text/javascript"> ) j! d7 e( b: c4 @8 E
function reinitIframe(){
# q) r6 e) P: Ivar iframe = document.getElementById("frame_content"); 5 o5 e A0 ]) E; P& [4 K
try{
$ i* h$ w' L$ Q- K4 avar bHeight = iframe.contentWindow.document.body.scrollHeight;
2 u1 O& ` w! } L2 S# |2 g& z) Wvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 0 ?9 ?. v: O) H( n3 |% h
var height = Math.max(bHeight, dHeight); 9 M- H: `- l6 m8 ~
iframe.height = height;
5 `' @, K5 |1 g% t+ [4 Q}catch (ex){} & Z% E& U- h" H. t
}
1 X4 _5 g1 O5 R$ ^window.setInterval("reinitIframe()", 200); # _0 h7 {2 F7 [, g6 ?( N6 m7 g
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|