(點擊上方公眾號,可快速關注)
譯者:伯樂在線專欄作者 - ^-^肥仔John
連結:http://web.jobbole.com/86065/
點擊 → 了解如何加入專欄作者
前言
每當來個需要既要水平排版又要設置固定高寬時,我就會想起display:inline-block,還有為了支持IE5.5/6/7的hack*display:inline;*zoom:1;。然後發現盒子間無端端多了個不可選的空白符,於是想盡辦法修復這個bug。
直到一天拜讀了@一絲姐、@HAX等高人的秘笈後才頓悟,原來我錯了。那不是bug,是我不懂而已。
先行者——IE5.5中的inline-block
當我們為支持IE5.5/6/7而添加這段hack時*display:inline;*zoom:1,總以為從IE8開始才支持display:inline-block屬性值。其實從IE5.5開始已經支持了,只是IE5.5/6/7支持的是IE的自定義標準,而從IE8開始則是支持CSS2.1標準而已。
https://msdn.microsoft.com/library/ms530751%28v=vs.85%29.aspx
The inline-block value is supported starting with Internet Explorer 5.5. You can use this value to give an object a layout without specifying the object’s height or width.
<style type="text/css">
.bk1{
background: #06F;
}
.bk2{
background: #F60;
}
.item{
width: 100px;
height: 100px;
display:inline-block;
}
</style>
<div class="bk1 item"></div>
<div class="bk2 item"></div>
<span class="bk1 item"></span>
<span class="bk2 item"></span>
經過CSS2.1洗禮的我們對上述內容不禁會發出兩個疑問:
還記得楊過是如何變成神鵰大俠的嗎?不就是被斷右臂後發現左手才是真愛嗎:)
好了,其實我的意思是拋棄過去對display:inline-block的認知,重新來理解IE5.5/6/7下的它才是硬道理啦。
對於問題1,首先上面的引用很直白地告訴我們——display:inline-block能觸發hasLayout,然後就沒了。所以block-level element依然是block-level element,不會一夜成了inline-level element的。結論:display:inline-block僅會觸發hasLayout,而元素本該怎麼排版還是怎麼排版。關於hasLayout的內容可參考《CSS魔法堂:hasLayout原來是這樣!》
對於問題2,我們先看看是否真的沒有間隙吧!
<style type="text/css">
.bk1{
background: #06F;
}
.bk2{
background: #F60;
}
.item{
width: 100px;
height: 100px;
display:inline-block;
}
</style>
<span class="bk1 item"></span>
<span class="bk2 item"></span>
<br/><br/>
<span class="bk1 item">bk1</span>
<span class="bk2 item"></span>
見鬼了,在前一個盒子內添加些文本就出現間隙了?其實這真的和display:inline-block無關的,大家就放過他吧!來上呈堂證供!
<style type="text/css">
.bk1{
background: #06F;
}
.bk2{
background: #F60;
}
<span class="bk1">no line break</span>
<span class="bk2">
has line break
</span>
可以看到藍色塊k和紅色塊h間存在一個空格,而紅色塊k後也存在一個空格。可是代碼中我們看到藍紅色塊間有4個 HTML實體,為啥只有一個空格呢?而紅色塊中僅僅換了行而已,怎麼就有個空格呢?
先拋結論:上面兩端代碼均是white space、white space collasping再作祟。
White space不僅是空格符那麼簡單
初看之下以為就是空格鍵,其實white space是一組空白字符和換行符組成。查看unicode字符集我們會發現有一大堆空白字符(如NO-BREAK SPACE等),但HTML只把ASCII space( )、ASCII tab( )、ASCII form feed()和Zero-width space()納入white space囊中,另外還將line break(換行符)carriage return( )、line feed( )和它倆的組合納入white space中。
inter-word space——White space的用途之一
西文是以空格來分隔單詞的,而漢字間則無需空格分隔,但為了統一西文、東亞和CJK的排版,於是抽象出一個名為inter-word space的概念用於分隔詞義單元,white space則作為inter-word space的值域,而定義域就是語言信息。如西文以ASCII SPACE作為inter-word space,而泰文則以Zero-width space作為inter-word space,漢語則沒有inter-word space,所以word-spacing屬性不影響漢字間的距離,本來無一物何處惹塵埃呢。
White space collapsing的玩法
兼容性問題又來了,因為各瀏覽器的實現均不盡相同。
<style type="text/css">
span{background:#F60;}
</style>
<div><span>
before</span></div>
<div><span>
before</span></div>
<div><span>after
</span></div>
<div><span>after
</span></div>
<div><span>after
</span></div>
<div><span>one
two</span></div>
<div><span>one
two</span></div>
<div><span> 
</span></div>
對於起始標籤與第一個non-white-space字符間的white-space字符串,以carriage return()作為white-space合併單元的起始符,最後保留各合併單元的合併結果。
結束標籤與最後一個non-white-space字符間的white-space字符串,以carriage return()作為white-space合併單元的結束符,最後保留各合併單元的合併結果。
詞義單元間的white-space字符串,以carriage return()作為white-space合併單元的分界符,最後保留各合併單元的合併結果。
標籤內僅包含white-space字符串,那麼這些white-space字符串將被忽略。
** FF5.0 **
對於起始標籤與第一個non-white-space字符間和結束標籤與最後一個non-white-space字符間的white-space字符串將被忽略。
詞義單元間的white-space字符串,以carriage return()作為white-space合併單元的分界符,最後保留各合併單元的合併結果。
標籤內僅包含white-space字符串,那麼這些white-space字符串將被忽略。
** IE8+ **
對於起始標籤與第一個non-white-space字符間和結束標籤與最後一個non-white-space字符間的white-space字符串將被忽略。
詞義單元間的white-space字符串,合併為1個(ASCII space)字符。
標籤內僅包含white-space字符串,那麼這些white-space字符串將被忽略。
** IE5.5/6/7 **
對於起始標籤與第一個non-white-space字符間的white-space字符串將被忽略。
結束標籤與最後一個non-white-space字符間的white-space字符串,合併為1個(ASCII space)字符。
詞義單元間的white-space字符串,合併為1個(ASCII space)字符。
標籤內僅包含white-space字符串,那麼這些white-space字符串將被忽略。
合併單元:合併單元包含0到N個white-space字符串,最終合併為0到1個white-space字符
SGML描述B.3.1 Line breaks
specifies that a line break immediately following a start tag must be ignored, as must a line break immediately before an end tag. This applies to all HTML elements without exception.
<A>My favorite Website</A>
<A>
My favorite Website
</A>
望文生義翻譯法:標籤與正文間的line breaks要被忽略掉!也就是上下兩種HTML格式的渲染效果應該一致。實際上除了IE5.5/6/7外其他瀏覽器均遵守之一規定的。也許你會說上面的實驗不是已經證明chrome43不遵守這個法則嗎?其實
<A>
My favorite Website
</A>
HTML格式等價於<A>#x000A;My favorite Website#x000A;</A>而不是<A>#x000D;#x000A;My favorite Website#x000D;#x000A;</A>。現在大家都清楚了吧:)
繞到這裡我想大家都有點暈了,到底這個跟問題2有啥關係呢?先不要著急嘛,我們先記住兩點:
IE5.5/6/7中」結束標籤與最後一個non-white-space字符間的white-space字符串,合併為1個(ASCII space)字符」;
IE5.5/6/7中僅字符(串)可以作為詞義單元,而IE8+中inline-level element也作為詞義單元。
<span class="bk1 item"></span>
<span class="bk2 item"></span>
<br/><br/>
<span class="bk1 item">bk1</span>
<span class="bk2 item"></span>
IE5.5/6/7下等價於
<span>
</span>
<br/><br/>
<span>bk1
</span>
對比一下上面的規則,空隙自然就有了。
IE8+下等價於
<span>
</span>
<br/><br/>
<span>
</span>
inline-level element整體作為詞義單元,從外部看根本不用管裡面具體字符串是什麼。
後來者居上——CSS2.1描述中的inline-block
相對IE自定義的inline-block,CSS2.1引入的inline-block就好理解多了,它做了兩件事:
將元素變性為inline-level element;
讓元素產生新的BFC。
消滅尾行者
現在我們終於明白通過display:inline-block進行元素的水平排版時,為啥會有個討人厭的跟屁蟲了,那剩下的工作當然是去而快之啦。首先這個跟屁蟲實質上就是white-space字符串,而我們一般會輸入的就是ASCII space( )、ASCII tab()和讓HTML Markup更可讀的line breakscarriage return()、line feed( )。
那麼消滅尾行者的方式就只有兩個方向:1. 從根本上消除white-space字符串;2. 視覺效果上消除white-space字符串的影響。
犧牲HTML Markup可讀性
犧牲前
<span>one</span>
<span>two</span>
<span>three</span>
犧牲後1:一行搞定(一大坨代碼,會鬥雞眼的。。。)
<span>one</span><span>two</span><span>three</span>
犧牲後2:注釋銜接(通過JS獲取子元素數會有問題)
<span>one</span><!--
--><span>two</span><!--
--><span>three</span>
犧牲後3
<span>one</span
><span>two</span
><span>three</span>
然後@一絲姐說為展現效果犧牲結構是耍流氓,@HAX說這是」削足適履」。雖說這方法從根本上清除了white-space字符串,但那種醜不是一般人能接受的。
font-size:0大法
這種方式存在兼容性的問題,而且子元素需要重新設置font-size以保證後續採用em設置屬性值正確有效這個就是一個巨蛋疼的事了。
負margin-right法
原理是通過負margin-right將white-space字符收入盒子後方,而margin-right的屬性值需要根據font-size來決定,必須恰恰等於字形寬度的負數,否則會出現元素重疊的問題。(IE5.5/6/7不兼容這玩法)
引入HTML預編譯
引入如Jade等HTML模板引擎,開發和維護時採用可讀性可維護性更高的語言,而瀏覽器運行時則採用效率更佳但可讀性差甚至非人類友好的編碼,然後通過如sourcemap來做映射。
但若僅僅為解決本文的問題而引入HTML模板引擎,是不是小題大造了呢?
用float啦!
既然上述方式皆不爽,而你又熟知float的使用和注意事項,那直接換成float就好了。float的內容可參考《CSS魔法堂:說說Float那個被埋沒的志向》
總結
原來display:inline-block受委屈的這麼多年,現在總算沉冤得雪了!都怪CSS2沒有專門的布局模塊,逼得我們東拼西湊地拼頁面。所幸的是CSS3專設了Flexbox/Grid/Multi-columns Layout Modules,我們可以寄望更美好的將來了!
感謝
inline-block 前世今生
inline-block 未來
應不應該使用inline-block代替float
inline-block元素間間隙產生及去除詳解
有哪些好方法能處理 display: inline-block 元素之間出現的空格?
Fighting the Space Between Inline Block Elements
拜拜了,浮動布局-基於display:inline-block的列表布局
9.1 White space
9.3.2 Controlling line breaks
【今日微信公號推薦↓】
更多推薦請看《值得關注的技術和設計公眾號》
其中推薦了包括技術、設計、極客 和 IT相親相關的熱門公眾號。技術涵蓋:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、資料庫、運維、大數據、算法、IT職場等。點擊《值得關注的技術和設計公眾號》,發現精彩!