- 注册时间
- 2009-12-25
- 最后登录
- 2021-7-10
- 在线时间
- 3302 小时
- 阅读权限
- 200
- 积分
- 10
- 帖子
- 13416
- 精华
- 1
- UID
- 2036
  
|
通过Google搜索iframe 自适应高度,结果5W多条,搜索iframe 高度自适应,结果2W多条。
: R6 L: d& }! j; J' x我翻了前面的几十条,刨去大量的转载,有那么三五篇是原创的。而这几篇原创里面,基本上只谈到如何自适应静的东西,就是没有考虑到JS操作DOM之后,如何做动态同步的问题。另外,在兼容性方面,也研究的不彻底。 ' z3 f6 q0 `8 W, @7 A7 c
这篇文章,希望在这两个方面再做一些深入。
' J6 _8 e% ]* W2 k5 R& \, X0 C可能有人还没接触到这个问题过,先说明一下,什么是高度自适应吧。所谓iframe高度自适应,就是,基于界面美观和交互的考虑,隐藏了iframe的border和scrollbar,让人看不出它是个iframe。如果iframe始终调用同一个固定高度的页面,我们直接写死iframe高度就可以了。而如果iframe要切换页面,或者被包含页面要做DOM动态操作,这时候,就需要程序去同步iframe高度和被包含页的实际高度了。
* y0 v6 M) |3 s: B4 B+ s7 z顺便说下,iframe在迫不得已的时候才去用,它会给前端开发带来太多的麻烦。 " i: l8 h! O& B: A Z
传统做法大致有两个: - K0 I( a, [, V @1 R$ F" \- A
方法一,在每个被包含页在本身内容加载完毕之后,执行JS取得本页面的高度,然后去同步父页面的iframe高度。
" Z, y$ o$ F( K5 Z! c" t1 N; t方法二,在主页面iframe的onload事件中执行JS,去取得被包含页的高度内容,然后去同步高度。
7 }8 ~% ]% ?5 F3 u' {. [7 ]在代码维护角度考虑,方法二是优于方法一的,因为方法一,每个被包含页都要去引入一段相同的代码来做这个事情,创建了好多副本。
( {5 ?7 K$ W& a& b; _" X% b7 u& X两个方法都只处理了静的东西,就是只在内容加载的时候执行,如果JS去操作DOM引起的高度变化,都不太方便。
" H" _9 i: x L如果在主窗口做一个Interval,不停的来获取被包含页的高度,然后做同步,是不是即方便,又解决了JS操作DOM的问题了呢?答案是肯定的。
T. V7 C- L \4 `" [3 H( JDemo页面:主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html
! @; S$ X! i( b. W主页面代码示例:
4 c. Q7 `0 \8 ?+ q/ f<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0"></iframe> . w- w) p8 n" t! V8 |* W
<script type="text/javascript">
7 Q" |+ a; t* |* f7 Zfunction reinitIframe(){
5 F7 H1 a7 f7 j6 i" c, _7 |var iframe = document.getElementById("frame_content");
/ @3 T7 R) K8 b0 q7 A/ s1 xtry{ }+ L' V7 B2 F: Z
iframe.height = iframe.contentWindow.document.documentElement.scrollHeight; 2 X9 s: C- b# }9 v$ z- P
}catch (ex){}
0 H x$ j& w3 F$ |}
7 I, v8 [5 N+ w$ y& i: F( |1 ?0 Swindow.setInterval("reinitIframe()", 200);
* A8 h* q/ s. x% p</script>一直执行,效率会不会有问题? 3 k d$ ?7 r8 `
我做了测试,同时开5个窗口(IE6、IE7、FF、Opera、Safari)执行这个代码,不会对CPU有什么影响,甚至调整到2ms,也没影响(基本维持在0%占用率)。
, I; j3 t& p2 {5 x2 {% f/ p( w# }下面谈谈各浏览器的兼容性问题,如何获取到正确的高度,主要是对body.scrollHeight和documentElement.scrollHeight两个值得比较。注意本文用的是这个doctype,不同的doctype应该不会影响结果,但是假如你的页面没有申明doctype,那还是先去加一个吧。
$ O2 }0 Q, j1 Z1 j+ t2 t8 y<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">在主页面追加以下测试代码,以输出这两个值,代码示例: 8 f, b `, ^2 x5 w, O! U; b* B
<div><button >Check Height</button></div> 6 w V$ C- t$ B& B
<script type="text/javascript"> : y: w. Q$ f+ R3 s, ~) e- a- [
function checkHeight() {
) b5 w' R- @, O1 c; yvar iframe = document.getElementById("frame_content");
4 n5 a8 B8 a& p) i" D0 Bvar bHeight = iframe.contentWindow.document.body.scrollHeight;
2 `8 H6 i, U; n& @) `var dHeight = iframe.contentWindow.document.documentElement.scrollHeight; % b) H$ a" c; v1 K
alert("bHeight:" + bHeight + ", dHeight:" + dHeight);
+ i; x: Q7 P6 b} # }9 H+ K# t4 V* I- { s$ E
</script>被加载页面,可以切换一个绝对定位的层,来使页面高度动态改变。如果层展开,则会撑高页面高度。代码示例: ) D* I( i6 r& J
<div>
C$ B. |- V( W5 ]0 a# T<button >Toggle Overlay</button>
9 W7 ?( U' I2 `+ Z</div>
% {8 h$ P. R# l) m<div style="height:160px;position:relative"> ' y! b9 y# @) ~6 w, j/ O
<div id="overlay" style="position:absolute;width:280px;height:280px;display:none;"></div> ; o- R6 |6 e! V) ]. H4 h& ~
</div> % i% N3 w9 x+ A9 g! N
<script type="text/javascript"> 4 A7 d8 @5 B/ _8 t' o/ Q
function toggleOverlay() { . ]! w( P# y$ }
var overlay = document.getElementById('overlay');
5 A/ P# A% C% Joverlay.style.display = (overlay.style.display == 'none') ? 'block' : 'none';
% {; i; h' D+ T; ~}
5 L6 n5 Y s- ]2 m</script>下面列出以上代码在各浏览器的测试值: K0 A* J- ~ ^3 p" T
(bHeight = body.scrollHeight, dHeight = documentElement.scrollHeight, 红色 = 错误值, 绿色 = 正确值)
5 n0 m. ~ ~* _9 |' e/ 层隐藏时 层展开时
/ _+ f* X' i- B/ Q. l0 @( e3 |bHeight dHeight bHeight dHeight
- C. m3 S7 @- a' _% cIE6 184 184 184 303 6 h. A1 y1 _3 U4 L! P( `
IE7 184 184 184 303 . k' e! M( c4 W2 x2 s) b
FF 184 184 184 303
9 S: D# {. ^/ W; qOpera 181 181 300 300
) z F/ z2 D7 S' r, n' U. SSafari 184 184 303 184
: A7 A$ Z' l& I暂且无视Opera比别人少3像素的问题…可以看出,如果没有绝对定位的东西,两个值是相等的,取哪个都无所谓。 ( [8 ^9 |0 T( z! G2 l. I7 {
但是如果有,那么各个浏览器的表现不太相同,单取哪个值都不对。但可以找到了一条规律,那就是取两个值得最大值可以兼容各浏览器。所以我们的主页面代码就要改造成这样了:
' ?; @+ w( f/ ~. k5 r7 L0 Lfunction reinitIframe(){
3 B+ U$ w/ w: n) nvar iframe = document.getElementById("frame_content");
4 R# B" k4 ^2 K" {8 m# stry{
7 }1 T( f# F$ P/ w4 @var bHeight = iframe.contentWindow.document.body.scrollHeight;
/ q$ c) j0 a: [7 B6 F! W( yvar dHeight = iframe.contentWindow.document.documentElement.scrollHeight; 6 c! K4 f9 Y# V3 h- i
var height = Math.max(bHeight, dHeight); ' H- L: \$ D* }, ~9 {
iframe.height = height; 7 ^" i9 l% E% J+ o/ l1 A* n, F
}catch (ex){}
. ~) ?: Q2 }$ h0 {: V1 M- A} 1 R8 z, ]6 g3 B' t& h
window.setInterval("reinitIframe()", 200);这样子,基本解决了兼容性问题。顺便说下,不光绝对定位的层会影响到值,float也会导致两个值的差异。 ; W' ?5 G. m! s1 g+ [
如果你演示Demo后,会发现,除了IE,其他浏览器中,当层展开后再隐藏,取到的高度值还是维持在展开的高度303,而非隐藏回去的真正值184,就是说长高了之后缩不回去了。这个现象在不同被包含页面之间做切换也会发生,当从高的页面切换到矮页面的时候,取到的高度还是那个高的值。 3 @1 J7 ~1 q, w6 \! N+ L/ O- f6 m
可以归纳为,当iframe窗体高度高于文档实际高度的时候,高度取的是窗体高度,而当窗体高度低于实际文档高度时,取的是文档实际高度。因此,要想办法在同步高度之前把高度设置到一个比实际文档低的值。所以,在iframe的添加 onload=”this.height=100″,让页面加载的时候先缩到足够矮,然后再同步到一样的高度。 * I% _" C! B8 Q" [2 [ P
这个值,在实际应用中决定,足够矮但又不能太矮,否则在FF等浏览器里会有很明显的闪烁。DOM操作的时候主页面无法监听到,只能DOM操作完了之后把高度变小了。
5 }8 \, V4 k1 e1 Q在我的一个实际项目中,在成本和收益之间权衡,我并没有做这个事情,因为每个DOM函数中都要插入这个代码,代价太高,其实层缩回去不缩掉也不是那么致命。包括Demo里,也没有去做这个事情。如果读者有更好的方法,请告诉我。
5 m& z9 `! @: n& Y D3 E" |" s这是最终的主页面的代码: . n3 j$ A" `3 T
<iframe id="frame_content" src="iframe_b.html" scrolling="no" frameborder="0" 1 s0 @: c O+ s; E
></iframe>
+ \' B C2 D1 v9 Y& Y* q: V/ v1 l<script type="text/javascript">
: B2 [" ^% S6 Q% ufunction reinitIframe(){
+ d U: T& K; k. ]- Bvar iframe = document.getElementById("frame_content"); 6 D# p/ W6 n: a
try{
' C5 G& H% Q3 z- `3 Wvar bHeight = iframe.contentWindow.document.body.scrollHeight; [0 V& ]( x- C" v1 f; t
var dHeight = iframe.contentWindow.document.documentElement.scrollHeight;
5 M3 Z" m( u1 B; J. fvar height = Math.max(bHeight, dHeight);
[$ z5 K$ V; [. p9 @% E& ?1 t& U4 a# Diframe.height = height; & m, R# {1 m* R- Y* `% \$ }1 R
}catch (ex){}
$ F) U! k" u* C& z}
7 m) I- v( }( A* \0 I* L9 Jwindow.setInterval("reinitIframe()", 200); ; U9 a) k# P, R0 G$ @) @# h# k
</script>附Demo页面: 主页面 iframe_a.html ,被包含页面 iframe_b.htm 和 iframe_c.html |
|