日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

前端布局基礎(chǔ)概述

 綁架外星人 2019-03-24

1. 前言

前端圈有個“?!保涸诿嬖嚂r,問個css的position屬性能刷掉一半人,其中不乏工作四五年的同學。在公司一直有參與前端的基礎(chǔ)面試,深感這個“?!辈皇莻€玩笑。

然而,我覺得實際比例可能會更高,甚至很多面試官自己也未必真正掌握。因為大部分前端同學,可能不知道初始包含塊的概念,或知道但對這個概念理解有誤。

造成這種現(xiàn)象的原因主要有兩方面,一方面是在介紹這個知識點時,網(wǎng)上有謬誤的文章太多,國內(nèi)外亦如此(MDN也名列其中),導致很多同學被誤導(我一開始也是),而且這種錯誤被代代相傳;另一方面可能是我們平時不太注重概念的定義、自身對待知識的態(tài)度還不夠嚴謹、缺乏驗證精神和系統(tǒng)總結(jié)的習慣。

一次偶然的機會,我發(fā)現(xiàn)了這種謬誤,并找到了W3C組織對初始化包含塊的官方定義,也為了讓剛?cè)肭岸巳Φ耐瑢W少走一些彎路,在此我想借本文分享給大家(詳述請見5.5. 包含塊章節(jié)),也系統(tǒng)分享一下,本人在前端布局基礎(chǔ)方面積累的淺薄經(jīng)驗。(因為是系統(tǒng)概述,所以篇幅會比較長,希望各位讀者有心理準備)

2. 什么是前端布局基礎(chǔ)?

前端布局方案主要有三種:

  • 傳統(tǒng)布局方案(借助浮動、定位等手段)
  • flex布局方案
  • grid布局方案

這些方案都能夠解決布局問題,而且每個方案都有各自的理論基礎(chǔ),那么哪一個方案的基礎(chǔ)理論可以稱得上是前端布局基礎(chǔ)?要回答這個問題,我們還得深入去了解這三種方案的特性。

傳統(tǒng)布局方案,需要使用者熟練掌握元素的分類及布局特性、浮動原理和定位原理等眾多基礎(chǔ)知識,方能在解決各類前端布局問題時游刃有余,這不僅學習成本大,而且實現(xiàn)的復雜度也高,實現(xiàn)的CSS代碼也不夠精簡、優(yōu)雅。但由于其基礎(chǔ)知識來源于CSS2,所以瀏覽器兼容性最好,對于用戶是友好的。

flex布局方案,正是為了解決傳統(tǒng)布局方案的種種不便,而提出的一種新型改進方案,它不再需要借助浮動和定位等布局手段,而是通過父元素(flex box)單方面配置相關(guān)的CSS屬性來決定子元素的布局規(guī)則,且在大多情況下無需子元素(flex item)參與,就能完成子元素間的布局問題,不僅學習成本低(公司之前有幾個后端工程師亦能快速上手),且大大簡化了布局的實現(xiàn)復雜度,CSS代碼也更加精煉。美中不足的是IE10才開始支持,且需要使用-ms-前綴(IE11無需)。

雖然現(xiàn)今的手機多使用的是現(xiàn)代瀏覽器,對flex支持度較好,然而并不是每一款手機都如此:筆者曾在一個移動端項目采用過flex布局方案,然而公司的測試同學在“華為榮耀5”的自帶瀏覽器,檢測到無法支持flex布局,我們能夠跟測試的同學說,是這款華為手機的瀏覽器有問題嗎?顯然不能。于是故筆者在項目早期就及時放棄了flex布局方案,改用傳統(tǒng)布局方案實現(xiàn),避免了后面大規(guī)模的改動。

grid布局方案,是由微軟提出,相對于傳統(tǒng)布局方案和flex布局方案,它是一種二維布局方案,在IE10開始支持,但需要使用-ms-后綴(IE11+不再需要)。

總的來說,這三類方案都能基本解決日常的前端布局問題,且從易用性、靈活性和強大性來說,flex布局和grid布局更是未來的趨勢。但是從當前各版本瀏覽器在用戶市場上的使用情況和各方案的瀏覽器兼容性來看,傳統(tǒng)布局方案對用戶最友好,具有一定的不可替代性,所以我覺得,傳統(tǒng)布局方案是最應該先掌握好的,尤其是對于在to B企業(yè)工作的前端同學來說。

所以本文將詳細介紹的“前端布局基礎(chǔ)”,指的是圍繞著“傳統(tǒng)布局方案”的眾多CSS知識,其主要內(nèi)容來源于CSS2規(guī)范。

3. 為什么要學好前端布局基礎(chǔ)?

頁面寫多了的前端同學,我想應該都會有這樣一個深刻的感受:在編寫頁面時,經(jīng)常會遇到不同場景的布局問題,我們不僅需要針對特定的場景選定可實現(xiàn)的布局實現(xiàn)方案,而且需要考慮未來可能發(fā)生的變化。

而要做好這一點,就需要扎實的前端基礎(chǔ)作為依托。

所以在我看來,學好前端布局基礎(chǔ),其目的是為了在面對不同場景的布局問題時,能夠提出一種合理的布局方案:既能解決問題,又能最大程度地擁抱變化。

4. 量化布局方案的合理性

前面提到過的“解決問題”、“擁抱變化”,僅僅是合理布局方案的兩大核心目標,如果想要讓目標更好地落地,我們?nèi)孕枰恍┝炕侠硇缘脑瓌t,來提升對目標的方向感,以讓目標變得更加可執(zhí)行。

說到量化“解決問題”這個目標,對于即寫即呈現(xiàn)的前端代碼來說,我們可以很直觀地判斷一種方案是否可行,所以不需要太多的量化手段,我們主要是要量化“擁抱變化”這個目標。

要想量化“擁抱變化”這個目標,我們首先得清楚“變化”有哪些。筆者根據(jù)過往的開發(fā)經(jīng)驗,將變化分為兩大類:一是布局需求的變化,二是運行環(huán)境的變化。

而針這這兩類變化,我提出如下量化原則:

一、對于布局需求的變化,可以做到:

  • 方便快速定位需修改的位置
  • 能夠不花或用最少的修改成本應對變化

二、對于運行環(huán)境的變化,可以做到:

  • 在不同瀏覽器均有正確或良好的顯示

如果一個方案能夠體現(xiàn)以上幾點原則,我認為可以稱得上是一個合理的方案。最后,我將布局實現(xiàn)方案的合理性歸納為:方案在滿足正確性的前提下,其實現(xiàn)邏輯規(guī)范、實現(xiàn)職責分明且擁有良好的瀏覽器兼容性。

下面我們正式開始介紹與“傳統(tǒng)布局方案”相關(guān)的布局基礎(chǔ)知識。

5. 布局基礎(chǔ)要點

5.1. CSS標準盒模型(或W3C盒模型)

一個web頁面是由眾多html元素拼湊而成的,而每一個html元素,都被解析為一個矩形盒,而CSS盒模型就是這種矩形盒的解構(gòu)模型。CSS盒模型,它由內(nèi)到外、被四條邊界Content edge、Padding edge、Border edge和Margin edge劃分為四個區(qū)域:Content area、Padding area、Border area和Margin area,在形狀上,Content area(又稱content-box)是實心矩形,其余是空心環(huán)形(空心部分是Content area),如下圖所示:

640

CSS盒模型-區(qū)域劃分圖

此外,每個區(qū)域都有其特定的作用:Content area,是當前元素用來容納所有子孫元素;Padding area,是當前元素用來隔離自身和子孫元素;Border area是當前元素用來顯示自身的輪廓;Margin area,是當前元素用來隔離自身和相鄰元素。理解每個區(qū)域的作用和職責至關(guān)重要,有助于我們寫出優(yōu)雅、清晰的布局代碼。

641

CSS盒模型-區(qū)域作用圖

而每個區(qū)域的尺寸,又分別由特定的CSS屬性來控制,如下圖所示:

642

CSS盒模型-屬性控制圖

這些CSS尺寸屬性(width、height、padding、border和margin),相當于一個個hook,我們可以通過設(shè)置這些“hook”來達到調(diào)整元素尺寸的目的。

5.2. box-sizing(CSS3屬性)

5.2.1. box-sizing的作用

box-sizing,顧名思義,其作用與設(shè)置CSS box的尺寸大小有關(guān),而CSS box又可細分為:

  • content-box(即content area)
  • padding-box(=content area + padding area)
  • border-box(=content area + padding area + border area)
  • margin-box(=content area + padding area + border area + margin area)

簡單來說,box-sizing的作用就是告訴瀏覽器:CSS屬性width和height是用于設(shè)置哪一種box的尺寸,在W3C標準中,box-sizing的值僅有content-box和border-box(firefox則額外支持padding-box)。所以,

當box-sizing的值為content-box(默認值)時,有:

width = content-width;

height = content-height;

當box-sizing的值為border-box時,有:

width = content-width + padding-left + padding-right + border-left-width + border-right-width;

height = content-height + padding-top + padding-bottom + border-top-height + border-bottom-height;

關(guān)于box-sizing的作用,還有另一種表述:告訴瀏覽器,是使用W3C盒模型,還是使用IE盒模型。

5.2.2. box-sizing的瀏覽器兼容性

box-sizing是CSS3屬性,在IE8+(包含IE8)開始支持,然而在IE8,box-sizing的值為border-box時,不能與min-width, max-width, min-height或max-height的一起使用,因為IE8對min-*和max-*的解析,仍是作用于content-box,不受box-sizing屬性控制。

5.2.3. box-sizing的產(chǎn)生原因

僅僅掌握box-sizing的基礎(chǔ)使用,是無法真正理解box-sizing的作用,所以要想把box-sizing用好,我們還得從CSS盒模型的發(fā)展史來深入理解box-sizing的產(chǎn)生原因。

在CSS的發(fā)展歷程中,有兩個版本,一個是IE盒模型,另外一個是W3C盒模型。IE盒模型,在IE5-(包含IE5)和navigator4上均有使用;而W3C盒模型,在IE6+(包含IE6)標準模式開始得到支持。兩種版本的盒模型,其實在模型結(jié)構(gòu)上是一致的,只是with和height屬性的計算規(guī)則不一樣,其區(qū)別,等價于“box-sizing的兩個屬性值border-box和content-box的區(qū)別“,如下圖所示:

643

IE盒模型和W3C盒模型的區(qū)別

在了解了CSS盒模型的發(fā)展歷程,以及后來新增的box-sizing的開始支持時間,我們不難發(fā)現(xiàn):

  • IE5-采用IE盒模型
  • IE6、7的標準模式放棄了IE盒模型,轉(zhuǎn)為使用W3C盒模型
  • IE8+借助box-sizing,又重新提供了對IE盒模型的支持

對于IE盒模型,我們看到了W3C組織先去后留的反復態(tài)度,我不禁提出以下兩點疑惑:

問題一: 為什么W3C組織在制定盒模型標準時,一開始會放棄IE盒模型,而重新建立以content-box為計算規(guī)則的W3C盒模型?W3C盒模型比IE盒模型好在哪里?

問題二:為什么在CSS3中,又重新提供了對IE盒模型的支持(box-sizing設(shè)置為border-box),又是基于哪方面的考慮?

關(guān)于第一個問題,本人并沒有找到相關(guān)的官方說明,但我比較認可的一種說法是:

在日常生活中,我們在放東西時,會關(guān)心東西放到多大的盒子里面,這里的“多大”,往往指的是盒子的容量,而不是整個盒子的尺寸。而HTML元素也被看成是一個盒子、一個容器,相應地,我們也會更關(guān)注其內(nèi)容區(qū)域的尺寸,也更希望對內(nèi)容區(qū)域有更強的控制力。所以,從存儲的角度來看,W3C盒模型更符合這種認知,借助width和height,我們可以通過聲明的方式,直接設(shè)置conent-box的尺寸。而如果采用IE盒模型,我們只能先設(shè)置整個盒子的尺寸(border-box),最后由瀏覽器自動計算出content-box的尺寸,顯得對content-box尺寸的控制力較弱。

關(guān)于第二個問題,我認為有以下幾個原因:

1. 有助于復用基于IE盒模型開發(fā)的CSS代碼;

2. IE盒模型的“遺老遺少”可以延續(xù)計算習慣;

3. 部分html元素,在解析時依然采用IE盒模型的計算規(guī)則(這樣的元素有select、button),使用IE盒模型有助于保持一致性;

4. 從元素布局的角度來看,IE盒模型的width和height的語義更符合人類的直觀認知(盒子的尺寸、輪廓應該以border為界);

5. 在彈性布局和響應式布局場景,IE盒模型比W3C盒模型表現(xiàn)更佳(更容易實現(xiàn)、瀏覽器兼容性更好),如設(shè)置某個元素的寬度始終占當前行總寬度的固定百分比(小于100%),并且該元素擁有固定像素的padding;

舉個例子:設(shè)置一個元素,其寬度分別為當前行的40%,且該元素的padding固定為10px。

IE盒模型的實現(xiàn)方案:

方案一: 使用一個div即可實現(xiàn),直接設(shè)置width為40%,padding為10px;

W3C盒模型的實現(xiàn)方案:

方案一:使用兩個div模擬實現(xiàn),外層div的width設(shè)置為40%,內(nèi)層div的padding為10px,                   width為auto;

方案二:使用一個div即可實現(xiàn),但是需要借用CSS3的calc函數(shù),動態(tài)計算其內(nèi)容區(qū)域的寬度,即width為calc(40% – 20px), padding為10px;

顯然,IE盒模型的實現(xiàn)方案更加簡潔,而且瀏覽器兼容性更好。

對上述兩個問題的解答,其實也是對IE盒模型和W3C盒模型的一個比較。我們可以從比較中,明晰兩種盒模型各自的優(yōu)缺點。同時,經(jīng)過大量的實踐經(jīng)驗證明和充分討論,IE盒模型總體上是優(yōu)于W3C盒模型,這也是IE盒模型能夠“王者歸來”,被W3C組織重新啟用的真正原因。

于是乎,為了重新在新規(guī)范中支持IE盒模型,也為了向后兼容W3C盒模型,W3C組織在CSS3中添加了box-sizing屬性,用于切換這兩種盒模型。

5.2.4. 對box-sizing的評價

在我看來,在CSS3中添加box-sizing其實是一種比較trick的彌補方式。雖然這種設(shè)計能重新提供對IE盒模型的支持,但是在某種程度上,造成了CSS屬性width和height具有二義性,使其職責變得不單一。然而這似乎又是最可取的修正方案了,因為在網(wǎng)上已經(jīng)存在了大量基于W3C盒模型開發(fā)的網(wǎng)頁,后續(xù)的修正方案不得不考慮向后兼容。我們只能在不合理設(shè)計的基礎(chǔ)上,再次用不優(yōu)雅的設(shè)計來解決新的問題。

如果能夠穿越時空,回到W3C組織在討論“如何設(shè)計標準盒模型”時,我認為更合適的設(shè)計方案是添加新的屬性單獨用于設(shè)置content-box的尺寸,而保留IE盒模型width和height原來的語義。這樣就不會有后來的box-sizing屬性。

我猜想W3C組織也想過這種方案,但是當時可能認為:

1. 直接設(shè)置元素border-box尺寸的意義不大,且border-box的尺寸設(shè)置也能夠通過設(shè)置content-box的尺寸來實現(xiàn);(其實同時兩種支持content-box和border-box尺寸的設(shè)置也無妨,完全可以當做是語法糖)

2. 設(shè)置content-box尺寸又屬于高頻操作,若新增的屬性命名為content-width或content-height則顯得名稱太長;(命名為cwidth和cheight也行)

基于這兩點,最終提出了用width和height來設(shè)置content-box尺寸的解決方案,也就是如今我們看到的W3C盒模型。

縱觀CSS盒模型的發(fā)展史和box-sizing的創(chuàng)建原因,感觸比較深的就是:不合理的設(shè)計并不是總會被修正,因為既有實現(xiàn)的廣泛應用,會使得其被繼續(xù)遵循。而后續(xù)的新增設(shè)計,也是建立在先前不合理設(shè)計的基礎(chǔ)上。這是否也驗證了黑格爾的哲學名言:存在即合理?

關(guān)于對box-sizing的評價和思考,可能顯得有一些馬后炮,一些猜想也可能只是筆者的憑空臆想,并非W3C組織原意。在這里只是為了分享我對重構(gòu)的一些思考,也是為了與和我有同樣疑惑的同學做個交流。

5.2.5. box-sizing的最佳實踐

在這里主要回答三個問題:

問題一:box-sizing的值,取content-box好,還是取border-box值好?

如果最低需要兼容IE6、7,那么box-sizing不可使用,只能使用W3C盒模型;

如果最低只需兼容IE8,那么使用content-box在功能上完全沒有問題,只是在一些彈性布局和響應式布局實現(xiàn)上,會稍微麻煩一點;而border-box雖然在這些方面表現(xiàn)更好,但是不能和IE8的min-width、min-height、max-width和max-height四個屬性一同使用,使用的話就要稍微注意一下;

如果最低只需兼容IE9,那么本人覺得,全局配置取content-box更為合適,局部配置二者均可。原因如下:

1. CSS3提供了calc函數(shù)(IE9+),使得W3C盒模型有了強有力的助攻,在彈性布局和響應式布局的表現(xiàn),與IE盒模型無異;

2. 默認優(yōu)于配置原則:我個人認為,“默認優(yōu)于配置”,特別是在reset.css這種架構(gòu)級、平臺級的配置文件,要盡量避免對未來可能引入的模塊有侵入性。譬如,我們在一個項目中時常需要引入第三方組件,如果這個組件沒有強聲明box-sizing,那么其默認使用的就是W3C標準盒模型,如果在全局的reset.css中設(shè)置box-sizing的值為border-box以選用IE盒模型,那么就會影響到這一類默認基于W3C盒模型的第三方組件的樣式。這里也給我們提了一個醒,在封裝組件時,記得強聲明box-sizing,哪怕你使用默認的content-box。

總之,大部分場景二者可以互換,只是使用理念不一樣。小部分場景border-box更具優(yōu)勢,但隨著calc函數(shù)的支持,這種優(yōu)勢已經(jīng)不再,相反content-box是默認值的優(yōu)勢愈加明顯。

我個人建議是:全局使用默認W3C盒模型(你的CSS代碼最低能夠兼容IE6/7,在IE8也可以和min-*和max-*一起使用),局部場景二者均可(僅把IE盒模型當作是一種布局技巧來使用)。你喜歡全局使用IE盒模型也是可以的,只要確認項目只需要兼容到IE8,即便有可能影響到引入的第三方組件,也是有辦法處理的。

問題二:如果想要全局使用IE盒模型,那么在reset.css中,該怎樣設(shè)置box-sizing?

這里提供一個參考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
html {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
*, *:before, *:after {
-webkit-box-sizing: inherit;
-moz-box-sizing: inherit;
box-sizing: inherit;
}

這樣設(shè)置的好處有:

1. 子元素的盒模型類型,默認由父元素決定,方便組件統(tǒng)一設(shè)置;

2. 支持低版本的瀏覽器:Safari (< 5.1), Chrome (< 10), and Firefox (< 29);

問題三:Bootstrap3開始,全局使用IE盒模型(box-sizing取border-box),又是基于怎樣的考慮?怎么協(xié)調(diào)好與基于標準盒模型開發(fā)的第三方組件的關(guān)系?

眾所周知,BS2還考慮對IE7的兼容,而BS3徹底放棄了對IE7的兼容,并將box-sizing設(shè)置為border-box。關(guān)于這一點,可見“Bootstrap 3 released”官方發(fā)布的change list,摘錄如下:

644

從以上直白的表述中:Better box model by default(默認使用更好的盒模型),我們可以看出BS作者是IE盒模型的擁躉。作者也把理由羅列了出來,其核心內(nèi)容也是如前面所提到的,IE盒模型在響應式布局上的良好表現(xiàn)。補充的一點是,如果不全局設(shè)置border-box,而每個組件及其子組件單獨設(shè)置,維護起來將是個夢魘(作者在官方編號為12351的issure中有提到)。

而關(guān)于BS如何處理好與基于標準盒模型開發(fā)的第三方組件的關(guān)系,亦可參見編號為12351的issue:”Move away from * {box-sizing: border-box } to play nice with 3rd party scripts”

作者在issue中,霸氣又委婉地回應:

1. BS并不考慮對第三方組件和框架的支持。作者委婉地說,BS是一個大項目,活躍維護者也主要是四個人,顧不來所有人的需求啊~(但感覺作者是在說,BS是個又大又全的框架,你丫還搞第三方組件干嘛呀)

2.IE盒模型,用了大家都說好,為什么第三方組件不轉(zhuǎn)過來支持IE盒模型?。ü皇氰F粉)

本章節(jié)從box-sizing的作用、瀏覽器兼容性、產(chǎn)生原因、評價和最佳實踐這5個切入點,來講解box-sizing屬性,以期加深各位同學對這個屬性的理解和掌握。特別要強調(diào)的一點是,如果剛接手某個項目,在編寫CSS代碼前,先看看項目是否有全局配置box-sizing,并根據(jù)具體的取值來選用相應的尺寸計算規(guī)則。

5.3. 元素的分類及其布局特性

5.3.1. 元素的分類

從元素的布局特性來分,主要可以分為三類元素:block-level(塊級)元素、inline-level(行內(nèi)級)元素和inline-block-level(行內(nèi)塊級)元素,我們可以對其下個定義:

5.3.1.1. 塊級元素

display屬性取block、table、flex、grid和list-item等值的獨占一行顯示的元素。

5.3.1.2. 行內(nèi)級元素

display屬性取inline值的可在同一行內(nèi)排列顯示的元素。

5.3.1.3. 行內(nèi)塊級元素

display屬性取inline-block、inline-table、inline-flex和inline-grid等值的兼具塊級元素和行內(nèi)級元素布局特性的元素。

友情提示:

1)關(guān)于各類元素display的取值,實際已全部羅列,但為了保證定義能擁抱變化(未來可能引入新的display屬性值),在羅列時使用了等字;

2)w3c官方文檔,把display屬性值為inline、inline-block、inline-table的元素,統(tǒng)稱為inline-level元素,我不太喜歡也不太認可這種泛泛的分類,本文重新定義了一個“inline-block-level元素”的概念,來對“inline-level元素”進行了細分,并將inline-blocks、inline-tables單獨分類為inline-block-level元素,原文檔如下:“The following values of the ‘display’ property make an element inline-level: ‘inline’, ‘inline-table’, and ‘inline-block’.”

5.3.2. 元素的布局特性

5.3.2.1. 塊級元素(block-level)的布局特性

對于塊級元素,有如下幾個布局特性:

  1. 獨占一行(width默認為100%,height為0);
  2. 可以設(shè)置任何尺寸相關(guān)的屬性(width、padding、margin和border);

5.3.2.2. 行內(nèi)級元素(inline-level)的布局特性

在講行內(nèi)級元素的布局特性之前,我們先了解一下行內(nèi)級元素的分類,其可再細分兩類元素:

1)可置換行內(nèi)元素

在MDN中,其對“可置換行內(nèi)元素”的定義如下:

645

按字面翻譯,“可置換行內(nèi)元素”,是展示內(nèi)容不在CSS作用域內(nèi)的元素。這句話是不是不好理解?我們可以換另外一種方式理解:“可置換行內(nèi)元素”,是這樣一類元素,其展示的內(nèi)容是通過元素的src、value等屬性或CSS content屬性從外部引用得到的,可被替換的。隨著內(nèi)容來源或內(nèi)容數(shù)量的變化,可置換元素本身也會有水平和垂直方向上尺寸的變化。典型的可替換元素有 <img>、<object> 、<video>  和 <embed>,表單類的可替換元素有 <textarea></textarea>和<input> ,某些元素只在一些特殊情況下表現(xiàn)為可替換元素,例如<audio> 、<object>、<canvas>和<applet>。

特別地,通過 CSS content 屬性來插入的對象又被稱作 匿名可置換元素。

2)不可置換行內(nèi)元素

“不可置換行內(nèi)元素”其實就是我們常見的一類行內(nèi)元素,這一類行內(nèi)元素有<a>和<span>等?!安豢芍脫Q行內(nèi)元素”是相對于“可置換行內(nèi)元素”的,其展示的內(nèi)容是在CSS作用域范圍內(nèi)的,是不可替換的。

言歸正傳,行內(nèi)級元素有如下幾個布局特性:

  1. 在一行內(nèi)可以與多個同類型的元素按從左到右的順序排列;
  2. 不可置換行內(nèi)元素不能設(shè)置width、height和垂直方向上的margin,而可置換行內(nèi)元素則可以;
  3. 在水平和垂直方向上的對齊方式,行內(nèi)級元素分別受父元素的text-align屬性和自身vertical-align屬性的控制(父元素是table-cell元素時,也受父元素的vertical-align屬性控制),在水平方向上默認左對齊,在垂直方向上默認在行框的baseline基線上顯示(“行框”的概念,會在后面深入講解);

友情提示:

1)有時候我們不必太糾結(jié)于哪些行內(nèi)元素是可置換行內(nèi)元素,因為有些瀏覽器(如chrome)的默認樣式(user agent stylesheet),會將這一類元素重置為inline-block元素,我們可以統(tǒng)一把可置換行內(nèi)元素理解為inline-block元素,因為其布局特性與inline-block-level元素相同。

2)當inline-level元素水平排列時,兩兩之間可能會出現(xiàn)大約6px的空白,這是由元素間的空白字符(換行符、空格或制表符)產(chǎn)生,如下圖所示:

686

清除方法有很多,本人習慣用浮動的方式來處理,其它方法可自行g(shù)oogle。

5.3.2.3. 行內(nèi)塊級元素(inline-block-level)的布局特性

行內(nèi)塊級元素兼具block-level元素和inline-level元素的布局特性,主要體現(xiàn)為:

  1. 排列方式與行內(nèi)級元素同,不獨占一行,在一行內(nèi)按從左到右的順序排列;
  2. 水平和垂直方向上的對齊方式與行內(nèi)級元素同;
  3. 和塊級元素一樣,可以設(shè)置任何尺寸屬性(但width默認為0);

注:我們不難發(fā)現(xiàn),其實可置換行內(nèi)元素,其布局特性與inline-block-level元素相同。

5.4. 格式化上下文(Formatting Context)

格式化上下文,它指的是具有某種CSS格式化規(guī)則(布局規(guī)則)的上下文環(huán)境,在這個上下文環(huán)境內(nèi)的所有子元素,都將根據(jù)其特定的CSS格式化規(guī)則來進行排列。

我們可以給某個作為容器的元素指定特定的格式化上下文,也就是說我們可以定義一個具有特定布局規(guī)則的渲染區(qū)域。常見的格式化上下文有BFC(CSS2.1 規(guī)范)、IFC(CSS2.1 規(guī)范)、 FFC(CSS3規(guī)范新增)和GFC(CSS3規(guī)范新增),具體介紹如下:

5.4.1. BFC

5.4.1.1. 定義

BFC, 全稱是block formatting context,它是一個獨立封閉的渲染區(qū)域,在這個區(qū)域內(nèi)的所有元素,從區(qū)域的頂部起,一個接一個地根據(jù)自身的布局特性進行排列:在這個區(qū)域內(nèi)的塊級元素 ,按從上到下的順序顯示,相鄰的塊級元素可以使用margin隔離,但在垂直方向上相鄰的塊級元素會發(fā)生margin合并;在這個區(qū)域內(nèi)的inline-level或inline-level-block元素,則按從左到右的順序顯示(W3C組織說BFC內(nèi)部的元素都是一個接一個地垂直顯示,我覺得不是很嚴格,因為BFC內(nèi)部也可以容納inline-level和inline-level-block元素,所以這里我的解釋和W3C還是稍微有一些不一樣)。具有BFC格式化環(huán)境的元素,我們稱之為BFC元素,可以說,BFC定義了BFC元素content區(qū)域的渲染規(guī)則。

看到這段描述,是不是覺得BFC的渲染規(guī)則,不就是文檔流的默認布局規(guī)則嗎?確實很像,但不完全等同。BFC元素內(nèi)部的渲染規(guī)則和普通塊級元素內(nèi)部的渲染規(guī)則,還是有一些不同的,我們將在5.4.1.3. 特性一節(jié)詳述。

5.4.1.2. 創(chuàng)建方式

創(chuàng)建BFC元素的方式有如下幾種(摘自MDN BFC):

  • 根元素或其它包含它的元素
  • 浮動元素 (元素的 float 不是 none)
  • 絕對定位元素 (元素的 position 為 absolute 或 fixed)
  • 內(nèi)聯(lián)塊 (元素具有 display: inline-block)
  • 表格單元格 (元素具有 display: table-cell,HTML表格單元格默認屬性)
  • 表格標題 (元素具有 display: table-caption, HTML表格標題默認屬性)
  • overflow 值不為 visible 的塊元素
  • display: flow-root
  • contain為以下值的元素: layoutcontent, 或 strict
  • 彈性項 (display: flex 或 inline-flex元素的子元素)
  • 網(wǎng)格項 (display: grid 或 inline-grid 元素的子元素)
  • 多列容器 (元素的 column-count 或 column-width 不為 auto, 包括 column-count: 1的元素)
  • column-span: all 應當總是會創(chuàng)建一個新的格式化上下文,即便具有 column-span: all 的元素并不被包裹在一個多列容器中。

5.4.1.3. 特性

BFC元素具有如下特性:

1. 對應一個獨立、封閉的渲染區(qū)域,子元素的CSS樣式不會影響B(tài)FC元素外部;

舉個例子,我們分別用連續(xù)的兩個塊級元素,一個是普通塊級元素,另一個是BFC元素(均使用綠色背景),分別包裹一個margin-top為20px的子元素(黃色背景),對比其布局效果:

646

說明:

  • 普通塊級元素,其子元素的margin-top,不會隔開自身與父元素(普通塊級元素),但是會作用到父元素外部(將父元素和叔伯元素或祖父元素隔開);
  • BFC元素,作為一個獨立、封閉的渲染區(qū)域,其子元素的margin-top,則會隔開自身與父元素(BFC元素),而不會影響到父元素外部;

2. 浮動子元素參與BFC父元素的高度計算,也就是BFC元素能夠識別浮動元素(將元素聲明為BFC元素,也是clearfix解決父元素塌陷問題的一種常用方法);

舉個例子:

647

說明:

  • BFC元素,能夠識別浮動子元素,浮動子元素參與BFC元素的高度計算,不會出現(xiàn)“高度塌陷”問題;
  • 普通塊級元素,不能夠識別浮動子元素,會出現(xiàn)“高度塌陷”問題;

3. 占據(jù)文檔流的BFC元素(可使用overflow: auto創(chuàng)建),能夠識別浮動的兄弟元素;

舉個例子:

647

說明:

  • 普通塊級元素,不能夠識別浮動的兄弟元素,會被浮動的兄弟元素覆蓋部分內(nèi)容;
  • 占據(jù)文檔流的BFC元素(可使用overflow: auto創(chuàng)建),能夠識別浮動的兄弟元素,不會被浮動的兄弟元素覆蓋,與之同行顯示;

4. 占據(jù)文檔流的BFC元素(可使用overflow: auto創(chuàng)建),width為auto時,會占滿當前行的剩余寬度;

舉個例子:

648

說明:

  • 文檔流中的BFC元素, width為auto時,會占滿當前行的剩余寬度;

5.4.2. IFC

5.4.2.1. 定義

IFC, 全稱是inline formatting context,其內(nèi)部的元素,在水平方向上,一個接一個地顯示;在垂直方向上,每個元素可以設(shè)置不同的對齊方式;IFC內(nèi)部的元素,被一行行的矩形框所包含,這些虛擬的矩形框,我們稱為行框(line box)。IFC的作用區(qū)域,可以看成是包含其所有子元素的行框組成的矩形區(qū)域。

5.4.2.2. 創(chuàng)建方式

和BFC相比,它的創(chuàng)建方式是被動的、隱式的,是由所包含的子元素來創(chuàng)建:只有在一個區(qū)域內(nèi)僅包含可水平排列的元素時才會生成,這些子元素可以是文本、inline-level元素或inline-block-level元素。

5.4.2.3. 特性

1. IFC內(nèi)部的元素,按從左到右、從上到下的順序排布;

2. IFC內(nèi)部的每個元素,都可以通過設(shè)置vertical-align屬性,來調(diào)整在垂直方向上的對齊;

3. 包含這些內(nèi)部元素的矩形區(qū)域,形成的每一行,被稱為line box(行框,后面會詳細介紹);

5.4.3. FFC和GFC

FFC(flex formatting context)和GFC(grid formatting context),分別是flex布局和grid布局的內(nèi)容,這兩個模塊的內(nèi)容非本文介紹的重點,所以感興趣的同學可以自行g(shù)oogle。

5.5. 包含塊(Containing Block)

5.5.1. 定義

我們在設(shè)置元素尺寸屬性(width、height、padding、margin和border)的百分比值或偏移屬性(top、right、bottom和left)的值時,通常會有一個“相對參考系”,這個”相對參考系”一般是包裹著這個元素的塊級祖先元素(一般是塊級父元素)或離這個元素最近的非static(relative、absolute和fixed)定位的祖先元素。這些具有“相對參考系”作用的祖先元素,其容納區(qū)域(cotent box或padding box),其實還有一個專門術(shù)語形容之,那就是包含塊(在知識體系中有個包含塊的概念,有助于加深對position定位原理的掌握)。

特別地,relative定位元素,其尺寸屬性(width、height等)的“相對坐標系”仍是其包含塊(塊級祖先元素(一般是父元素)的content box),但是偏移屬性(top、right、bottom和left)的“相對坐標系”則是其在文檔流原來的位置。

5.5.2. ICB(initial containing block, 初始包含塊)

5.5.2.1. 定義

如前面所說,任何一個元素都會有一個包含塊作為設(shè)置尺寸屬性和偏移屬性的“相對參考系”,而對于頂層的根元素<html/>,沒有任何元素包裹它,它的包含塊是什么?它選取什么作為“相對參考系”?

其實根元素<html/>是有包含塊的,它是一個不可見的矩形框,W3C組織稱之為ICB(initial containing block, 初始包含塊)。以下是W3C組織對ICB對定義:

The containing block in which the root element lives is a rectangle called the initial containing block.

5.5.2.2. ICB的尺寸和起始位置(左上角坐標)

在解釋ICB的尺寸和起始位置時,在這里先簡單補充一個背景知識:連續(xù)媒體(continuous media)和分頁媒體(paged media)。如何理解這兩個概念?在視覺閱讀層面,它們是展示內(nèi)容的兩種呈現(xiàn)方式。

連續(xù)媒體,就是采用連續(xù)展示內(nèi)容的方式,它保持了展示內(nèi)容顯示的連續(xù)性(一頁顯示所有內(nèi)容),我們可以在連續(xù)媒體的viewport(可視窗口)查看當前呈現(xiàn)的內(nèi)容。特別地,瀏覽器窗口就可以看成是連續(xù)媒體,當內(nèi)容的尺寸超過viewport時,讀者可以通過平滑滾動的方式來閱讀內(nèi)容。

分頁媒體,就是采用切頁展示內(nèi)容的方式,它將要展示的內(nèi)容切分為等尺寸的多頁(分頁顯示所有內(nèi)容),我們可以在分頁媒體的page area(頁面顯示區(qū)域)查看當前呈現(xiàn)的內(nèi)容。特別地,像幻燈片、電子書閱讀器,就可以看成是分頁媒體,當內(nèi)容的尺寸超過page area時,讀者可以通過切頁的方式來閱讀內(nèi)容;

對于屬于連續(xù)媒體(continuous media)的瀏覽器窗口來說,ICB的尺寸為viewport(瀏覽器視窗),其起始位置為畫布原點(canvas origin,即首屏的左上角,瀏覽器渲染數(shù)據(jù)后生成的內(nèi)容文檔可以看成是一張畫布)。

對于分頁媒體來說,ICB的尺寸為page area(關(guān)于ICB在分頁媒體的起始位置,沒有找到相關(guān)資料,但這個對于本文來說也不是重點)。

直觀來看,根元素<html/>的包含塊ICB,就是“首屏”。

5.5.3. 不同定位元素分別對應的包含塊

  • static和relative定位元素的包含塊,為其塊級祖先元素(通常是塊級父元素)的content box;
  • absolute定位元素的包含塊,為最近的非靜態(tài)定位祖先元素的padding box,查無非靜態(tài)定位祖先元素,那么它的包含塊是ICB(即根元素<html/>的包含塊);
  • fix定位元素的包含塊,為當前viewport(視窗);

在這里要強調(diào)的一點,ICB(初始包含塊)是專有名詞,它特指根元素<html/>的包含塊。不要將一個元素的初始包含塊,錯誤理解為它的父元素。MDN的一位編輯者也犯了這種錯誤。具體如下:

649

經(jīng)修正后:

688

也有一些權(quán)威CSS書籍說,當一個絕對定位元素找不到最近的非static祖先元素時,則相對于根元素<html/>定位,這種說法也是不嚴謹?shù)?。剛好看到一本,如下?/p>

650

圖1. 書的封面

651

圖2. 原話截圖

我們可以通過一個簡單的例子推翻這種說法:將根元素html的高度設(shè)置為超過viewport高度,如5000px(假設(shè)viewport高度為500px),再將一個沒有最近的非static祖先元素的絕對定位元素的bottom設(shè)置為0,尺寸為100px*100px即可。如果真如該書中所言,那么在首屏時,該絕對定位元素是被隱藏在滾動條下面的。而實際情況是:該絕對定位元素必然出現(xiàn)在首屏的底端,并且會隨著頁面滾動而滾動。驗證如下:

652

相信這個謬誤在前端圈流傳已久,希望各位同學引起重視。

5.6. 基本原理

5.6.1. 文檔流(正常流)

5.6.1.1. 定義

關(guān)于“文檔流”,并沒有找到較為官方的定義。筆者從google搜到一些認為比較靠譜的解釋,羅列如下:

1)摘自:《CSS: understanding the document flow》

The document flow is the model by which elements are rendered by default in the CSS specifications. In this model, elements are rendered according by their default display rule. In other words, block-level elements are displayed on a new line and inline elements on the same line. Everything is stacked in an ordered way from top to bottom. 

2)摘自:《What is “document flow”?》

Document flow is the arrangement of page elements as defined by positioning statements and the order of html statements; that is, how the different elements take up space and arrange themselves around each other.

在這里我想分享一下我自己對“文檔流”下的定義:

文檔流,是頁面元素默認存放的“容器”。

5.6.1.2. 特性

文檔流具有如下特性:

1. 文檔流按照頁面元素書寫的順序,將頁面元素按從左到右,從上至下的一般順序進行排列,而頁面元素則根據(jù)自身的布局屬性(block-box or inline-box),決定是行內(nèi)顯示,還是換行顯示;

2. 文檔流內(nèi)的元素,相互尊重:有序排列,彼此識別;

5.6.1.3. 脫離文檔流

元素脫離文檔流,按我之前下的定義,其實就意味著:元素脫離了默認存放的容器,換到另外一個容器存放。一個元素脫離了文檔流,這樣會導致:其父元素無法識別其,其也不參與父元素高度的計算。若有一個父元素的所有子元素都脫離文檔流,則會出現(xiàn)“高度塌陷”問題。常見的脫離文檔流的方法有:

  • 將元素設(shè)置為浮動元素
  • 將元素設(shè)置為absolute、fixed元素

5.6.2. 浮動(float屬性)

5.6.2.1. 浮動元素的分類

根據(jù)float屬性的設(shè)置,元素可以分為浮動元素(值為left或right)和非浮動元素(值為none)。而按浮動方向劃分,又可細分為:

  • 左浮動元素:float值為left的元素
  • 右浮動元素:float值為right的元素

5.6.2.2. 浮動原理

要想掌握浮動元素的浮動原理,只要理解浮動元素的浮動起始位置、浮動方向和浮動結(jié)束位置即可。

  • 浮動起始位置浮動元素(包括左右)的浮動起始位置,為最后一行最左側(cè)的空白位置,而不管空白位置是否能夠容納當前浮動元素;
  • 浮動方向左浮動元素的浮動方向為從起始位置向左浮動;右浮動元素的浮動方向為從起始位置向右浮動;
  • 浮動結(jié)束位置

左浮動元素遇到第一個左浮動元素或包含塊的最左側(cè)padding時,結(jié)束浮動;

右浮動元素遇到第一個右浮動元素或包含塊的最右側(cè)padding時,結(jié)束浮動;

以下demo可以幫助各位同學理解浮動元素的三要素:

/06:layout/float/1. 浮動元素三要素.html:

1
2
3
4
5
6
7
8
9
10
11
<body>
    <div class='fl'>左浮動元素-1width: 30%; height: 100px;</div>
    <div class='fl'>左浮動元素-2width: 30%; height: 200px;</div>
    <div class='fl'>左浮動元素-3width: 30%; height: 100px;</div>
    <div class='fl'>左浮動元素-4width: 30%; height: 100px;</div>
</body>

顯示結(jié)果:

653

說明:

a. 有四個連續(xù)左浮動的元素,每個元素寬度為30%;

b. 當一行排滿三個元素時,當前行只剩10%的寬度,不足以容納第四個左浮動元素;

c. 第四個浮動元素,從起始位置(最后一行的最左側(cè)空白)開始向左浮動,直到遇到第二個浮動元素的邊界;

為了幫助大家理解好浮動原理,在這里我想額外定義幾個術(shù)語:

  • 左浮動隊:由若干個連續(xù)的左浮動元素組成
  • 右浮動隊:由若干個連續(xù)的右浮動元素組成
  • 左浮動隊頭元素:左浮動隊的第一個元素,也是最左側(cè)的元素
  • 右浮動隊頭元素:右浮動隊的第一個元素,也是最右側(cè)的元素

特別地,

1. 同一行內(nèi),最多有兩條浮動隊,一是左浮動隊,二是右浮動隊;

2. 同一行內(nèi),一條浮動隊可能占滿一行;

3. 連續(xù)浮動的若干元素,如果無法在同一行內(nèi)顯示,則會按行被切分為兩條或更多條浮動隊;

5.6.2.3. 浮動對元素display的影響

當元素設(shè)置為浮動元素后,可能會引發(fā)display屬性的值變化,具體規(guī)則如下:

654

浮動元素display屬性變化對照表

5.6.3. 清除浮動(clear屬性)

5.6.3.1. 三要素

清除浮動,其作用是改變“當前元素”與“前一個聲明的浮動元素”之間的默認布局規(guī)則,這種改變主要體現(xiàn)為:讓當前元素換行顯示。這句話包含三個要素,分別為:

  • 使用者:當前元素(浮動元素或者非浮動元素的塊級元素)
  • 作用對象(清除誰的浮動):前一個聲明的浮動元素
  • 目的(作用):讓當前元素換行顯示

特別地,為什么使用者不包括非浮動的inline元素?因為非浮動的inline元素能夠識別浮動元素,是否使用clear清除“前一個聲明的浮動元素”的浮動,其布局結(jié)果是一樣的。感興趣的同學可以參考:06:layout/clear/4.非浮動inline元素清除左浮動.html,可以在調(diào)試器中觀察注釋非浮動inline元素的clear:left前后,其顯示位置的變化。而非浮動的塊級元素,因為無法識別前面聲明的左浮動元素,故會和左浮動元素發(fā)生重疊(左浮動元素在上),所以非浮動的塊級元素使用clear:left清除前一個左浮動元素,就能避免重疊的現(xiàn)象。

5.6.3.2. clear屬性的取值及應用場景

前面簡單介紹了clear屬性的作用,是清除前面聲明的浮動元素的浮動,然后讓當前元素換行顯示。但是要具體怎么使用,我們還得深入到clear的屬性值和應用場景。

clear屬性的取值有l(wèi)eft、right和both。那么它們的應用場景分別是什么?

left值的應用場景是,前面聲明的浮動元素是向左浮動(float: left);

right的應用場景是,前面聲明的浮動元素是向右浮動(float: right);

both的應用場景是,前面聲明的浮動元素的浮動方向不確定,可能是左,也可能是右(了解過clearfix實現(xiàn)原理的同學,就不難明白);

再次強調(diào)一下,當前元素如果要清除浮動,清除的是前面聲明的浮動元素的浮動,其clear屬性要取什么值,跟當前元素的是否是浮動元素或浮動方向沒有任何關(guān)系,而取決于其前面聲明的浮動元素的浮動方向。

舉個例子,一個右浮動元素(float:right),前面有一個左浮動元素(float:left),如果這個右浮動元素使用clear: left時,這個元素會清除前一個元素的浮動,進而換行顯示;如果使用clear:left時,這個元素在當前行的最右端顯示。如下圖所示(06:layout/clear/4.右浮動清除左浮動.html):

655

圖1. 右浮動清除左浮動

656

圖2.右浮動元素清除右浮動

在了解完clear屬性的取值和應用場景,我們可以對其作用,可以總結(jié)為:

如果當前元素浮動元素或非浮動的塊級元素,且前面聲明的元素是左(右)浮動元素,那么當前元素可以使用clear: left(clear: right),清除前一個左(右)浮動元素的左(右)浮動,此時當前元素會換行顯示;如果當前元素clear的浮動與前面一個浮動元素的浮動方向不同向,當前元素不會換行;

5.6.3.3. 清除浮動后的margin合并問題

1)兩個浮動元素之間,其垂直方向上的margin不會發(fā)生合并,如下圖所示:

657

                                    浮動元素間會不發(fā)生垂直margin合并

2)非浮動的塊級元素和浮動元素之間,其垂直方向上的margin會發(fā)生合并,如下圖所示:

658

非浮動的塊級元素與浮動元素間會發(fā)生margin合并

特別地,MDN的文檔說非浮動的塊級元素與浮動元素間不會發(fā)生margin合并,實際上會,上述結(jié)果已經(jīng)證明,已在MDN上更正該錯誤。以下為MDN未修改前的原話:

                                                   MDN未修正前的原話

5.6.3.4. 清除浮動的特殊應用:解決父元素高度塌陷問題

眾所周知,當一個父元素里面的所有元素都是浮動元素時,此時父元素無法識別這些浮動子元素,會進一步導致父元素發(fā)生高度塌陷問題。一種通用的解決方案就是在父元素內(nèi)部的尾部append一個非浮動的、尺寸為0的塊級元素(后面簡稱fix元素),然后使用clear:both,讓這個fix元素換行顯示,進而讓父元素能夠識別前一行的高度。這種樸素的方案其實就是clearfix的基本原理,clearfix只是更加優(yōu)雅地用:after來實現(xiàn)fix元素。

特別說明:解決父元素高度塌陷問題,還可以通過將父元素聲明為BFC元素來實現(xiàn)。

5.6.4. 定位(position屬性)

5.6.4.1. 定位元素的分類

根據(jù)position屬性的取值,static(默認值)、relative、absolute、fixed,元素可以分為靜態(tài)定位元素(值為static)、相對定位元素(值為relative)、絕對定位元素(值為absoute)和固定定位元素(值為fixed)。

注:position的取值還有sticky,但IE11都不支持,此處不講

5.6.4.2. 定位原理

static定位元素定位時的相對坐標系:無法設(shè)置top、right、bottom和left這四個偏移屬性;

relative定位元素定位時的相對坐標系:元素在文檔流原來的位置(區(qū)域);

absolute定位元素定位時的相對坐標系:離元素最近的一個非static(包含relative、absolute和fixed)定位祖先元素(包含塊為其padding box),如果沒有則為ICB(初始包含塊),即根元素html的包含塊;

fixed定位元素定位時的相對坐標系:當前的視窗(viewport);

5.6.5. line box(行框)

5.6.5.1. 定義

前面在介紹IFC時,我們提到過line box的定義:包含IFC內(nèi)部的所有子元素的虛擬矩形區(qū)域,形成的每一行,稱為line box。由于它是矩形的,中文常見將之翻譯為行框。

5.6.5.2. 模型結(jié)構(gòu)(七線譜)

line box的模型結(jié)構(gòu),形如七線譜,其中有六條重要的線:top線、text-top線、middle線、baseline線、text-bottom線和bottom線,如下圖所示:659

行框七線譜

其中top線到text-top線的區(qū)域bottom線到text-bottom的區(qū)域,又稱為行半距(half-leading),兩個行半距之和,為一個行距;text-top線到text-bottom線的區(qū)域,稱之為內(nèi)容區(qū)域(content-area)。如下圖所示:

660

行框區(qū)域劃分

5.6.5.3. 行框高度的計算

行框的高度,即一行的top線和bottom線間的垂直距離,這個垂直距離為:上下兩個行半距的高度和一個內(nèi)容區(qū)域的高度之和。影響行框高度計算的因素來自兩方面,一是自身line-height屬性的設(shè)置,二是內(nèi)部inline-level子元素的margin box高度的取值和line-height、vertical-align兩個屬性的設(shè)置。關(guān)于其計算規(guī)則,具體羅列如下:

1. 一個元素的行框高度,可由該元素的line-height屬性設(shè)置;

2. 一個元素的行框高度,受不可置換(span、a、label等)的子元素的內(nèi)容高度(text-top到text-bottom的垂直距離)影響(內(nèi)容高度又受font-size屬性和瀏覽器的解析規(guī)則影響,但主要由font-size決定;相同的font-size,在不同的瀏覽器,計算出來的內(nèi)容高度也不一樣,最終導致的行框高度也不一樣);

3. 一個元素的行框高度,可由不可置換(span、a、label等)的子元素的line-height屬性設(shè)置;

4. 一個元素的行框高度,可由可置換行內(nèi)元素(如img)或display屬性為inline-block、inline-table的這一類inline-block-level子元素的margin box高度和vertical-align屬性決定,當vertical-align為top或bottom時,行框的高度達到最小,剛好為子元素的margin box高度;

657

圖1. img元素的margin box高度比行框高度小

658

圖2. img元素的margin box高度與行框高度一致,行框高度達到最小

5. 如果同時滿足以上設(shè)置條件,那么行框的高度取最大值;

友情提示:在圖1 img元素的margin box高度比行框高度小,我們會看到img元素到父元素的底端會有一段空白,為什么會有這種現(xiàn)象?張鑫旭老師在《CSS深入理解vertical-align和line-height的基友關(guān)系》一文中將之定義為“幽靈空白節(jié)點”,其實結(jié)合行框理論來解釋,這段空白并不“幽靈”,也很好理解:它是行框的baseline線到bottom線的垂直距離,可置行內(nèi)換元素如img和inline-block-level元素,在被瀏覽器解析時,會和“文本”一樣,默認在baseline線上顯示,而不是在行框的bottom線上。

舉個例子:  行框高度的計算

html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<style>
        .line-box {
        background: yellow;
        line-height: 32px;
        font-size: 20px;
        }
        .span-1 {
        line-height: 40px;
        background: red;
        }
        .span-2 {
        line-height: 38px;
        background: green;
        }
        img {
        width: 50px;
        height: 50px;
        }
</style>
<body>
    <div class='line-box'>
        <span class='span-1'>span(line-height: 40px)</span>
        <span class='span-2'>span(line-height: 38px)</span>
    </div>
    
    <div class='line-box'>
        <span class='span-1'>span(line-height: 40px)</span>
        <span class='span-2'>span(line-height: 38px)</span>
        <img src='#' />
    </div>
</body>

顯示結(jié)果(chrome下):

661

圖1. line box內(nèi)部僅有不可置換元素

662

圖2. line box內(nèi)部還有可置換元素img

說明:

a. 元素每一行的line-height,既可以由當前元素的line-height屬性設(shè)置(32px),也可以由該行子元素的line-height屬性設(shè)置(分別是40px和38px),但取最大的line-height(40px),如圖1所示;

b. 特別地,如果一行內(nèi)還有可以設(shè)置height的可置換元素如img(height: 50px),且img的高度大于設(shè)置的最大line-height(40px)時,那么該行會被撐高,瀏覽器會重新計算line-height(最終結(jié)果為63px),如圖2所示;

5.6.5.4. 與line box行框有關(guān)的兩個重要屬性:line-height和vertical-align

相信很多前端同學有這樣的感覺:line-height和vertical-align這兩個屬性總是形影不離,而且有著一種說不清的關(guān)系。

它們到底有什么聯(lián)系嗎?

其實這兩個屬性的關(guān)系可由行框和行框內(nèi)的inline-level元素來體現(xiàn)。line-height屬性決定inline-level元素所在行框的高度,它是inline-level元素在一行內(nèi)垂直方向上的顯示范圍;vertical-align屬性則決定inline-level元素在一行內(nèi)的垂直對齊方式,即決定inline-level元素在一行內(nèi)垂直方向上的最終位置。下面我們來深入介紹這兩個屬性:

1)line-height屬性

1.1)line-height屬性的作用

line-height屬性一般用于塊級元素設(shè)置其內(nèi)部每一行的高度,即默認行高;line-height屬性也可以用于不可置換元素(如span、a)設(shè)置所在行框的高度。也就說,每一行計算出來的最終行高,既受父元素line-height屬性的影響,也受子元素line-height屬性的影響。

1.2)line-height屬性的取值

line-height的取值有<length>、<number>、和關(guān)鍵字normal(默認值)。其中:

  • <length>表示使用指定帶單位的長度來設(shè)置line-height,這些長度單位可以是px、pt和em和rem;
  • <number>表示用font-size值的倍數(shù)來設(shè)置line-height;
  • <percentage>表示用font-size值的百分比來設(shè)置line-height;
  • 而關(guān)鍵字normal,其最終計算出來的尺寸,則取決于瀏覽器各自的解析機制和選用的font-family類型:瀏覽器會根據(jù)選用的font-family類型來計算出一個合適的值,W3C官方推薦使用<number>值,并且推薦值的范圍為1.0到1.2之間(但經(jīng)過實測,瀏覽器在實現(xiàn)時,遠比這個復雜,而且不同瀏覽器間也存在差異。唯一可以確定的一點是,最終的行高肯定會比font-size值要大)。

我們在將UI稿實現(xiàn)為頁面代碼時,常常強調(diào)要Pixel Perfect、高精準地還原設(shè)計稿。但

我們常常會遇到這樣一個問題:當我們用一個塊級元素包裹文本時,會發(fā)現(xiàn)塊級元素的高度,實際比文本的font-size尺寸還要高,導致上下形成了一些空白,進一步造成塊級元素內(nèi)的文本與垂直方向上相鄰元素的距離變大,如下圖所示:

line-height值為normal

這種誤差是由于line-height的默認值為normal,那有什么辦法可以解決這個問題呢?較常用的方法是將塊級元素的line-height設(shè)置為1或100%。設(shè)置后的結(jié)果如下圖所示:

664

line-height值為1或100%

這樣做也有一點不好,那就是:瀏覽器最終解析出來的內(nèi)容高度,有可能是比font-size要大的,當行高為font-size時,文本內(nèi)容就會溢出。我們將字體放大為100px,溢出效果就很明顯,如下圖所示:

665

line-height值為1或100%

1.2)line-height屬性對元素高度的影響

我們可以通過了解line-height屬性分別對塊元素和不可置換的行內(nèi)元素自身高度的影響、以及不可置換的子元素的line-height屬性對父元素高度的影響,來深入理解line-height屬性的作用。

為了幫助大家更好地理解line-height,我設(shè)計了如下三個小demo:

demo1:  line-height屬性對塊級元素自身height的影響

html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</body>
    // div為單行
    <div class='block'>
        div(line-height: 32px)
    </div>
    
    // div為多行
    <div class='block'>
        div(line-height: 32px)
        <br>
        div(line-height: 32px)
    </div>
</body>

顯示結(jié)果(chrome下):

666

圖1. div為單行時

667

圖2. div為多行時

說明:

a. 當一個塊元素不設(shè)置height,而且這個塊元素僅有一行時,那么其height剛好等于line-height;

b. 當一個塊元素不設(shè)置height,而且這個塊元素有多行時,那么其height剛好等于每一行的line-height之和;

demo2:  line-height屬性對不可置換行內(nèi)元素(如span)的height的影響

html:

1
2
3
4
5
6
7
8
9
<div class='line-box'>
    <span class='inline-element'>
        span(line-height: 40px;font-size: 20px)            
    </span>
</div>

顯示結(jié)果:

668

圖1. 行內(nèi)元素為單行時,height為28px(在chrome下)

669

圖2. 行內(nèi)元素單行時,height為20.5px(在firefox下)

說明:

a. 不可置換行內(nèi)元素為單行時,其height等于text-top線到text-bottom線的距離,所以line-height的取值不會影響到其height,其height由font-size和瀏覽器的默認解析機制決定(一般>font-size,大多少則取決于瀏覽器解析機制,如圖1、2所示);

b. 不可置換元素為多行時,其height等于第一行的text-top線到最后一行的text-bottom線的距離,此時line-height的取值就會影響到其height,其height=line-height * 行數(shù) – (line-height – 每一行text-top線到text-bottom的距離),即height=line-height * 行數(shù) – 2 * half-heading;如下圖所示:

670

圖3. 行內(nèi)元素為多行時(在chrome下)

demo3:不可置換的子元素(如span)的line-height,對父元素height的影響

html:

1
2
3
4
5
6
7
8
9
10
11
<div class='line-box'>
      <span class='span-1'>span(line-height: 40px)</span>
      <span class='span-2'>span(line-height: 38px)</span>
       <br>
       <span class='span-3'>span(line-height: 50px)</span>
</div>

顯示結(jié)果(chrome下):

671

說明:

a. 塊級元素每一行的行高都可以不同;

b. 不可置換的行內(nèi)子元素的line-height屬性,可以決定所在行框的高度;

c. 如果一個父元素不設(shè)置height,那么其height為所有行的高度之和;

d. 不可置換的行內(nèi)子元素的line-height屬性,是通過影響行框的高度來影響父元素的高度的。

2)vertical-align屬性

vertical-align的作用之一:就是用于設(shè)置inline-level元素自身在“行框”內(nèi)的垂直對齊方式,其控制范圍在一行內(nèi)。較常用的值有top、middle、baseline(默認值)和bottom,不常用的有text-top、text-bottom、sub和super,幾乎不用的有<length>和<percentage>。

672

行框七線譜

vertical-align屬性的幾個重要取值的作用如下:

  • 當vertical-align取top時,表示當前inline-level元素的上margin edge在行框內(nèi)貼頂;
  • 當vertical-align取bottom時,表示當前inline-level元素的下margin edge在行框內(nèi)貼底;
  • 當vertical-align取middle時,表示當前inline-level元素的垂直平分線和行框的middle線重合;
  • 當vertical-align取baseline時,表示當前inline-level元素的下margin edge緊貼在行框的baseline上;

vertical-align屬性的另一個作用:就是table-cell元素用于控制其內(nèi)部子元素在垂直方向上的對齊方式,而且這些子元素的類型不受限制,不僅可以是span,而且可以是div。

舉個例子:

html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<table>
    <tr>
        <td class='top'>div(top)</td>
        <td class='middle'>div(middle)</td>
        <td class='baseline'>div(baseline)</td>
        <td class='bottom'>div(bottom)</td>
    </tr>
</table>

顯示結(jié)果:

673

說明:

a. table-cell元素通過設(shè)置自身的vertical-align屬性,來設(shè)置其子元素在垂直方向上的對齊方式;

特別說明:我們常用說的使用table布局來實現(xiàn)子元素在父元素內(nèi)部垂直居中,就是運用到了這個知識點。

5.6.6. margin

在傳統(tǒng)的布局方案中,margin不僅用來隔離自身與相鄰元素或父元素(一般不推薦用來隔離父元素),而且在元素水平和垂直方向上的居中定位,亦發(fā)揮了重要的作用。下面我們來深入介紹margin的相關(guān)布局特性。

5.6.6.1. auto的計算規(guī)則(在width和margin上使用)

1)水平方向上

談到“如何設(shè)置文檔流中的塊級元素在父元素內(nèi)部水平居中?”這個布局問題,相信很多同學馬上會想到這個方案:給元素設(shè)置固定寬度,并使用margin: 0 auto(水平方向上的margin為auto)

.child {

width: 100px;

margin: 0 auto;

}

它的實現(xiàn)原理,包含如下四個基礎(chǔ)知識點:

  • 塊級元素的水平尺寸(outerWidth,margin box的寬度)的計算規(guī)則:

outerWidth = margin-left + border-left-width + padding-left + width + padding-right + border-right + margin-right,如下圖所示:

674

  • 文檔流中的塊級元素,其在水平方向上的尺寸屬性的初始值,僅width為auto,其余為0
  • 在水平方向上的尺寸屬性,僅width、margin-left和margin-right可以設(shè)置auto值(自動計算)
  • 文檔流中的塊級元素,其在水平方向上的尺寸屬性,當值為auto時,表示取所在行的剩余寬度,特別地,當margin-left和margin-right的值均為auto時,會平分所在行的剩余寬度

在理解了上述四個基礎(chǔ)知識點,我們不難理解其原理:

當塊級元素在水平方向上的尺寸屬性,除了margin-left和margin-right值為auto,其余皆為定值,那么margin-left和margin-right會自動平分父元素的剩余寬度,進而達到在父元素內(nèi)部水平居中的效果,如下圖所示:

675

  圖1. 元素在父元素內(nèi)部水平居中(左右margin各取一半)

676

圖2. 水平居中元素的盒模型解構(gòu)圖

結(jié)合上述四個基礎(chǔ)知識點,我們還可以得出如下結(jié)論:

文檔流中的塊級元素如果不設(shè)置任何水平尺寸屬性,那么其默認的width為當前行的content width,此時width取auto和100%,最終的計算值一樣

2)垂直方向上

或許我們都曾問過這樣的一個問題:既然可以通過設(shè)置margin: 0 auto,讓文檔流中的塊級元素在父元素內(nèi)部水平居中,那么可否通過設(shè)置margin: auto 0,讓其垂直居中?

答案是不能的,因為文檔流中的塊級元素,其垂直方向上的margin為auto時的計算規(guī)則和在水平方向上的計算規(guī)則不同:不取父元素剩余的高度,而為0。W3C標準原話如下:

“If “margin-top” or “margin-bottom” is “auto”, their used value is 0″

或許大家會問,為什么要這樣設(shè)計呢?官方并沒有給出說明,但是有網(wǎng)友給出了如下幾個解釋,羅列如下,供各位參考(可以在留言中分享你的看法,本人比較認同第一條):

It could be because of the typical vertical page flow, where page size increases height-wise. So, centering an element vertically in its container is not going to make it appear centered, relative to the page itself, unlike when it’s done horizontally (in most cases).

And maybe it’s because of this same reason, they decided to add an exception for absolute elements which can be centered vertically along the entire page’s height.

It could also be because of the margin collapse effect (a collapse of adjacent elements” margins) which is another exception for the vertical margins.

在W3C標準規(guī)約中,雖不能使用margin: auto 0,實現(xiàn)普通文檔流中的塊級元素在父元素內(nèi)部垂直居中,但是可以使用margin: auto 0,實現(xiàn)絕對或固定定位元素在包含塊內(nèi)部垂直居中,因為絕對或固定定位元素垂直方向上的margin,其 auto仍會取包含塊的剩余高度,W3C官方文檔給出的計算公式如下:

‘top’ + ‘margin-top’ + ‘border-top-width’ + ‘padding-top’ + ‘height’ + ‘padding-bottom’ + ‘border-bottom-width’ + ‘margin-bottom’ + ‘bottom’ = height of containing block

等價的簡化公式:

子元素outerHeight  = 包含塊height  – 子元素top – 子元素bottom

提示:

1. 子元素outerHeight,是指當前子元素margin box的高度;

2. 包含塊height,可以為當前子元素的相對定位參考系元素的padding box的高度、ICB的高度或viewport的高度;

要使用上述規(guī)則來實現(xiàn)子元素在父元素內(nèi)部垂直居中,那么就需要保證:

  • 子元素的top值 + bottom值為0(原因:讓子元素outerHeight 等于包含塊height)
  • 子元素的top值取0(原因:讓子元素的上margin edge緊貼包含塊的頂部)

下面通過一個demo來詳細介紹:

html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  <style>
        .parent {
            position: relative;
            background: yellow;
            height: 100px;
        }
        .child {
            position: absolute;
            top: 0;
            bottom: 0;
            background: green;
            width: 140px;
            height: 20px;
            margin: auto 0;
            text-align: center;
        }
</style>
<body>
    <div class='parent'>
    <div class='child'>垂直居中的子元素</div>
    </div>
</body>

顯示結(jié)果:

677

圖1. 子元素在父元素內(nèi)部垂直居中(上下margin各取一半)

678

圖2. 垂直居中元素的盒模型解構(gòu)圖

說明:

a. 絕對定位的子元素的top為0,其輪廓(包含margin)的上邊界與其包含塊內(nèi)容區(qū)域的頂部緊貼;

b. 由已知求未知:包含塊的height已知,子元素的top值和bottom值之和為0,即子元素的outerHeight可求,又因為子元素height已知,故垂直方向上的剩余高度可以確定,當子元素的margin-top和margin-bottom均為auto時,將平分剩余的高度;

5.6.6.2. margin合并(margin collapsing)

在垂直方向上,元素與自身或相鄰的兄弟元素、父元素、子元素的margin,會發(fā)生合并(注意:在IE6/7子元素垂直方向上的margin會隔離父元素,而不是和父元素的margin發(fā)生合并,IE8+則與標準瀏覽器同),margin取較大的值,而在水平方向上則不會。各位讀者可以從下面三個demo,來理解垂直方向上margin的合并:

1) 父元素與子元素(第一個子元素、最后一個子元素)

html:

1
2
3
4
5
6
7
8
9
10
11
<div class='wrapper'>
  <div class='parent'>
    <div class='child first-child'>第一個子元素(margin-top: 20px)</div>
    <div class='child last-child'>最后一個子元素(margin-bottom: 20px)</div>
  </div>
</div>

顯示結(jié)果:

679

說明:

a. 父元素(黃色)的margin-top(40px)和第一個子元素的margin-top(20px)發(fā)生融合(取較大的40px);

b. 父元素(黃色)的margin-bottom(40px)和最后一個子元素的margin-bottom(20px)發(fā)生融合(取較大的40px);

2) 上下相鄰的兄弟元素(同層元素)

html:

1
2
3
4
5
6
7
<div class='parent'>
    <div class='child first-child'>第一個元素(margin-bottom: 40px)</div>
    <div class='child last-child'>第二個元素(margin-top: 20px)</div>
</div>

顯示結(jié)果:

680

說明:

a. 第一個元素的margin-bottom(40px)和第二個元素的margin-top(20px)發(fā)生融合(取較大的40px);

3) 空塊級元素

html:

1
2
3
4
5
<div class='line'>第一行</div>
    <div class='empty-block'></div>
<div class='line'>第二行</div>

顯示結(jié)果:

681

說明:

a. 兩行之間的空白區(qū)域,為一個空塊元素;

b. 空塊的margin-top為40px, margin-bottom為20px;

c. 兩行之間的距離為40px,可知空塊元素的margin-top和margin-bottom發(fā)生了合并,取較大值;

這里我們舉了三個會在垂直方向上發(fā)生margin合并的例子,但是細心的同學可能記得,我們在“5.6.3.3. 清除浮動后的margin合并問題”章節(jié),舉了一個在垂直方向上例子不會發(fā)生margin合并的例子:浮動元素間在垂直方向上不會發(fā)生margin合并。

5.6.6.3. 子元素的margin隔離父元素

細心的讀者不難發(fā)現(xiàn),在“2) 上下相鄰的兄弟元素(同層元素)” 的demo可以看到

682

子元素(綠色)垂直方向上的margin并沒有將自己與父元素(黃色)隔離開(IE6/7會,IE8+不會)。

那么什么情況,子元素的margin可以和父元素隔離開?

首先要強調(diào)的一點是, 子元素水平方向上的margin,始終能夠隔離父元素;然而子元素在垂直方向上的margin隔離父元素的情況,本人記錄的僅有以下四種(歡迎補充):

case 1: 父元素是BFC元素

html:

1
2
3
4
5
<div class='parent'>
    <div class='child'>子元素(margin: 20px)</div>
</div>

顯示結(jié)果:

683

說明:

a. 父元素(黃色)是BFC元素,子元素(綠色)垂直方向上的margin能夠隔離父元素;

case 2:父元素擁有border

html:

1
2
3
4
5
<div class='parent'>
    <div class='child'>子元素(margin: 20px)</div>
</div>

顯示結(jié)果:

684

說明:

a. 父元素(黃色)擁有border,子元素(綠色)垂直方向上的margin能夠隔離父元素;

case 3:父元素擁有padding

html:

1
2
3
4
5
<div class='parent'>
    <div class='child'>子元素(margin: 20px)</div>
</div>

顯示結(jié)果:

685

說明:

a. 父元素(黃色)擁有padding,子元素(綠色)垂直方向上的margin能夠隔離父元素;

case 4:子元素是可置換元素或display為inline-block、inline-table、table-caption的元素

html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<style>
    .parent {
        background: yellow;
        width: 100%;
        height: 60px;
        line-height: 20px;
    }
    .inline-block {
        display: inline-block;
    }
    .inline-table {
        display: inline-table;
    }
    img, .inline-block, .inline-table {
        border: 1px solid green;
        height: 20px;
        min-width: 20px;
        margin-top: 10px;
        vertical-align: top;
    }
</style>
<body>
    <div class='parent'>
        <img src='frame_image.svg' />
        <div class='inline-block'>display: inline-block </div>
        <div class='inline-table'>display: inline-table</div>
    </div>
</body>

顯示結(jié)果:

686

說明:

a. 可置換行內(nèi)的和display屬性為inline-block、inline-table的子元素,其垂直方向上的margin能夠隔離自身與父元素;

在這里對margin合并和margin隔離作一個小結(jié),本人把遇到過的所有在垂直方向上會發(fā)生與不會發(fā)生margin合并、能使用margin隔離與不能使用margin隔離的例子,都羅列了出來(然而這僅僅是在標準瀏覽器的例子,在IE6/7情況還會不一樣,但因為現(xiàn)在基本無需再兼容低版本的IE,所以就不再列舉)。目的不是讓大家記住它,而是讓大家避開它:在垂直方向上,兄弟元素間盡量不要設(shè)置相鄰的margin,子元素也不要使用margin來隔離父元素,這樣能盡量保證你的CSS代碼,在各種版本的瀏覽器都有較好的兼容性(顯示一致)。

7. 結(jié)尾語

本文從CSS盒模型及其發(fā)展史、元素的分類及其布局特性、格式化上下文(Formatting Context)、包含塊、基本原理(文檔流、浮動、清除浮動、定位、行框、margin)這五大模塊,系統(tǒng)介紹了一下前端的布局基礎(chǔ),希望此次分享,能夠讓各位讀者對前端基礎(chǔ)布局有一個底層、體系的認識。因為內(nèi)容涵蓋過廣,難免會有紕漏,還望見諒和指正。

此篇文章斷斷續(xù)續(xù)寫了幾個月,從年前寫到年后,一方面是因為這個標題太大,含括的內(nèi)容太多,需要慢慢梳理;一方面是文中要講的東西,很多是出于本人的感悟和總結(jié),為了保證觀點的正確性、嚴謹性以及和行業(yè)的標準術(shù)語做好同步,需逐一驗證;還有一方面也是近幾個月來,本人需要處理的私事較多,分散了精力。

時隔半年,依然有不少朋友還在關(guān)注我的公眾號,謝謝你們的支持。這遲來的一篇分享,希望能對各位有用,后面我也會努力分享更多對大家有幫助的文章。

最后我還想再分享一些心得體會:

  • 不要輕視簡單的東西我們在生活中總是容易忽略一些簡單的東西,因為輕視簡單,導致過了幾年依然也沒有掌握,前端的同學更應該注意這一點。
  • 盡信書不如無書不要太相信權(quán)威,而是要學會驗證、總結(jié),并構(gòu)建自己的知識體系。
  • 學技術(shù)要看官方文檔很多同學在初學時喜歡看一些快速入門的教程,我覺得這種學習習慣挺好的,但是建議不要遺漏官方文檔的學習。因為初學者很難去鑒定一個非官方文檔的質(zhì)量,運氣不好的話,還會被誤導。而且官方文檔最貼近原作者的想法,我們更容易體會到其設(shè)計思想。

演示代碼地址:https://github.com/momopig/simplicity/tree/master/06:layout

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多