文章出處

前言

 每當來個需要既要水平排版又要設置固定高寬時,我就會想起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洗禮的我們對上述內容不禁會發出兩個疑問:

  1. 為啥block-level element設置了display:inline-block后還是垂直方向排列呢?
  2. 為啥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>
&#x20;&#x20;
&#x20;&#x20;
<span class="bk2">
has line break
</span>


 可以看到藍色塊k和紅色塊h間存在一個空格,而紅色塊k后也存在一個空格。可是代碼中我們看到藍紅色塊間有4個&#x20HTML實體,為啥只有一個空格呢?而紅色塊中僅僅換了行而已,怎么就有個空格呢?
 先拋結論:上面兩端代碼均是white space、white space collasping再作祟。

White space不僅是空格符那么簡單

 初看之下以為就是空格鍵,其實white space是一組空白字符和換行符組成。查看unicode字符集我們會發現有一大堆空白字符(如NO-BREAK SPACE等),但HTML只把ASCII space(&#x0020;)ASCII tab(&#x0009;)ASCII form feed(&#x000C)Zero-width space(&#x200B;)納入white space囊中,另外還將line break(換行符)carriage return(&#x000D;)line feed(&#x000A;)和它倆的組合納入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>&#x0020;&#x000D;&#x000A;&#x000D;&#x000A;before</span></div>
<div><span>&#x000D;&#x0020;&#x000A;&#x000D;&#x0020;&#x000A;before</span></div>
<div><span>after&#x000A;&#x000D;&#x000A;</span></div>
<div><span>after&#x000A;&#x000D;</span></div>
<div><span>after&#x000A;&#x0020;</span></div>
<div><span>one&#x000A;&#x0020;two</span></div>
<div><span>one&#x000A;&#x0020;&#x000D;&#x000A;&#x0020;two</span></div>
<div><span>&#x0009;&#x0020;&#x000C;&#x000D;&#x000A;</span></div>

** chrome43 **

  1. 對于起始標簽與第一個non-white-space字符間的white-space字符串,以carriage return(&#x000D;)作為white-space合并單元的起始符,最后保留各合并單元的合并結果。
  2. 結束標簽與最后一個non-white-space字符間的white-space字符串,以carriage return(&#x000D;)作為white-space合并單元的結束符,最后保留各合并單元的合并結果。
  3. 詞義單元間的white-space字符串,以carriage return(&#x000D;)作為white-space合并單元的分界符,最后保留各合并單元的合并結果。
  4. 標簽內僅包含white-space字符串,那么這些white-space字符串將被忽略。


** FF5.0 **

  1. 對于起始標簽與第一個non-white-space字符間和結束標簽與最后一個non-white-space字符間的white-space字符串將被忽略。
  2. 詞義單元間的white-space字符串,以carriage return(&#x000D;)作為white-space合并單元的分界符,最后保留各合并單元的合并結果。
  3. 標簽內僅包含white-space字符串,那么這些white-space字符串將被忽略。


** IE8+ **

  1. 對于起始標簽與第一個non-white-space字符間和結束標簽與最后一個non-white-space字符間的white-space字符串將被忽略。
  2. 詞義單元間的white-space字符串,合并為1個(ASCII space)字符。
  3. 標簽內僅包含white-space字符串,那么這些white-space字符串將被忽略。

** IE5.5/6/7 **

  1. 對于起始標簽與第一個non-white-space字符間的white-space字符串將被忽略。
  2. 結束標簽與最后一個non-white-space字符間的white-space字符串,合并為1個(ASCII space)字符。
  3. 詞義單元間的white-space字符串,合并為1個(ASCII space)字符。
  4. 標簽內僅包含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有啥關系呢?先不要著急嘛,我們先記住兩點:

  1. IE5.5/6/7中"結束標簽與最后一個non-white-space字符間的white-space字符串,合并為1個(ASCII space)字符";
  2. 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>&#x000A;</span>
    <br/><br/>
    <span>bk1&#x000A;</span>

    對比一下上面的規則,空隙自然就有了。
    IE8+下等價于

    <span>&nbsp;&#x000A;&nbsp;</span>
    <br/><br/>
    <span>&nbsp;&#x000A;&nbsp;</span>

    inline-level element整體作為詞義單元,從外部看根本不用管里面具體字符串是什么。

后來者居上——CSS2.1描述中的inline-block

 相對IE自定義的inline-block,CSS2.1引入的inline-block就好理解多了,它做了兩件事:

  1. 將元素變性為inline-level element;
  2. 讓元素產生新的BFC。

消滅尾行者

 現在我們終于明白通過display:inline-block進行元素的水平排版時,為啥會有個討人厭的跟屁蟲了,那剩下的工作當然是去而快之啦。首先這個跟屁蟲實質上就是white-space字符串,而我們一般會輸入的就是ASCII space(&#x0020;)ASCII tab(&#x0009;)和讓HTML Markup更可讀的line breakscarriage return(&#x000D;)line feed(&#x000A;)
那么消滅尾行者的方式就只有兩個方向: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


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()