CSS魔法堂:」那不是bug,是你不懂我!」 by inline-block

2021-12-25 前端大全

(點擊上方公眾號,可快速關注)

譯者:伯樂在線專欄作者 - ^-^肥仔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個&#x20HTML實體,為啥只有一個空格呢?而紅色塊中僅僅換了行而已,怎麼就有個空格呢?

先拋結論:上面兩端代碼均是white space、white space collasping再作祟。

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

初看之下以為就是空格鍵,其實white space是一組空白字符和換行符組成。查看unicode字符集我們會發現有一大堆空白字符(如NO-BREAK SPACE等),但HTML只把ASCII space( )、ASCII tab( )、ASCII form feed(&#x000C)和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> &#x000C;

</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>&nbsp;

&nbsp;</span>

<br/><br/>

<span>&nbsp;

&nbsp;</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職場等。點擊《值得關注的技術和設計公眾號》,發現精彩!

相關焦點

  • 應不應該使用inline-block代替float
    Inline-block是我們的另一種選擇。使用這種屬性可以模擬部分浮動的特徵,而不需要處理一些浮動帶來的問題。  Inline-block不是什麼新鮮話題了,估計你也用過。不過我最近才是用到這個屬性。之前的幾個站點上,有展示一系列照片的需求,我就用inline-block代替了浮動。
  • 最全的CSS自適應布局總結
    先思考清楚頁面的構造,理清各元素之間的關係,特別需要注意的是在不同的設備下需要有怎樣的展現,當你思路清晰找到最好的布局方案時,coding其實真的不需要多少時間。尺寸相關為什麼要先說尺寸呢?因為尺寸在布局中的作用非常核心,布局方式定位這些只是改變了元素之間的關係,沒有尺寸就什麼也不是。
  • 如何理解CSS的display屬性
    其最常見的屬性值有block,inline,none,table以及inline-block。最近的新寵為flex,因為它是專門為布局創建的display屬性。新出現的grid(最近仍比較活躍)是另外一個指定的布局屬性,其很快就會廣泛被使用。這篇文章比我預期的要長很多,你可以選擇性進行閱讀,但是我更希望你可以靜下心來閱讀整篇文章。
  • 一個超炫酷帶陰影的CSS/Sass 3D進度條
    僅使用CSS創建UI組件將訓練你的創造性思維,並且在本教程中,我們將通過製作進度條並動畫化來向大家展示一些如何創建更複雜形狀的技巧。注意:某些CSS屬性僅在現代瀏覽器中受支持。IE仍然不支持transform-style:preserve-3d,這是一個用於創建嵌套3D結構的關鍵屬性;所以進度條在IE瀏覽器中將是扁平/無效的。
  • css知識的一些總結(一)
    一.css 盒模型介紹    css盒子模型分為標準盒子模型,IE盒子模型。
  • 「你懂我的意思嗎?」「不懂」
    如果在年終總結匯報的時候抓住機會,那可能就能得到升職加薪。所以,表達能力真的可以決定你的職場未來。當然,比表達力更為重要的就是精準表達的能力。我們不光要會表達,還要用最簡潔易懂的語言,在最短的時間內精準表達。你能順利通過麥肯錫的「30秒電梯演講」嗎?有一回,麥肯錫公司為一家重要的大公司做諮詢。
  • CSS盒模型詳解
    讀完這篇文章,你可以了解:1.什麼是盒模型?
  • Zippo:陪你這麼多年,你從未懂我
    美劇「康斯坦丁」我能感知你每次打開我時的每一種心情煩躁的,難過的,無奈的偶爾是喜悅的可你卻從未懂我二戰期間,我是美軍的寶貝。貓王限量版我的 「開關」音被寫進歌曲當中,你也可以在唱片封面上、搖滾歌手的皮膚上、滾石樂隊的照片中看到我的樣子。
  • 學科沙龍 程序史上的著名BUG你知道哪些
    而當日曆越來越接近1999年12月31日時,人們越來越擔心在千禧年的新年夜大家的電腦系統都會崩潰,因為系統日期會更新為1900年1月1日而不是2000年1月1日,這樣可能意味著無數的災難事件,甚至是世界末日。       全球花了上億的美金用來升級系統。而且,也發生了一些小的事故:在西班牙,停車場計費表壞了;法國氣象局公布了19100年1月1日的天氣預報(厲害了我的哥,預測未來!)
  • 關於3傻切甲bug修復
    8.13的更新28.3以及後續的3次熱修之後,存活了長達4個大版本的切甲bug終於基本被修掉了。
  • 最好的關係,是我懂你
    他隨即打開,讀了起來:親愛的先生和朋友:記得你曾對我說過,不論遇到什麼事,我都可得到你的幫助。現在我就有一件難於啟齒的事要求助於你。查理眼看是不行了,望你能來幫我一把,不要讓我在他臨終之際1個人守在他身邊。他眼下還能起床,但醫生對我說,他恐怕是過不了這個星期了。此時此刻,要日夜守著他,我已力不從心。一想到即將來臨的最後時刻,我便無比恐懼。
  • 一個bug讓你在沒有接聽的情況下仍能被監視
    iPhone或iPad等蘋果設備的麥克風和前置攝像頭來監聽(或監視)你,即使是在被你沒有接聽的情況下。想要利用這個bug,呼叫者只需要向你發起FaceTime通話,在你接聽之前,快速將自己添加為FaceTime群組的另一個聯繫人。這番操作將導致你的iOS設備的麥克風被激活,進而允許呼叫者監聽你房間裡正在發生的一切。更糟糕的是,如果你此時試圖通過按下音量減小按鈕或電源按鈕來嘗試靜音或解除呼叫,那麼你的iOS設備的前置攝像頭也會被激活。
  • 【Mac空間】你不得不看的Mac OS High Sierra 10.13的bug及其解決方法
    首先我個人一貫的不贊成任何一臺主力機去升級10.13beta版本,個人多年經驗,每次出較大改動的系統,總會有很多bug,尤其是最初的幾個
  • 幾個你絕對不知道的我的世界小技巧!
    2、甘蔗是可以在PE中進行催熟的哦~  3、這是一個不需要谷粉就能加快植物的生長速度,那就是將不同的作物種在一個田裡就可以了  4、在我的世界0.12.1中或PC中,當你射出箭的那一瞬間將弓換成附魔劍的話,那麼受到的箭傷害也就是附魔劍的,為什麼呢?那就是系統會檢查你拿的是什麼武器。  5、知道嗎?