前言
每當來個需要既要水平排版又要設置固定高寬時,我就會想起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洗禮的我們對上述內容不禁會發出兩個疑問:
- 為啥block-level element設置了
display:inline-block
后還是垂直方向排列呢? - 為啥inline-level element設置了
display:inline-block
后之間沒有詭異的間隙呢?
還記得楊過是如何變成神雕大俠的嗎?不就是被斷右臂后發現左手才是真愛嗎:)
好了,其實我的意思是拋棄過去對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>
** chrome43 **
- 對于起始標簽與第一個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,我們可以寄望更美好的將來了!
尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/5396037.html^_^肥仔John
感謝
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
文章列表
留言列表