早在2013年9月,我在測試我的Solved by Flexbox項目時,在IE10和IE11中發現了一個Bug,就是Sticky footer實際上不會粘貼在頁面的底部。我花了很多時間來解決這個問題,但始終沒有成功。
起初,我真的很生氣。在Flexbox出現之前,如果在不知道頁腳的確切尺寸情況之下,要使用純CSS來實現Sticky Footer的效果是不太可能。Flexbox改變了這一切,可以使用CSS解決這個問題。
失望之後,我最終得出一個結論,這不是什麼大不了的事情。我的意思是,從漸進增強的角度來看,我的解決方案還是相當不錯的。雖然它在IE並不完美, 但不至於壞得一塌糊塗,內容依然可以訪問,它只會在較小的內容頁面上展示,而在較長的頁面上,它看起來和Chrome、Firefox、Opera和 Safari浏覽器得到的展示很像。
幾周前,我在Github看到一個新的請求,在IE下使用@media
規則來解決Sticky Footer的問題。這讓我再次思考起這個問題,我決定找到一個特定的解決方案,而不再使用任何浏覽器特殊的Hack技巧。
事實證明了是有相應的解決方案可以實現的,也再次證明了我自己還是不夠努力。
在這篇文章中,我將解釋這個解決方案,並且討論一些我在浏覽器中發現的的Bug。並且對相應的Bug提出一些修復建議,讓Flexbox能更好的實現跨浏覽器的兼容。
Flexbox規范還沒有最終確定,所以自然會有一些最新的草案和浏覽器實現滯後現象。本文不打算在背後對任何組織進行指指點點,他們的目的是幫助我們做前端開發的人員管理好浏覽器的不一致。
下面列出了我在制作Sticky Footer案例時在各個浏覽器是碰到的問題清單:
min-height
)將會忽略它們的父容器的高度(height
);flex-basis
在IE10和IE11中,min-height
屬性可以設置flex
容器列方向的大小,但是,如果容器的flex
項目在不知道他們父容器的大小時,也就是沒有給他們父容器設置height
時,設置min-height
將會失效。
這個問題就存在我的Sticky Footer案例中,因為Sticky Footer布局中就需要給內容設置一個min-height
為100%
(或100vh
)的聲明,確保內容區域至少和浏覽器窗口的高度是一樣高。
既然flex
項目無法識別min-height
,我們就需要找到另一種能讓其工作的方式。
當一個大的flex
項目放到flex
容器中時,其他的flex
項目就會相應的變小(這就是flex
布局的算法)。flex-shrink
會根據內容來進行計算。但許多浏覽器剛好與其相反,他們不應該允許flex
項目無限的縮小。他們小到最低寬度或高度的聲明時不應該再繼續縮小,如果沒有聲明最低寬度或高度,應該縮小到其內容的尺寸為止,不再繼續縮小。
Flexbox規范是這樣描述的:
默認情況下,
flex
項目收縮後不會低於其最小內容尺寸(最長單詞的長度或固定大小的元素)。為了改變這一狀況,給flex
項目設置min-height
或min-wdith
屬性。
Chrome,Opera和Safari目前已經忽略了這個規范,讓flex
項目可以縮減為零。作為一個結果,你的內容就會重疊在一起。
之前發布的IE10版本的flexbox規范時表示,flexbox項目在使用flex
要需要設置單位。
如果更想的大小是“0”,它必須指定一個單位(如
0px
)以避免歧義;不帶單位的”0“要麼被視為靈活性,要麼被視為一個語法上的錯誤。
這不是真正的規范,但IE10~11仍然把它當作是真實的。如果您使用flex
聲明了一個1 0 0
,那麼這個規則在IE10~11中將被視為是一個錯誤,整個規則的靈活性將被浏覽器忽略。
我選擇Sticky Footer布局做為本文的主要例子,那是因為遇到很多人都在想找到一個跨浏覽器的解決方案。但是在我們正式進入介紹細節之前,先讓確認一下我們都是在同一個頁面上。
下面是我用來制作Sticky Footer布局的結構:
<body class="Site">
<header class="Site-header">…</header>
<main class="Site-content">…</main>
<footer class="Site-footer">…</footer>
</body>
遵循規范的浏覽器使用下面的代碼可以實現Sticky Footer布局效果:
.Site {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.Site-content {
flex: 1;
}
現在正如我們提到的,這個CSS是可以正常工作,但是不是所有浏覽器都完全遵循規范的,所以在一些浏覽器中,他是存在問題的。
如果你是一位有經驗的前端開發人員,你應該知道,任何解決跨浏覽器的問題,不僅要讓他今天能正常工作,還需要保證你的項目完成後續也能正常工作。
解決方案取決於解決方案的任何行為。
到目前為止,基於前面所說的一切,我個人的要求是,找到解決Sticky Footer布局的替代方案,其必須滿足:
在所有浏覽器下都能正常工作
浏覽器修復了這些不良的行為也要能繼續工作
不依賴浏覽器任何特殊的Hack手段
使用height替代min-height
如果你做前端開發有一段時間的話,你應該記得IE6中永遠都不支持min-height(或者min-width)屬性。然而,在解決這個問題時,總是使用height:100%來替代min-height:100%。因為浏覽器對其解析出來的效果是一致的。因此,在制作Sticky Footer布局時,建議像在IE6中的解決方案一樣,設置一個height:100%。
知道這一點之後,我試過了我的找到的第一個解決方案,就是使用height:100 vh來替代min-height: 100vh。經過測試,他確實能在IE中正常工作,但它不能在Chrome中工作,所以我立即記錄下來了。
事實證明,我們應該認真的了解規范,而不能一味的認味Chrome是正確的,IE就是錯誤的。
在CSS中,你通常都會使用min-height來替代height避免內容超過高度是溢出問題。當有太多的內容時,你顯式的設置了height值,這也就意味著,內容要被剪切,重疊或出現滾動條。在很多情況之下,這些都是不好的表現形式。然而,當你在body元素上這樣處理時(就比如Sticky footer布局),出現滾動條並沒什麼大不了的。其實這也正是你想要的。所以在body元素上顯式定義height:100 vh和有太多內容時,其表現的結果應該是一樣的。
那麼問題來了:為什麼在Chrome浏覽器中不能正常工作呢?
最小尺寸的問題
前面我提到過,一些浏覽器錯誤地將flex項目縮減到小於其默認內容的尺寸,導致內容重疊。這也就是為會要將min-height換成height,但我在Chrome浏覽器中測試時,不能正常工作。
頁頭,頁腳和內容元素都縮小到元素內容最小尺寸(但不是更少)時會發生些什麼呢?如果這些元素(組合)有更多的內容在屏幕上,body元素應該與滾動條溢出是一樣的。頁頭、頁腳和內容元素都應該正常顯示,一個在另一個之下,沒有重疊在一起。
Chrome發生了什麼,而不是允許頁頭,頁腳和內容元素收縮小於默認內容的最小尺寸。其結果是body元素並沒有溢出,而發生在頁頭,頁腳和內容元素自身上。由於這些元素的overflow默認值是visible,他們的內容是會相互層疊的。頁腳是固定在頁面底部,而頁面主內容是溢出和其層疊在一起。
幸運的是,有一種簡單的方法可以解決這個問題。
Flexbox規范定認了一個flex-shrink屬性,其默認值為1,表示的是其flex項目縮小不應小於其內容默認的最小尺寸。你也可以使用flex-shrink:0;取得flex-shrink:1相同的表現形為。如果元素的大小是根據其子元素的來決定,並沒有設置width、height或flex-basis值,然後設置flex-shrink:0;可以取得同樣的效果,來避免前面提到的錯誤。
避免沒單位的flex-basis
不帶單位的flex-basis的Bug是幾個Bug中最為簡單的一個,但它可以說也是最難跟蹤的。
原來在解決Sticky Footer問題時,在content元素上聲明了flex:1。因為flex:1是flex: 1 1 0px的簡寫,而且自從知道不需要任何縮放時,我決定使用flex: 1 0 0px。
這個一直都能正常工作,直到我在IE中測試為止。
IE中存在一個組合的漏洞和CSS的壓縮:為了縮小CSS文件大小,常常將1 0 0px省略單位值1 0 0(沒有帶單位的flex-basis值)。因此IE10~11完全忽略了這個聲明。
一旦發現了問題根源所在,那麼解決辦法就微不足道。設置一個明確的flex-basis值,或在使用flex簡寫值時,使用flex:0%。注意,使用0%比0px強。
將他們放在一起
下面將本文討論的Bug和相應的解決方案,總結到一起:
在IE10~11浏覽器,min-height不適合於flex容器的子元素flex項目。如果可能的話,使用height來替代min-height。
在Chrome,Opear和Safari浏覽器不識別flex項目內容的最小尺寸。可以設置flex-shrink的值為0(而不是默認的1),以避免不必要的收縮。
不使用無單位的flex-basis值,因為在IE10~11中,flex簡寫被忽略。常使用0%來替代0px。
記住所有的缺陷和解決方法,這是我最後想出的替代解決方案。它可能沒有最初那樣清爽,但它滿足我們所有要求的一個解決方案:
它可以在所有浏覽器中工作
規范的兼容性,錯誤是有,但可以繼續工作
不使用任何浏覽器的特定hack
我在CSS中添加了注釋,來解決那些解決方法:
/**
* 1. Avoid the IE 10-11 `min-height` bug.
* 2. Set `flex-shrink` to `0` to prevent Chrome, Opera, and Safari from
* letting these items shrink to smaller than their content's default
* minimum size.
*/
.Site {
display: flex;
flex-direction: column;
height: 100vh; /* 1 */
}
.Site-header,
.Site-footer {
flex-shrink: 0; /* 2 */
}
.Site-content {
flex: 1 0 auto; /* 2 */
}
可以點擊這個案例,查看使用Flexbox解決Sticky Footer的方案。
腳注:
從技術上講,如果不需要兼容IE7以及其以下版本,在不知道頁頭和頁尾高度時,創建一個Sticky Footer布局可以使用display:table;。在實現跨浏覽器,要使用浏覽器的Hack,可以說不是一個實際解決方案。
這裡所說的”所有浏覽器“,我指的是所有實現2012年3月或更新發布的Flexbox規范的浏覽器。換句話說,支持flexbox的現代浏覽器。
使用flex-basis:0解決大多數問題,但不是全部問題。如果你想讓flex項目收縮不小於默認內容大小,這個解決方案是行不通的。
2014年3月更新的flexbox規范改變了flex:1含義,從1 0 0px變成1 0 0%。也就是說flex:1的簡寫相當於是flex: 1 0 0%。