前端知識歸納(3)-- HTML/CSS(part1 css布局)

目錄概覽:

  • 深入理解HTML語義
  • 讓IE8支持HTML5語義化標(biāo)簽
  • 視覺格式化模型
  • 深入了解inline-block
  • flexbox剩余空間分配規(guī)則
  • grids布局系統(tǒng)
  • 瀏覽器如何渲染HTML & CSS
  • 重排與重繪

一屁擅、深入理解 HTML 語義

HTML 語義當(dāng)然不僅僅只是幾個 HTML 語義標(biāo)簽。

從“文檔”說起

很多時候我會把“頁面”和“文檔”這兩個詞混著用解总,比如將 HTML 頁面說成“HTML 文檔”。

HTML 就是文檔,最開始的《Web 簡史》中我們有提到過,萬維網(wǎng)的雛形是一個文檔共享系統(tǒng)国拇,萬維網(wǎng)就是一個放大版的文檔共享系統(tǒng)。

只是隨著 Web 的發(fā)展惯殊,各種酷炫的頁面和應(yīng)用層出不窮酱吝,倒是讓新入行的小伙伴忽略了,HTML 的本質(zhì)其實(shí)是文檔(document)土思。

現(xiàn)實(shí)生活中掉瞳,說到文檔,我們的第一反應(yīng)就是 Microsoft Word 浪漠,其實(shí) Word 當(dāng)年也有可能成為 Web 標(biāo)準(zhǔn)陕习,當(dāng)然這是另外一個故事。

文檔大綱

所以我們從 Word 說開去址愿,希望你 Word 用的很順溜哦该镣。

Word 中的標(biāo)題有一級標(biāo)題、二級標(biāo)題……的說法响谓,所有的標(biāo)題就構(gòu)建出了這個 Word 文檔的大綱损合。下圖的左邊欄就是大綱視圖省艳,可以很好的觀察文檔大綱。

word-outline

在 HTML 文檔中呢嫁审,H1 ~ H6 就是一級到六級標(biāo)題跋炕,它們構(gòu)成了 HTML 文檔的大綱。下面是騰訊網(wǎng)首頁的文檔大綱律适。一個好的頁面辐烂,必定先是一個好文檔,好文檔必然有著嚴(yán)謹(jǐn)?shù)奈臋n大綱捂贿。

這個是谷歌瀏覽器查看頁面文檔大綱的插件纠修,html5-outliner

web-outline

再來看“萬維網(wǎng)”

最初的萬維網(wǎng)是一個文檔共享的網(wǎng)絡(luò)厂僧,現(xiàn)在的萬維網(wǎng)則是一個資源共享的網(wǎng)絡(luò)扣草,包括圖片、多媒體等等颜屠。

HTML 則是萬維網(wǎng)的粘合劑辰妙,也是萬維網(wǎng)的載體,但是現(xiàn)在 HTML 給我們的感覺啊甫窟,就是給人看的密浑,其實(shí),HTML 同時也會給機(jī)器看蕴坪,比如下圖肴掷,除了 human 在讀 HTML ,各式各樣的機(jī)器也在讀 HTML 背传,比如搜索引擎的爬蟲和讀屏設(shè)備呆瞻。

視覺上的各種酷炫會給人以視覺沖擊,但對機(jī)器來說径玖,并沒有什么用痴脾,它們更看重的是語義,這樣才能更好地解析內(nèi)容梳星。這也是為什么樣式會從結(jié)構(gòu)里面分離出來的原因之一赞赖。

exploiting-html

現(xiàn)在才到 HTML 語義

本文的開頭就提到了,HTML 語義當(dāng)然不僅僅只是幾個 HTML 語義標(biāo)簽冤灾。在 HTML 本身這個層面上前域,語義也有更多的東西,關(guān)于這些已經(jīng)有不少前輩為我們總結(jié)好了韵吨,參考如下:

二、讓 IE8 支持 HTML5 語義化標(biāo)簽

HTML5是 HTML 最新的修訂版本,于2014年10月由萬維網(wǎng)聯(lián)盟(W3C)完成標(biāo)準(zhǔn)制定椿疗。而 IE8 面世時間為2009年3月19日漏峰,時間相差如此之大,所以 IE8 作為比較古老的瀏覽器届榄,不支持 HTML 5 引入的語義化標(biāo)簽(如 header浅乔、nav、menu铝条、section靖苇、article 等)也是很正常的。

默認(rèn)情況下 IE8 對 HTML5 標(biāo)簽的處理

在 IE8 里面攻晒,未定義的標(biāo)簽——IE8 不認(rèn)識所有新引入的 HTML5 標(biāo)簽顾复,所以定義樣式是不會生效班挖。比如下面這段代碼(抽取主要代碼):

<style>
  section { color: red; }
</style>

<section>
  hello world
</section>

期待展示的效果如下:

chrome

但在 IE8 中實(shí)際展示效果如下:

IE8

如何讓 IE8 支持 HTML5 標(biāo)簽

雖然默認(rèn)不支持鲁捏,但是我們可以通過 JS 使用 document.createElement 來“欺騙” IE 的 CSS 引擎,讓它知道某個標(biāo)簽的存在萧芙,具體做法如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>HTML5 test</title>
  </head>
  <body>

    <script>
      document.createElement('section');
    </script>

    <style>
      section { color: red; }
    </style>

    <section>
      Hello!
    </section>

  </body>
</html>

顯示效果如下:

IE_HTML5

既然元素默認(rèn)都不支持给梅,就更沒有相關(guān)默認(rèn)的樣式了,所以我們還要加上一些重置樣式如下:

article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary {
  display: block;
}

借助 html5shiv.js 讓 IE8 支持更多的 HTML5 特性

其實(shí)不只是 IE8 双揪, IE6-9动羽、 Safari 4.x (以及 iPhone 3.x)、還有Firefox 3.x 等等渔期,對 HTML5 的支持都不完善运吓。所以有了一個庫 html5shiv.js 來做統(tǒng)一處理,shiv 意為用作武器的小刀(實(shí)際上是一個拼寫錯誤疯趟,應(yīng)該為 shim)拘哨,在機(jī)械工程中的專業(yè)釋義為墊片,比喻給那些老舊的瀏覽器加個墊片信峻,讓它們基本能用倦青。

更多閱讀

三产镐、視覺格式化模型(visual formatting model)

前面我們已經(jīng)學(xué)習(xí)了盒模型(box model),知道了元素會被渲染成一個個盒子踢步。那么這些盒子在屏幕上的位置又是怎么放置的呢癣亚?這就是我們現(xiàn)在要學(xué)習(xí)的——CSS 視覺格式化模型(visual formatting model)。視覺格式化模型是 CSS 布局的一個基礎(chǔ)理論體系获印,需要你有一定的 CSS 功底述雾,所以一時半會是很難掌握的,但是只要你一掌握,對于 CSS 布局就會豁然開朗绰咽。(建議整個 CSS 布局學(xué)完后再重新細(xì)讀深入下菇肃。)

盒子的位置擺放

默認(rèn)情況下,盒子按照元素在 HTML 中的先后位置從左至右自上而下一個接著一個排列擺放取募。如下圖:

image

在圖中我們可以看到琐谤,有些元素的盒子被渲染為完整的一行,如h1玩敏、p斗忌、div;而有些元素的盒子則被渲染為水平排列旺聚,直到該行被占滿然后換行织阳,如span、a砰粹、strong唧躲。

這是因?yàn)椴煌暮凶邮褂玫氖遣煌母袷交舷挛模╢ormatting context)來布局,每個格式化上下文都擁有一套不同的渲染規(guī)則碱璃,它決定了其子元素將如何定位弄痹,以及和其他元素的關(guān)系和相互作用。(就如我們參加結(jié)婚喜宴一樣嵌器,有父母長輩席肛真,好友席,同事席爽航,甚至前男/女朋友席等蚓让,不同的身份坐到對應(yīng)位置即可。)

格式化上下文(formatting context)

我們常見的兩個格式化上下文分別為:塊格式化上下文(block formatting context 簡稱 BFC)和行內(nèi)格式化上下文(inline formatting context 簡稱 IFC)

BFC

塊級盒(block-level boxes)

當(dāng)元素的 CSS 屬性 display 的計(jì)算值為 block讥珍,list-item历极,table,flex 或 grid 時串述,它是塊級元素执解。視覺上呈現(xiàn)為塊,豎直排列纲酗。典型的如 <div> 元素衰腌,<p> 元素等都是塊級元素。

每個塊級元素至少生成一個塊級盒觅赊,稱為主要塊級盒(principal block-level box)右蕊。一些元素,比如<li>吮螺,生成額外的盒來放置項(xiàng)目符號饶囚,不過多數(shù)元素只生成一個主要塊級盒帕翻。

塊級盒參與 BFC,被渲染為完整的一個新行萝风。

渲染規(guī)則

默認(rèn)根元素(html 元素)會創(chuàng)建一個 BFC嘀掸,其塊級盒子元素將會按照如下規(guī)則進(jìn)行渲染:

  • 塊級盒會在垂直方向,一個接一個地放置规惰,每個盒子水平占滿整個容器空間
  • 塊級盒的垂直方向距離由上下 margin 決定睬塌,同屬于一個 BFC 中的兩個或以上塊級盒的相接的 margin 會發(fā)生重疊
  • BFC 就是頁面上的一個隔離的獨(dú)立容器,容器里面的子元素不會影響到外面的元素歇万。反之也如此
  • 計(jì)算 BFC 的高度時揩晴,浮動元素也參與計(jì)算

除此之外,還有其他方法可以創(chuàng)建一個新的 BFC贪磺,具體可參看:塊格式化上下文硫兰。除此之外,flexbox 布局和 grids 布局中的 item 都會創(chuàng)建一個新的 BFC寒锚。

具體渲染規(guī)則及效果可參看:塊格式化上下文

更多關(guān)于 BFC 相關(guān)內(nèi)容可參看:

IFC

行內(nèi)級盒(inline-level boxes)

當(dāng)元素的 CSS 屬性 display 的計(jì)算值為 inline劫映,inline-block,inline-table壕曼,inline-flex 或 inline-grid 時苏研,它是行內(nèi)級元素等浊。視覺上它將內(nèi)容與其它行內(nèi)級元素排列為一行腮郊,直到該行被占滿然后換行。典型的如段落內(nèi)容筹燕,文本或圖片轧飞,都是行內(nèi)級元素。

注:由于目前幾乎所有資料都將行內(nèi)元素當(dāng)做行內(nèi)元素撒踪,所以前面的課程我們也遵循這個美麗的錯誤过咬。嚴(yán)格來說,行內(nèi)元素不包括 inline-block 的制妄,行內(nèi)級元素才包括掸绞。我們要理解其中的區(qū)別,知曉這個美麗的錯誤耕捞。

行內(nèi)級元素生成行內(nèi)級盒衔掸,參與行內(nèi)格式化上下文(inline formatting context),被渲染為水平排列, 直到當(dāng)行被占滿然后換行俺抽。

行內(nèi)級盒分為行內(nèi)盒(inline boxes)和原子行內(nèi)級盒(atomic inline-level boxes)敞映。前者由非置換元素且 display 值為 inline 的元素生成;后者由行內(nèi)級置換元素磷斧,或 display 值為 inline-block, inline-table, inline-flex, inline-grid 的元素生成振愿。

每一行排列的行內(nèi)級盒都可以看做由一個匿名的行盒包裹捷犹,如下圖(使用了兩種灰色背景色來模擬):

image

渲染規(guī)則

當(dāng)塊容器盒(block container box)不包括任何塊級盒(block-level boxes)時,就會創(chuàng)建一個行內(nèi)格式化上下文(IFC)冕末。(一般來說一個塊級盒也是一個塊容器盒萍歉,具體可參看: Block-level elements and block boxes

IFC 中的行內(nèi)級盒將會按照如下規(guī)則進(jìn)行渲染(規(guī)則有點(diǎn)多,大概主要點(diǎn)就是行盒档桃,折行機(jī)制翠桦,水平對齊方式,垂直高度及垂直對齊方式):

  • 盒子一個接一個地水平擺放胳蛮,當(dāng)容器寬度不夠時就會換行
  • 在水平方向上销凑,這些盒的外邊距、邊框仅炊、內(nèi)邊距所占用的空間都會被計(jì)算斗幼,但行內(nèi)盒的垂直的border,padding 與 margin 都不會撐開行盒的高度
  • 在垂直方向上抚垄,這些盒可能會以不同形式來對齊蜕窿,可通過 vertical-align 來設(shè)置,默認(rèn)對齊為 baseline
  • 每一行將生成一個行盒(line box)呆馁,包括該行所有的盒子桐经,行盒的寬度是由包含塊和存在的浮動來決定
  • 行盒一般左右邊都貼緊其包含塊,但是會因?yàn)楦雍校╢loat 元素)的存在而發(fā)生變化浙滤。浮動盒會位于包含塊邊緣與行盒邊緣之間阴挣,這樣行盒的可用寬度就小于包含塊的寬度
  • 當(dāng)所有盒的總寬度小于行盒的寬度,那么行盒中的水平方向排版由 text-align 屬性來決定
  • 當(dāng)所有盒的總寬度超過一個行盒時纺腊,就會形成多個行盒畔咧,多個行盒相互之間垂直方向不能分離,不能重疊
  • 當(dāng)一個行內(nèi)盒超過行盒的寬度時揖膜,它會被分割成多個盒誓沸,這些盒被分布在多個行盒里。如果一個行內(nèi)盒不能被分割(比如只包含單個字符壹粟,或word-breaking機(jī)制被禁用拜隧,或該行內(nèi)框受white-space屬性值為nowrap或pre的影響),那么這個行內(nèi)盒將溢出這個行盒
  • 當(dāng)一個行內(nèi)盒發(fā)生分割時趁仙,分割處的 margins, borders 和 padding 不會有任何視覺效果(或者其他任何分裂洪添,只要是有多個行盒)
  • 行盒的高度由內(nèi)部元素中實(shí)際高度最高的元素計(jì)算出來。每個行盒的高度由于內(nèi)容不一樣幸撕,所以高度也可能不一樣
  • 在一個行盒中薇组,當(dāng)他包含的內(nèi)部容器的高度小于行盒的高度的時候,內(nèi)部容器的垂直位置可由自己的 vertical-align 屬性來確定

注:在 IFC 的環(huán)境中坐儿,是不能存在塊級元素的律胀,如果將塊級元素插入到 IFC 中宋光,那么此 IFC 將會被破壞掉變成 BFC,而塊級元素前的元素或文本和塊級元素后的元素或文本將會各自自動產(chǎn)生一個匿名塊盒其包圍炭菌。

具體行盒高度及垂直對齊方式渲染效果可參看:

更多關(guān)于 IFC 相關(guān)內(nèi)容可參看:

其他格式化上下文

除此之外罪佳,還有一些其他不同類型的盒子,如下:

  • 表格布局:可以創(chuàng)建一個表格包裹盒(table wrapper box)黑低,包括了表格盒(table box)及任何標(biāo)題盒(caption boxes)赘艳。
  • 多列布局:可以在容器盒與內(nèi)容之間創(chuàng)建列盒(column boxes)
  • 彈性布局:將會創(chuàng)建一個彈性容器盒(flex container box)
  • 網(wǎng)格布局:將會創(chuàng)建一個網(wǎng)格容器盒(grid container box)

而這些盒子也將采用不用的格式化上下文來渲染,如 table formatting context(table 布局)克握、flex formatting context(flexbox 布局)蕾管、grid formatting context(grid 布局)。

更多關(guān)于盒子的介紹可參看:

定位方案

上面我們所討論的其實(shí)都是常規(guī)流(normal flow)中盒子的擺放菩暗。但實(shí)際上我們有三種定位方案掰曾,分別為:

  • 常規(guī)流(normal flow):盒一個接一個排列,不同的盒子采用不同的格式化上下文渲染停团。
  • 浮動(float):盒將從常規(guī)流里提出來旷坦,放在當(dāng)前盒的旁邊。
  • 絕對定位(absolute positioning):盒將脫離常規(guī)流佑稠,其坐標(biāo)是絕對的(通過 top / bottom / left / right 來設(shè)置)秒梅。

常規(guī)流(normal flow)

默認(rèn)盒的定位方案就是常規(guī)流,但是如果觸發(fā)了以下任何一個條件舌胶,將不會使用常規(guī)流:

  • position 的值非 static 或 relative
  • float 的值非 none

在常規(guī)流中捆蜀,不同的盒子將采用不同的格式化上下文渲染,也就是上面所講的部分辆琅。

浮動(float)

對于浮動定位方案, 盒稱為浮動盒(floating boxes)漱办。它位于當(dāng)前行的開頭或末尾。這導(dǎo)致常規(guī)流環(huán)繞在它的周邊婉烟,除非設(shè)置 clear 屬性。

要使用浮動定位方案暇屋,元素 CSS 屬性 position 必須為 static 或 relative似袁,然后 float 不為 none 。如果 float 設(shè)為 left, 則浮動定位到當(dāng)前位置的開始位置咐刨,如果設(shè)為 right, 則浮動定位到當(dāng)前位置的最后位置昙衅。

具體介紹可學(xué)習(xí)下面章節(jié):元素浮動——float。

絕對定位(absolute position)

如果元素的屬性 position 不是 static 或 relative定鸟, 那它就是絕對定位元素而涉。

對于絕對定位方案,盒從常規(guī)流中被移除联予,不影響常規(guī)流的布局啼县。 它的定位相對于它的包含塊材原,定位坐標(biāo)可通過屬性 top、bottom季眷、left余蟹、right 來設(shè)置 。

固定定位元素(fixed positioned element)也是絕對定位元素子刮,它的包含塊是視口威酒。當(dāng)頁面滾動時它固定在屏幕上,因?yàn)橐暱跊]有移動挺峡。

具體介紹可學(xué)習(xí)下面章節(jié):元素定位——position葵孤。

參考資料

四、深入了解 inline-block

在之前的課程中橱赠,我們學(xué)習(xí)到了 inline-block 的基礎(chǔ)知識佛呻,接下來我將介紹一些使用 inline-block 產(chǎn)生的問題和解決方法以及其常見的應(yīng)用場景,來進(jìn)一步加深了大家對 inline-block 的理解病线。

水平間隙問題

我們創(chuàng)建一個導(dǎo)航列表吓著,并將其列表 item 設(shè)置為 inline-block,主要代碼如下:

<div class="nav">
  <div class="nav-item"><a>導(dǎo)航</a></div>
  <div class="nav-item"><a>導(dǎo)航</a></div>
  <div class="nav-item"><a>導(dǎo)航</a></div>
</div>

.nav {
  background: #999;
}
.nav-item{
  display:inline-block; /* 設(shè)置為inline-block */
  width: 100px;
  background: #ddd;
}

效果圖如下:

image

我們從效果圖中可以看到列表 item 之間有一點(diǎn)小空隙送挑,但是我們在代碼中并沒有設(shè)置 margin 水平間距绑莺。那么這個空隙是如何產(chǎn)生的呢?

這是因?yàn)槲覀兙帉懘a時輸入空格惕耕、換行都會產(chǎn)生空白符纺裁。而瀏覽器是不會忽略空白符的,且對于多個連續(xù)的空白符瀏覽器會自動將其合并成一個司澎,故產(chǎn)生了所謂的間隙欺缘。

對于上面實(shí)例,我們在列表 item 元素之間輸入了回車換行以方便閱讀挤安,而這間隙正是這個回車換行產(chǎn)生的空白符谚殊。

同樣對于所有的行內(nèi)元素(inline,inline-block)蛤铜,換行都會產(chǎn)生空白符的間隙嫩絮。

如何消除空白符

從上面我們了解到空白符双藕,是瀏覽器正常的表現(xiàn)行為板辽。但是對于某些場景來說,并不美觀怕磨,而且間隙大小非可控穆刻,所以我們往往需要去掉這個空白間隙置尔。一般來說我們有兩種方法來去掉這個換行引起間隙:代碼不換行和設(shè)置 font-size。

代碼不換行

我們了解到氢伟,由于換行空格導(dǎo)致產(chǎn)生換行符榜轿,因此我們可以將上述例子中的列表 item 寫成一行幽歼,這樣空白符便消失,間隙就不復(fù)存在了差导。其代碼如下:

<div class="nav">
  <div class="nav-item">導(dǎo)航</div><div class="nav-item">導(dǎo)航</div><div class="nav-item">導(dǎo)航</div>
</div>

但考慮到代碼可讀及維護(hù)性试躏,我們一般不建議連成一行的寫法。

設(shè)置 font-size

首先要理解空白符歸根結(jié)底是個字符设褐,因此颠蕴,我們可以通過設(shè)置 font-size 屬性來控制產(chǎn)生的間隙的大小。我們知道如果將 font-size 設(shè)置為 0助析,文字字符是沒法顯示的犀被,那么同樣這個空白字也沒了,間隙也就沒了外冀。

于是順著這個思路就有了另一個解決方案:通過設(shè)置父元素的 font-size 為 0 來去掉這個間隙寡键,然后重置子元素的 font-size,讓其恢復(fù)子元素文字字符雪隧。

所以該方法代碼如下:

.nav {
  background: #999;
  font-size: 0; /* 空白字符大小為0 */
}
.nav-item{
  display:inline-block;
  width: 100px;
  font-size: 16px; /* 重置 font-size 為16px*/
  background: #ddd;
}

使用該方法時需要特別注意其子元素一定要重置 font-size西轩,不然很容易掉進(jìn)坑里(文字顯示不出來)。

對齊問題

由于 inline-block 屬于行內(nèi)級元素脑沿,所以 vertical-align 屬性同樣對其適用藕畔。

在正式講解 vertical-align 之前,我們需要先說一些基本概念庄拇。

中線注服、基線、頂線措近、底線

中線(middle)溶弟、基線(baseline)、頂線(text-top瞭郑、底線(text-bottom))是文本的幾個基本線辜御,其對應(yīng)位置如下圖:

image
  • 基線(base line):小寫英文字母x的下端沿。
  • 中線(middle line):小寫英文字母x的中間凰浮。
  • 頂線(text-top):父元素 font-size 大小所組成的一個內(nèi)容區(qū)域的頂部
  • 底線(text-bottom):父元素 font-size 大小所組成的一個內(nèi)容區(qū)域的底部

vertical-align 的值

vertical-align 只接受8個關(guān)鍵字我抠、一個百分?jǐn)?shù)值或者一個長度值。下面我們將看看各關(guān)鍵字如何作用于行內(nèi)元素袜茧。

描述
baseline 默認(rèn)元素的基線與父元素的基線對齊。
sub 將元素的基線與其父元素的下標(biāo)基線對齊瓣窄。
super 將元素的基線與其父代的上標(biāo) - 基線對齊笛厦。
text-top 將元素的頂部與父元素的字體頂部對齊。
text-bottom 將元素的底部與父元素的字體的底部對齊俺夕。
middle 將元素的中間與基線對齊加上父元素的x-height的一半裳凸。
top 將元素的頂部和其后代與整行的頂部對齊贱鄙。
bottom 將元素的底部和其后代與整行的底部對齊。
<length> 將元素的基線對準(zhǔn)給定長度高于其父元素的基線姨谷。
<percentage> 像<長度>值逗宁,百分比是line-height屬性的百分比。

具體 demo 可參考:行內(nèi)級元素垂直對齊方式

五梦湘、清除浮動詳解

清除浮動主要是為了解決由于浮動元素脫離文流導(dǎo)致的元素重疊或者父元素高度坍塌的問題瞎颗,而這兩個問題分別對應(yīng)了需要清除浮動的兩種種情況:清除前面兄弟元素浮動和閉合子元素浮動(解決父元素高度坍塌)。

清除前面兄弟元素浮動

清除前面兄弟元素浮動很簡單捌议,只需要在不想受到浮動元素影響的元素上使用 clear:both 即可哼拔, HTML & CSS 代碼如下:

<div class="fl">我是左浮動元素</div>
<div class="fr">我是右浮動元素</div>
<div class="cb">我不受浮動元素的影響</div>

.fl {
    float: left;
}
.fr {
    float: right;
}
.cb {
    clear: both;
}

在 CSS2 以前,clear 的原理為自動增加元素的上外邊距(margin-top)值瓣颅,使之最后落在浮動元素的下面倦逐。在 CSS2.1 中引入了一個清除區(qū)域(clearance)——在元素上外邊距之上增加的額外間距,使之最后落在浮動元素的下面宫补。所以如果需要設(shè)置浮動元素與 clear 元素的間距檬姥,得設(shè)置浮動的元素的 margin-bottom,而不是 clear 元素的 margin-top粉怕。

demo 可見:clear 清除浮動

閉合子元素浮動

我們知道健民,在計(jì)算頁面排版的時候,如果沒有設(shè)置父元素的高度斋荞,那么該父元素的高度是由他的子元素高度撐開的荞雏。但是如果子元素是設(shè)置了浮動,脫離了文檔流平酿,那么父元素計(jì)算高度的時候就會忽略該子元素凤优,甚至當(dāng)所有子元素都是浮動的時候,就會出現(xiàn)父元素高度為 0 的情況蜈彼,這就是所謂的父元素高度坍塌問題筑辨。為了能讓父元素正確包裹子元素的高度,不發(fā)生坍塌幸逆,我們需要閉合子元素的浮動棍辕。

一般我們有兩種辦法可以用來閉合子元素浮動:

  • 給最后一個元素設(shè)置 clear: both
  • 給父元素新建一個 BFC(塊格式化上下文)

clear:both

由于我們最后一個元素使用 clear:both,所以該元素就能不受浮動元素影響出現(xiàn)在父元素的最底部还绘,而父元素計(jì)算高度的時候需要考慮到這個正常元素的位置楚昭,所以高度自然包裹到了最底部,也就沒有了坍塌拍顷。

對于這個方法抚太,以前我們是利用新增一個空元素(<b><span><div> 等)來實(shí)現(xiàn)的,如下:

<div class="container">
    <div class="box"></div>
    <span class="clear-box"></span>
</div>

.box {
    float: left;
}
.clear-box {
    clear: both;
}

雖然這種辦法比較直觀,但是不是很優(yōu)雅尿贫,因?yàn)樵黾恿艘粋€無用的空白標(biāo)簽电媳,比較冗余而且不方便后期維護(hù)(一般不太建議使用該辦法)。所以后期有了通過父元素的偽元素(::after)實(shí)現(xiàn)的著名 clearfix 方法庆亡,代碼如下:

<div class="container clearfix">
    <div class="box"></div>
</div>

.clearfix::after {
    content:"";
    display:table;
    clear: both;
}

上面方法給父元素增加一個專門用于處理閉合子元素浮動的 clearfix 類名匾乓,該類使用 ::after 偽元素類選擇器增加一個內(nèi)容為空的結(jié)構(gòu)來清除浮動,可能你們比較疑惑的是為什么要設(shè)置 display:table 屬性又谋,這其實(shí)涉及到一個比較復(fù)雜的進(jìn)化過程拼缝,具體可以參考資料——clearfix浮動進(jìn)化史

新建 BFC

該方法的原理是:父元素在新建一個 BFC 時,其高度計(jì)算時會把浮動子元素的包進(jìn)來搂根。

下面我們以實(shí)例為證:如下圖我們的圖片為浮動珍促,父元素 article 的高度就出現(xiàn)了坍塌(沒有包括圖片),而根元素 HTML (默認(rèn)情況下我們的根元素 HTML 就是一個 BFC)的高度則包括了圖片的高度剩愧。

image
image

既然新建一個 BFC 可以解決父元素高度坍陷問題猪叙,那就好辦了,下面這些都可以創(chuàng)建一個 BFC :

  • 根元素或其它包含它的元素
  • 浮動 (元素的 float 不是 none)
  • 絕對定位的元素 (元素具有 position 為 absolute 或 fixed)
  • 內(nèi)聯(lián)塊 inline-blocks (元素具有 display: inline-block)
  • 表格單元格 (元素具有 display: table-cell仁卷,HTML表格單元格默認(rèn)屬性)
  • 表格標(biāo)題 (元素具有 display: table-caption, HTML表格標(biāo)題默認(rèn)屬性)
  • 塊元素具有overflow 穴翩,且值不是 visible
  • display: flow-root

雖然有這么多方法可用,可我們常用的就是 overflow: hidden锦积,代碼如下:

<div class="container">
    <div class="box"></div>
</div

.container {
    overflow: hidden;
}
.box {
    float: left;
}

總結(jié)

上面主要講解了我們比較常的一些清除浮動解決方案芒帕,看似簡單的清除浮動方法其實(shí)則涉及到了很多復(fù)雜的CSS規(guī)則,大家在實(shí)際操作的時候可以針對不同的情況參考上面的方法丰介。

六背蟆、深入了解 z-index

網(wǎng)頁正常文檔流排版可以理解為在一個平面立面里面,元素占據(jù)空間哮幢,依次排列带膀,互不覆蓋。但是當(dāng)頁面中元素設(shè)置了定位屬性的時候橙垢,難免會出現(xiàn)元素之間相互重疊的情況垛叨,比如下圖小貓和小狗的圖片都設(shè)置了絕對定位,2張圖片的位置重疊了柜某,小貓圖顯示在小狗圖上面∷栽現(xiàn)在如果我們想要小狗圖顯示在上面,就需要涉及到 CSS 中的 z-index 屬性了喂击。

z-index

z-index

z-index 屬性用于指定已定位元素在垂直于頁面方向的排列順序剂癌,其屬性值有2種:auto(默認(rèn)值)和整數(shù)。這里有2個需要注意的點(diǎn):

  1. z-index 屬性只對定位元素元素生效翰绊,也就是 position 屬性不為 static 的元素珍手。
  2. 除了默認(rèn)值 auto, z-index 可以設(shè)置為任意整數(shù)辞做,正數(shù)琳要,0,負(fù)數(shù)都可以秤茅。

一般情況下稚补,z-index 值進(jìn)行比較有下面2條規(guī)則:

  1. 數(shù)值大的在上面(auto 數(shù)值上相當(dāng)于0)。
  2. 數(shù)值相同的框喳,在 HTML 結(jié)構(gòu)中排后面的在上面课幕。

所以上面例子,2張圖都沒設(shè)置 z-index 的值五垮,在 dom 結(jié)構(gòu)上排后面的小貓圖展示在上面乍惊。如果想小狗圖展示在小貓圖上面,只需要設(shè)置小狗圖的 z-index 屬性值大于0就可以了放仗。

<img class="dog" src="http://coding.imweb.io/img/p3/z-index/dog.png" alt="dog">
<img class="cat" src="http://coding.imweb.io/img/p3/z-index/cat.png" alt="cat">

.dog {
  position: absolute;
  top: 10px;
  left: 100px;
  z-index: 1; /* 設(shè)置小狗圖的 z-index 值大于0 */
}
.cat {
  position: absolute;
  top: 80px;
  left: 70px;
}

.dog 元素增加了 z-index: 1 屬性就可以讓小狗圖相比于小貓圖在垂直于頁面的方向上離我們更近润绎,這樣效果就是小狗圖顯示在上面了。

z-index

層疊上下文

上面說到诞挨,z-index 默認(rèn)值 auto 數(shù)值上等于0莉撇,那設(shè)置了 z-index:0 和 默認(rèn)的 z-index:auto; 有沒有區(qū)別呢? 答案是有區(qū)別的惶傻。區(qū)別在于設(shè)置了 z-index 屬性為整數(shù)值(包括0)的元素棍郎,自身會創(chuàng)建一個層疊上下文。而創(chuàng)建一個層疊上下文之后银室,其子元素的層疊順序就相對于父元素計(jì)算涂佃,不會與外部元素比較。這樣說比較抽象蜈敢,我們來看個例子辜荠。

<div class="dog-container">
  <img class="dog" src="http://coding.imweb.io/img/p3/z-index/dog.png" alt="dog">
</div>
<div class="cat-container">
  <img class="cat" src="http://coding.imweb.io/img/p3/z-index/cat.png" alt="cat">
</div>

img {
  width: 200px;
}
.dog-container {
  width: 200px;
  height: 100px;
  background: red;
  position: relative;
  z-index: auto; /* 默認(rèn)值auto */
}
.dog {
  position: absolute;
  top: 10px;
  left: 100px;
  z-index: 2;
}
.cat {
  position: absolute;
  top: 80px;
  left: 70px;
  z-index: 1;
}

上面例子中,我們給 .dog.cat 增加了容器 .dog-container.cat-container, 并且 .dog.cat 都設(shè)置了 z-index 值扶认,所以都顯示在紅色背景的 .container 之上侨拦,而且 .dog z-index 數(shù)值比較大,所以顯示在上面辐宾。

z-index

但是當(dāng)我們設(shè)置了 .dog-container 的 z-index 屬性值為0之后狱从,我們發(fā)現(xiàn),z-index 值比較大的 .dog 元素反而到 z-index值比較小的 .cat 下面了

.dog-container {
  width: 200px;
  height: 100px;
  background: red;
  position: relative;
  z-index: 0; /* 將 z-index 值改成0 */
}

效果:

z-index

其原因就在于我們給 .dog-container 設(shè)置了 z-index:0 之后叠纹,.dog-container 就創(chuàng)建了自己的層疊上下文季研,其子元素 .dog 在比較層疊順序的時候只會在 .dog-container 內(nèi)比較,而不會與外面的 .cat 比較誉察。如下圖所示:

z-index

上面例子告訴我們与涡,并不是所有情況 z-index 值大的元素都會在上面,我們在進(jìn)行 z-index 比較的時候要留意其祖先元素有沒有建立獨(dú)立的層疊上下文,z-index 只有在同一個層疊上下文中比較才有意義驼卖。另外氨肌,對定位元素設(shè)置 z-index 屬性不是唯一創(chuàng)建層疊上下文的方法,具有下面屬性的元素都會創(chuàng)建層疊上下文(具體可參看:層疊上下文 | MDN):

  • 根元素 (HTML)
  • z-index 值不為 "auto"的 絕對/相對定位
  • 一個 z-index 值不為 "auto"的 flex 項(xiàng)目 (flex item)酌畜,即:父元素 display: flex|inline-flex
  • opacity 屬性值小于 1 的元素
  • transform 屬性值不為 "none"的元素怎囚,
  • mix-blend-mode 屬性值不為 "normal"的元素,
  • filter值不為“none”的元素桥胞,
  • perspective值不為“none”的元素恳守,
  • isolation 屬性被設(shè)置為 "isolate"的元素,
  • position: fixed
  • 在 will-change 中指定了任意 CSS 屬性贩虾,即便你沒有直接指定這些屬性的值(參考這篇文章
  • -webkit-overflow-scrolling 屬性被設(shè)置 "touch"的元素

也就是說催烘,上面例子中, .dog-container 滿足上面任意一條屬性缎罢,也會一樣出現(xiàn)上面的情況伊群。比如設(shè)置 opacity 屬性:

.dog-container {
  width: 200px;
  height: 100px;
  background: red;
  position: relative;
  opacity: 0.6; /* 設(shè)置 opacity 屬性小于1也會創(chuàng)建層疊上下文 */
}

效果如下:

z-index

總結(jié)

  1. z-index 屬性用于描述定位元素在垂直于頁面方向上的排列順序。
  2. z-index 一般比較規(guī)則是值大在上屁使,值相同則排后面的在上在岂。
  3. 元素在設(shè)置了某些屬性的時候會創(chuàng)建層疊上下文,z-index 值比較大小只有在同一個層疊上下文才有效蛮寂。

參考文檔

七蔽午、flexbox 剩余空間分配規(guī)則

前面我們學(xué)習(xí)到了 flexbox 布局。通過使用 flexbox 布局酬蹋,我們可以更輕松實(shí)現(xiàn)以往很難實(shí)現(xiàn)的頁面布局及老。本文主要講解 flexbox 布局是如何去分配和計(jì)算布局剩余空間的。(本文閱讀前要求你對 flexbox 已經(jīng)有了初步的認(rèn)知范抓,如果不是很了解骄恶,建議先學(xué)習(xí)前面視頻內(nèi)容。)

基礎(chǔ)概念

為了更好地理解本文內(nèi)容匕垫,我們需要先了解下面一些基礎(chǔ)概念僧鲁。

flexbox 容器 (flexbox container)

flexbox 容器又稱彈性容器,通過設(shè)置 display: flex 而產(chǎn)生象泵,簡單示例如下:

.container {
  display: flex; /* 或者 inline-flex */
}

flexbox 項(xiàng)目 (flexbox item)

當(dāng)設(shè)置一個元素為 flexbox 容器時寞秃,其直接子元素將自動成為容器成員,也可以稱之為:flexbox 項(xiàng)目偶惠。

注:因?yàn)?flexbox 布局是發(fā)生在父元素和子元素之間的春寿,所以下面為了行文方便,統(tǒng)一將 flexbox 容器稱為“父容器”忽孽,而 flexbox 項(xiàng)目統(tǒng)一稱為“子元素”

剩余空間

剩余空間就是指父容器在主軸方向上剩余未分配的空間绑改,它是 flexbox 布局中一個很重要的詞谢床。我們可以借助下面的例子來更好地理解:

image
<div class="container" width="600px" style="display:flex;">
  <span class="item1" width="200px">item1</span>
  <span class="item2" width="200px">item2</span>
</div>

上面代碼,我們定義了一個寬度為600px的父容器 container厘线,以及寬度為200px的子元素 item1 和 item2 识腿,那么我們得出其剩余空間為200px,計(jì)算公式為:

剩余空間 = 父容器的寬度 - item1的寬度 - item2的寬度

剩余空間分配相關(guān)屬性

flexbox 布局中的子元素可以通過設(shè)置 flex 屬性來改變其所分配到的空間大小皆的。flex 屬性包括了 flex-basis覆履、 flex-growflex-shrink

flex-basis

flex-basis 用來定義子元素的默認(rèn)寬或高费薄。如果父容器 flex-direction 屬性的方向?yàn)樗椒较騽t為寬度,如為垂直方向則為高度栖雾。相當(dāng)于給子元素設(shè)置寬或高楞抡。如果同時設(shè)置了該屬性與寬或高,則該屬性權(quán)重大于寬或高的值析藕。

flex-grow

flex-grow 用來指定父容器多余空間的分配比率召廷,默認(rèn)值為0≌穗剩看到這里竞慢,大家可能還是沒有概念。為了更形象地理解治泥,我們一起看下下面的例子筹煮。

例子: 只設(shè)置 item1 的 flex-grow 為1

image

其 HTML 代碼如下:

<div class="container">
  <span class="item item-flex-grow">item1</span>
  <span class="item item2">item2</span>
</div>

其 CSS 代碼如下:

/* css 部分 */
.container {
  display: flex;
  width: 600px;
  height: 140px;
  align-items: center;
  background-color: #ddd;
}
.item {
  width: 200px;
  height: 120px;
  line-height: 120px;
  text-align: center;
  background-color: orange;
}
.item2 {
  background-color: green;
}
.item-flex-grow{
  flex-grow:1;
  background-color: 
}

這里我們對 item1 設(shè)置flex-grow:1 后,我們可以看到 item1 的所占空間寬度變成400px居夹,也就是說 item1 把之前我們所說的父容器剩余的200px空間都占用了败潦。

在這里我們可以得出:其實(shí) flex-grow 即定義如何去分配父容器的剩余空間 ,當(dāng)值為0時准脂,則子元素都不會索取父容器的剩余空間劫扒。當(dāng) item1 設(shè)置 flex-grow: 1 的時候,由于 item2 沒有設(shè)置 flex-grow 的值狸膏,則剩余空間將會被分成一份沟饥,并且分別分給了 item1。

例子: 設(shè)置 item1 的 flex-grow 為1湾戳,且 item2 的 flex-grow 為3

如果此時我們設(shè)置 item2 的flex-grow:3 贤旷,item2 也將會參與索取父容器的剩余空間,此時父容器的剩余空間將分為4份院塞,然后1份分配到 item1遮晚,而分配3份到 item2,如下圖:

image

如果子元素的寬度的總和超過父容器拦止,flex-grow 將不生效县遣。

上面的例子糜颠,我們只考慮了子元素的寬度總和都是沒有超過父容器的寬度的情況,則其可以使用 flex-grow 來分配父容器的剩余空間萧求。那么當(dāng)子元素的寬度總和超過父容器的寬度時其兴,這時剩余空間還可以分配嗎?此時 flex-grow 是否還有效呢夸政?讓我們先看看下面的例子:

這里我們設(shè)置上面例子的 item1 和 item2 的寬度為350px元旬,則子元素的寬度總和為700px且超過父容器container的600px寬度。

image

我們可以看到 flex-grow 并沒有起到作用守问,且兩個 item 的寬度還被壓縮到只有300px匀归。

通過之前學(xué)到的如何計(jì)算剩余空間的方法,我們可以算出本例子的剩余空間為600px - 700px即 -100px,這里可以得出由于沒有剩余空間耗帕,則定義了 flex-grow 的子元素能分配到的空間為0穆端,故不生效。另外我們需要知道的是 flexbox 環(huán)境的父容器的寬度600px并不會因?yàn)樽釉氐目倢挾淖兎卤悖醋釉氐膶挾瓤偤妥疃嗟扔诟溉萜鞯膶挾忍鍐詾榱俗屪釉赝暾@示在父容器內(nèi),只有兩個辦法:

  • 通過設(shè)置 flex-wrap 來使子元素?fù)Q行
  • 通過壓縮子元素來使其能容納在父容器內(nèi)

flex-shrink

flex-shrink 用來指定父容器空間不夠時子元素的縮小比例嗽仪,默認(rèn)為1荒勇。如果一個 flexbox 項(xiàng)目的 flex-shrink 屬性為0,則該元素不會被壓縮闻坚。

為什么需要 flex-shrink 來定義縮小比例呢沽翔?

上面我們可以知道,當(dāng)子元素的寬度總和大于 flexbox 父容器的寬度時鲤氢,其剩余空間將為負(fù)數(shù)搀擂,如果沒有設(shè)置換行的情況下,其將會通過壓縮子元素來使其能夠容納在父容器內(nèi)卷玉。那么我們?nèi)绾慰刂谱釉氐膲嚎s比例呢哨颂?答案就是通過將通過設(shè)置 flex-shrink 這個屬性。

例子:設(shè)置項(xiàng)目的flex-shrink

下面例子相种,我們設(shè)置兩個 item 的寬度為350px威恼,而容器 container 的寬度仍為600px。同時定義了 item1 和 item2 的 flex-shrink 的屬性分別為1和4寝并。如下所示:

image

代碼如下:

/* 這里只展示關(guān)鍵css */
.container {
  display: flex;
  width: 600px;
  height: 140px;
}
.item {
  width: 350px;
  height: 120px;
}
.item1 {
  flex-shrink: 1;
}
.item2{
  flex-shrink: 4;
}

我們看到由于缺少100px的空間箫措,按照 item1 和 item2 定義的 flex-shrink 的值,缺少的100px將分成5份衬潦。item1 將壓縮其中的 1/5 即20px斤蔓,item2 的將壓縮其中的 4/5 即80px。

例子:設(shè)置項(xiàng)目的 flex-shrink 為0

在上面的知識中镀岛,我們了解到 flex-shrink 默認(rèn)值為1弦牡,即默認(rèn)子元素在父容器空間不足時會被壓縮∮淹裕現(xiàn)在我們把項(xiàng)目的 flex-shrink 設(shè)為0來看下不壓縮的情況。如下所示:

[圖片上傳中...(image-22b3f3-1542962334288-0)]

代碼如下:

/* 這里只展示關(guān)鍵css */
.container {
  display: flex;
  width: 600px;
  height: 140px;
}
.item {
  width: 350px;
  height: 120px;
  flex-shrink: 0;
}

更多閱讀

八驾锰、grids 布局系統(tǒng)

我們之前有提到過網(wǎng)格系統(tǒng)卸留,比如960sbootstrap 的網(wǎng)格系統(tǒng)椭豫,但是這些網(wǎng)格系統(tǒng)都是模擬出來的(使用 float 或 flexbox)耻瑟,而并非天生的,雖然可以解決一些常見布局問題赏酥,但面臨 Win10 UI 還是有點(diǎn)力所不及喳整,如下圖:

image

但是隨著 CSS 的不斷發(fā)展及完善,一種新的網(wǎng)格布局方式被納入規(guī)范今缚,它將會解決所有的網(wǎng)格問題算柳,這就是我們要說的 CSS Grid Layout。

網(wǎng)格系統(tǒng)基礎(chǔ)概念

在說 CSS Grid Layout 之前姓言,我們先來看看 excel 的表格。

excel css gri layout

以我們的 Sheet1 的 A1 單元格為例蔗蹋,先是有上下左右四條線圍著何荚,然后定位是由豎直的 A 欄與橫向的1行二維坐標(biāo)表示 A1。如果有需要甚至還還可以和鄰近的單元格合并猪杭。

現(xiàn)在我們提煉下上面的幾個概念:線條餐塘,欄(豎直),行(橫向)皂吮,單元格戒傻,合并。接下來我們把這些概念對應(yīng)到我們的網(wǎng)格系統(tǒng):

CSS Grid Layout
  • Grid Container:首先我們要設(shè)置父元素的布局為 grid蜂筹,通過使用 display 屬性給元素顯式設(shè)置屬性值grid或inline-grid需纳,此時這個元素將自動變成網(wǎng)格容器,對應(yīng)上圖的Sheet1
  • Grid Item:Item 是 container 的直接子元素艺挪,如果不考慮單元格的合并跟下面的 cell 是一樣的不翩,如果有單元格合并,在該 item 可能包括幾個cell麻裳,對應(yīng)上圖的一個個格子口蝠,如藍(lán)色的 A1
  • Grid Lines:網(wǎng)格線分為水平線和垂直線,對應(yīng)上圖的橙色線條
  • Grid Track:就是由lines構(gòu)成的水平和垂直空間津坑,對應(yīng)到上圖的水平和垂直灰色區(qū)域妙蔗,而對于table來說就是row和column
  • Grid Cell:簡單來說就是單元格了,對應(yīng)到上圖就是藍(lán)色的A1疆瑰,而對于table來說就是單元格
  • Grid Area:網(wǎng)格區(qū)域是由任意四條網(wǎng)格線組成的空間眉反,可能由一個或多個單元格組成睡互。對應(yīng)到上圖就是紅色區(qū)域汽烦,相當(dāng)于表格中的合并單元格之后的區(qū)域

網(wǎng)格系統(tǒng)基本屬性

網(wǎng)格系統(tǒng)布局其實(shí)跟 flexbox 布局差不多,都是由父子元素構(gòu)成的布局。所以屬性分為父元素屬性和子元素屬性轻绞。

因篇幅有限,這里只簡單介紹每個屬性的用途懊纳,具體的屬性取值請參考:

父元素(Grid Container)屬性

這里我們將父元素屬性大概分為三大類蚓炬,其中第一類與第二類屬性可以簡寫為 grid 屬性(不包括 display 屬性):

第一類:如何去定義一個網(wǎng)格系統(tǒng),行列及間距等秘狞。

  • display:grid/inline-grid叭莫,定義使用網(wǎng)格系統(tǒng)
  • grid-template-columns:定義垂直欄
  • grid-template-rows:定義水平行
  • grid-template-areas:定義區(qū)域
  • grid-column-gap:定義垂直欄與垂直欄之間的間距,如上圖的A與B之間的間距
  • grid-row-gap:定義水平行與水平行之間的間距烁试,如上圖的1與2之間的間距
  • grid-gap:上面兩個欄與行間距的縮寫

第二類:自動分配形式雇初,當(dāng)定義的行或列數(shù)量不夠時,多出 item 的自動排列方式:

  • grid-auto-columns:定義多出的 item 的自動column的寬度大小
  • grid-auto-rows:定義多出的 item 自動 row 的高度大小
  • grid-auto-flow:定義自動 item 是按照先水平方向排列還是垂直方向排列

第三類:分布對齊的方式(屬性跟 flexbox 的有點(diǎn)像)减响。

  • justify-items:item 在水平行中的對齊方式
  • align-items:item 在垂直欄中的對齊方式
  • justify-content:整個水平行在 grid 范圍的對齊方式靖诗,這里有個好用的 space-evenly 值,補(bǔ)足了以前flex的 space-aroundspace-between 的不足
  • align-content:整個垂直欄在 grid 范圍的對齊方式

子元素(Grid Item)屬性

接下來是我們的 item 屬性支示,同樣這里我也將它分為兩類:

第一類:單元格所占格子多少

  • grid-column-start:item 的起始欄
  • grid-column-end:item 的結(jié)束欄
  • grid-column:起始欄和結(jié)束欄的簡寫
  • grid-row-start:item 的起始行
  • grid-row-end:item 的結(jié)束行
  • grid-row:起始行與結(jié)束行的簡寫
  • grid-area:item所在區(qū)域

第二類:單元格的自定義對齊方式刊橘,這個跟 flexbox 的 item 有點(diǎn)相似。

  • justify-self:自定義 item 的水平方向?qū)R方式
  • align-self:自定義 item 的垂直方向?qū)R方式

實(shí)例演示

百說不如一練颂鸿,我們接下來使用網(wǎng)格系統(tǒng)來實(shí)戰(zhàn)下我們的 Win10 UI促绵,如下圖:

grids demo

)

html結(jié)構(gòu)為:

<div class="grid">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
    <div class="item">7</div>
    <div class="item">8</div>
    <div class="item">9</div>
    <div class="item">10</div>
    <div class="item">11</div>
</div

寫好結(jié)構(gòu)后,我們就開始使用剛才說得 grid 來實(shí)現(xiàn)我們的效果了嘴纺。先拆分成最小的單元格為 6欄 * 3行败晴,最小單元格的大小為140px,整體內(nèi)容一屏水平垂直居中栽渴。

html,body {
 height: 100%;
}
.grid {
  height: 100%;
  display: grid; /* 網(wǎng)格布局 */

  /* 整體水平垂直居中 */
  justify-content: center;
  align-content: center;

  /* 定義6欄3行 */
  grid-template-columns: 140px 140px 140px 140px 140px 140px;
  grid-template-rows: 140px 140px 140px;

  /* 定義item之間的間距為20px */
  grid-gap: 20px;

  background: #efefef;
}
.item{
  background: #ccc;
}

現(xiàn)在我們可以看到效果如下:

image

接下來要合并單元格實(shí)現(xiàn)我們的最終效果尖坤。合并單元格有兩種實(shí)現(xiàn)方式一種是 line 的開始與結(jié)束(包括 colunm 與 row),另一種就是在 grid 上面定義的 area熔萧,這里我們使用第一種方法糖驴。

這里重提下上面的 Grid Lines 概念,如要實(shí)現(xiàn) n欄 * m行的網(wǎng)格佛致,則需要n+1條垂直line贮缕,m+1條水平線。

第一個 item 元素單元格占了兩列俺榆,第一列和第二列感昼,那么就垂直欄開始于第一條 line,結(jié)束于第三條 line罐脊,同樣第5個 item 元素也是如此

.item:nth-child(1),
.item:nth-child(5) {
  grid-column: 1 / 3; /* 起始于1定嗓,結(jié)束于3 */
}

而第二個 item 元素欄和行都跨了兩個蜕琴,CSS 代碼如下:

.item:nth-child(2) {
  grid-column: 3 / 5; /* column起始于3,結(jié)束于5 */
  grid-row: 1 / 3;  /* row起始于1宵溅,結(jié)束于3 */
}

同樣第七個 item 元素行跨了兩個凌简,第9個 item 元素欄跨了兩個,CSS 代碼如下:

.item:nth-child(7) {
  grid-column: 6;
  grid-row: 2 / 4; /* row起始于2恃逻,結(jié)束于4 */
}
.item:nth-child(9) {
  grid-column: 2 / 4; /* column起始于2雏搂,結(jié)束于4 */
}

這個布局就這么簡單的完成了,效果可見 demo

瀏覽器支持

現(xiàn)代瀏覽器最新版本基本上都支持了 CSS Grid Layout寇损,下圖是 caniuse 上的支持情況:

caniuse

有些瀏覽器舊版本的已經(jīng)實(shí)現(xiàn)但是沒有默認(rèn)開啟(chrome < 57凸郑,firefox < 52)則需要通過下面的方式手動設(shè)置開啟:

  • chrome 在地址欄輸入“chrome://flags”,找到"experimental web platform features"開啟
  • firefox在地址欄輸入"about:config"矛市,找到"layout.css.grid.enabled"開啟

總結(jié)

上面只是 grid 布局的簡單使用芙沥,實(shí)際上 grid 布局十分強(qiáng)大,使用起來也十分方便浊吏,未來將會是布局的主力軍而昨,但是目前由于兼容問題暫時不建議在生產(chǎn)環(huán)境中使用,不過我們相信很快就可以見識到 grid 布局的強(qiáng)大威力了找田。

參考資料

九配紫、瀏覽器如何渲染 HTML & CSS

我們現(xiàn)在已經(jīng)知道,使用 HTML & CSS 可以搭建出一個漂亮的 Web 頁面午阵。

那么瀏覽器到底是如何使用我們的 HTML & CSS 渲染成我們在屏幕上所看到的頁面呢?

雖然具體渲染過程很復(fù)雜享扔,但是還是可以將其分為幾個關(guān)鍵路徑底桂,如下:

  • 處理 HTML 標(biāo)記并構(gòu)建 DOM 樹
  • 處理 CSS 標(biāo)記并構(gòu)建 CSSOM 樹
  • 將 DOM 與 CSSOM 合并成一個渲染樹
  • 根據(jù)渲染樹來布局,以計(jì)算每個節(jié)點(diǎn)的幾何信息惧眠,再將各個節(jié)點(diǎn)繪制到屏幕上

構(gòu)建 DOM 樹

首先瀏覽器渲染頁面前會根據(jù) HTML 結(jié)構(gòu)構(gòu)建成對應(yīng)的 DOM 樹籽懦。

以下面的 HTML 代碼為例:

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>

其 DOM 樹生成的流程如下圖:

[圖片上傳中...(image-b05031-1542962693660-5)]

  1. 轉(zhuǎn)換: 瀏覽器從磁盤或網(wǎng)絡(luò)讀取 HTML 的原始字節(jié),并根據(jù)文件的指定編碼(例如 UTF-8)將它們轉(zhuǎn)換成各個字符氛魁。
  2. 令牌化: 瀏覽器將字符串轉(zhuǎn)換成 W3C HTML5 標(biāo)準(zhǔn)規(guī)定的各種令牌暮顺,例如,“”秀存、“”捶码,以及其他尖括號內(nèi)的字符串。每個令牌都具有特殊含義和一組規(guī)則或链。
  3. 詞法分析: 發(fā)出的令牌轉(zhuǎn)換成定義其屬性和規(guī)則的“對象”惫恼。
  4. DOM 構(gòu)建: 最后,由于 HTML 標(biāo)記定義不同標(biāo)記之間的關(guān)系(一些標(biāo)記包含在其他標(biāo)記內(nèi))澳盐,創(chuàng)建的對象鏈接在一個樹數(shù)據(jù)結(jié)構(gòu)內(nèi)祈纯,此結(jié)構(gòu)也會捕獲原始標(biāo)記中定義的父項(xiàng)-子項(xiàng)關(guān)系:HTML 對象是 body 對象的父項(xiàng)令宿,body 是 paragraph 對象的父項(xiàng),依此類推腕窥。

整個流程的最終輸出就是我們這個簡單頁面的文檔對象模型 (DOM)粒没,如下圖:

[圖片上傳中...(image-2b953-1542962693660-4)]

CSSOM

在瀏覽器構(gòu)建上面的 DOM 時,在文檔的 head 部分遇到了一個 link 標(biāo)記簇爆,該標(biāo)記引用一個外部 CSS 樣式表:style.css癞松。由于預(yù)見到需要利用該資源來渲染頁面,它會立即發(fā)出對該資源的請求冕碟,并返回以下內(nèi)容:

/* style.css */

body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }

與處理 HTML 時一樣拦惋,我們需要將收到的 CSS 規(guī)則轉(zhuǎn)換成某種瀏覽器能夠理解和處理的東西。因此安寺,我們會重復(fù) HTML 過程厕妖,不過是為 CSS 而不是 HTML:

image

CSS 字節(jié)轉(zhuǎn)換成字符,接著轉(zhuǎn)換成令牌和節(jié)點(diǎn)挑庶,最后掛靠到一個稱為“CSS 對象模型”(CSSOM) 的樹結(jié)構(gòu)內(nèi):

[圖片上傳中...(image-d167f0-1542962693660-2)]

CSSOM 為何具有樹結(jié)構(gòu)言秸?這是因?yàn)闉g覽器為頁面上的任何對象計(jì)算最后一組樣式時,都會先從適用于該節(jié)點(diǎn)的最通用規(guī)則開始(例如迎捺,如果該節(jié)點(diǎn)是 body 元素的子項(xiàng)举畸,則應(yīng)用所有 body 樣式),然后通過應(yīng)用更具體的規(guī)則(即規(guī)則“向下級聯(lián)”)以遞歸方式優(yōu)化計(jì)算的樣式凳枝。

以上面的 CSSOM 樹為例進(jìn)行更具體的闡述抄沮。span 標(biāo)記內(nèi)包含的任何置于 body 元素內(nèi)的文本都將具有 16 像素字號,并且顏色為紅色 — font-size 指令從 body 向下級聯(lián)至 span岖瑰。不過叛买,如果某個 span 標(biāo)記是某個段落 (p) 標(biāo)記的子項(xiàng),則其內(nèi)容將不會顯示蹋订。

合并渲染樹

接下來就是將 DOM 樹與 CSSOM 樹合并形成渲染樹率挣。

渲染樹會網(wǎng)羅網(wǎng)頁上所有可見的 DOM 內(nèi)容,以及每個節(jié)點(diǎn)的所有 CSSOM 樣式信息露戒。

[圖片上傳中...(image-293e8f-1542962693660-1)]

注:渲染樹只包含渲染網(wǎng)頁所需的節(jié)點(diǎn)椒功,如display: none;的元素是不會出現(xiàn)在渲染樹種的。

布局及繪制

有了渲染樹智什,我們就可以進(jìn)入“布局”階段动漾。

到目前為止,我們計(jì)算了哪些節(jié)點(diǎn)應(yīng)該是可見的以及它們的計(jì)算樣式撩鹿,但我們尚未計(jì)算它們在設(shè)備視口內(nèi)的確切位置和大小---這就是“布局”階段谦炬,也稱為“自動重排”。

為弄清每個對象在網(wǎng)頁上的確切大小和位置,瀏覽器從渲染樹的根節(jié)點(diǎn)開始進(jìn)行遍歷键思。讓我們考慮下面這樣一個簡單的實(shí)例:

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Critial Path: Hello world!</title>
  </head>
  <body>
    <div style="width: 50%">
      <div style="width: 50%">Hello world!</div>
    </div>
  </body>
</html>

以上網(wǎng)頁的正文包含兩個嵌套 div:第一個(父)div 將節(jié)點(diǎn)的顯示尺寸設(shè)置為視口寬度的 50%础爬;而里面內(nèi)嵌的第二個 div 將其寬度設(shè)置為其父項(xiàng)的 50%,即視口寬度的 25%吼鳞。如下圖:

image

布局流程的輸出是一個“盒模型”看蚜,它會精確地捕獲每個元素在視口內(nèi)的確切位置和尺寸:所有相對測量值都轉(zhuǎn)換為屏幕上的絕對像素。

最后赔桌,既然我們知道了哪些節(jié)點(diǎn)可見供炎、它們的計(jì)算樣式以及幾何信息,我們終于可以將這些信息傳遞給最后一個階段:將渲染樹中的每個節(jié)點(diǎn)轉(zhuǎn)換成屏幕上的實(shí)際像素形成我們可見的頁面疾党。這一步通常稱為“繪制”或“柵格化”音诫。

注:執(zhí)行渲染樹構(gòu)建、布局和繪制所需的時間將取決于文檔大小雪位、應(yīng)用的樣式竭钝,以及運(yùn)行文檔的設(shè)備:文檔越大,瀏覽器需要完成的工作就越多雹洗;樣式越復(fù)雜香罐,繪制需要的時間就越長(例如,單色的繪制開銷“較小”时肿,而陰影的計(jì)算和渲染開銷則要“大得多”)庇茫。

參考資料

十、重排與重繪

一個頁面渲染完畢后螃成,隨著用戶的操作旦签,或者數(shù)據(jù)變化,網(wǎng)頁還會進(jìn)行重新渲染寸宏。根據(jù)不同的觸發(fā)條件顷霹,重新渲染分為兩種情況:重排(reflow)和重繪(repaint)。

所有對元素視覺表現(xiàn)屬性的修改击吱,都會導(dǎo)致重繪(repaint)。比如修改了背景顏色遥昧、文字顏色等覆醇。

所有會觸發(fā)元素布局發(fā)生變化的修改,都會導(dǎo)致重排(reflow)炭臭。比如窗口尺寸發(fā)生變化永脓,刪除、添加 DOM 元素鞋仍,修改了影響元素盒子大小的 CSS 屬性如 width常摧、 heightpadding 等。

通常情況下落午,重排的影響更大谎懦,重排會導(dǎo)致文檔局部或全部的重新運(yùn)算:重新計(jì)算元素位置,大小溃斋。(改變一個元素的布局界拦,可能會影響很多個元素的布局)

不管是重繪還是重排導(dǎo)致的重新渲染,都會阻塞瀏覽器梗劫。重新渲染的的過程中享甸,JavaScript 會被阻塞,用戶的交互行為也會被卡住梳侨。復(fù)雜的 CSS 動畫甚至?xí)下?JavaScript 的運(yùn)行速度蛉威。

注:本文涉及到的 JavaScript 部分,可以先忽略走哺,等以后學(xué)習(xí)了 JavaScript 再來查看蚯嫌。

導(dǎo)致重排和重繪的場景

CSS 屬性改變

網(wǎng)站 CSS trigglers 列出了所有 CSS 屬性對 layout (布局)、paint (繪制)的影響割坠。通過這個表齐帚,可以查到不同內(nèi)核下,對 CSS 屬性的修改會導(dǎo)致重繪彼哼、重排還是兩者都會發(fā)生对妄。

re-render

注:Composite (渲染層合并) 是 chrome 引入 GPU 加速帶來的新概念。(相關(guān)信息可參看下面的參看資料)

對 CSS 屬性進(jìn)行修改敢朱,包括但不限于以下場景:

  • 通過 display: none 隱藏 DOM 節(jié)點(diǎn)(導(dǎo)致重繪和重排)
  • 通過 visibility: hidden 隱藏 DOM 節(jié)點(diǎn) (導(dǎo)致重繪剪菱,因?yàn)樗鼪]有影響其它元素位置布局)
  • CSS 動畫
  • 通過 JavaScript 添加樣式,修改樣式

用戶交互

  • 對瀏覽器窗口進(jìn)行縮放操作會導(dǎo)致重排
  • 對 DOM 節(jié)點(diǎn)進(jìn)行操作拴签,添加孝常、刪除、更新 DOM 節(jié)點(diǎn)都會導(dǎo)致重排
  • 光標(biāo) :hover 蚓哩、進(jìn)入文本輸入框构灸、修改瀏覽器的字體都會導(dǎo)致重排

最佳實(shí)踐

所有的最佳實(shí)踐都是圍繞盡最大可能的降低重繪和重排的頻率,來達(dá)到減少重新渲染的目的岸梨。

CSS 屬性的讀喜颁、寫操作分開

瀏覽對 CSS 屬性的連續(xù)修改做了優(yōu)化,比如下面的連續(xù)修改兩次 style曹阔,不會導(dǎo)致兩次重新渲染:

div.style.color = 'blue';
div.style.marginTop = '30px';

上面代碼只會進(jìn)行一次重新渲染半开。但是如果寫的不好,則會觸發(fā)兩次重新渲染赃份,如下:

div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';

上面代碼對 div 元素設(shè)置背景色以后寂拆,第二行要求瀏覽器給出該元素的位置奢米,所以瀏覽器不得不重新渲染然后得到該元素的位置。

除此之外纠永,樣式的寫操作之后鬓长,如果有下面這些屬性的讀操作,都會引發(fā)瀏覽器立即重新渲染:

  • offsetTop/offsetLeft/offsetWidth/offsetHeight
  • scrollTop/scrollLeft/scrollWidth/scrollHeight
  • clientTop/clientLeft/clientWidth/clientHeight
  • getComputedStyle()

通過 class 或者 csstext 來批量更新樣式

上面對通過對 style 對 CSS 屬性一個一個修改渺蒿,其實(shí)更好的方式應(yīng)該是通過 class 來批量化痢士。

// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";

// good 
el.className += " theclassname";

// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

其他方法

  • DOM 樣式離線更新:盡量使用離線 DOM,而不是真實(shí)的網(wǎng)頁 DOM 來改變元素樣式茂装。比如怠蹂,操作 Document Fragment對象,完成后再把這個對象加入 DOM少态。再比如城侧,使用 cloneNode() 方法,在克隆的節(jié)點(diǎn)上進(jìn)行操作彼妻,然后再用克隆的節(jié)點(diǎn)替換原始節(jié)點(diǎn)嫌佑。
  • 使用 display: none 進(jìn)行樣式批量更新:先將元素設(shè)為 display: none(需要1次重排和重繪),然后對這個節(jié)點(diǎn)進(jìn)行100次操作侨歉,最后再恢復(fù)顯示(需要1次重排和重繪)屋摇。這樣一來,你就用兩次重新渲染幽邓,取代了可能高達(dá)100次的重新渲染炮温。
  • 善用 position:position 屬性為 absolutefixed 的元素,重排的開銷會比較小牵舵,因?yàn)椴挥每紤]它對其他元素的影響柒啤。
  • 將元素設(shè)置為不可見:只在必要的時候,才將元素的 display 屬性為可見畸颅,因?yàn)椴豢梢姷脑夭挥绊懼嘏藕椭乩L担巩。另外,visibility : hidden 的元素只對重繪有影響没炒,不影響重排涛癌。
  • 減少樣式的更新頻率:使用虛擬 DOM 腳本庫,比如 React 等送火。
  • 調(diào)節(jié) js 運(yùn)行幀率:使用 window.requestAnimationFrame()祖很、window.requestIdleCallback() 這兩個方法調(diào)節(jié)重新渲染的頻率。
  • 慎用 table 布局:table 的單元格具有非常好的自適應(yīng)特性漾脂,但是同時代價也很高,能不用就不用胚鸯。如果非要使用 table 骨稿,給 table 添加 table-layout: fixed 屬性,這個屬性的目的是讓后面單元格的寬度由表頭的寬度來決定——減少布局的計(jì)算量。

參看資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坦冠,一起剝皮案震驚了整個濱河市形耗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辙浑,老刑警劉巖激涤,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異判呕,居然都是意外死亡倦踢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門侠草,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辱挥,“玉大人,你說我怎么就攤上這事边涕∥畹猓” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵功蜓,是天一觀的道長园爷。 經(jīng)常有香客問我,道長式撼,這世上最難降的妖魔是什么童社? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮端衰,結(jié)果婚禮上叠洗,老公的妹妹穿的比我還像新娘。我一直安慰自己旅东,他們只是感情好灭抑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抵代,像睡著了一般腾节。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荤牍,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天案腺,我揣著相機(jī)與錄音,去河邊找鬼康吵。 笑死劈榨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晦嵌。 我是一名探鬼主播同辣,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼拷姿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了旱函?” 一聲冷哼從身側(cè)響起响巢,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棒妨,沒想到半個月后踪古,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡券腔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年伏穆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颅眶。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜈出,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涛酗,到底是詐尸還是另有隱情铡原,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布商叹,位于F島的核電站燕刻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剖笙。R本人自食惡果不足惜卵洗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弥咪。 院中可真熱鬧过蹂,春花似錦、人聲如沸聚至。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扳躬。三九已至脆诉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贷币,已是汗流浹背击胜。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留役纹,地道東北人偶摔。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像促脉,于是被迫代替她去往敵國和親辰斋。 傳聞我的和親對象是個殘疾皇子信不,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,748評論 1 92
  • CSS 是什么 css(Cascading Style Sheets)亡呵,層疊樣式表,選擇器{屬性:值硫戈;屬性:值}h...
    崔敏嫣閱讀 1,481評論 0 5
  • 所有題目答案整理自網(wǎng)絡(luò)锰什,如有錯誤,接受指正丁逝,拒絕批評汁胆! 關(guān)于html5 HTML5的十大新特性 語義化標(biāo)簽使得頁面...
    黃金原野閱讀 1,463評論 0 0
  • 由于項(xiàng)目上的需要,我要同時往orcale數(shù)據(jù)庫與sqlserver數(shù)據(jù)中插入數(shù)據(jù)霜幼,需要在一個事務(wù)之內(nèi)完成這兩個庫的...
    idelo閱讀 25,010評論 4 24
  • 早晨九點(diǎn)罪既,陽光透過陽臺的飄窗灑滿我的全身铸题,一切靜好。就是在這樣的好天氣琢感,我要與大家談?wù)勔钟舭Y丢间。 一提到“抑郁癥”,...
    桌子老師閱讀 434評論 0 0