目錄概覽:

深入理解HTML語義

讓IE8支持HTML5語義化標籤

視覺格式化模型

深入瞭解inline-block

flexbox剩餘空間分配規則

grids佈局系統

瀏覽器如何渲染HTML & CSS

重排與重繪

一、深入理解 HTML 語義

HTML 語義當然不僅僅只是幾個 HTML 語義標籤。

從“文檔”説起

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

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

只是隨着 Web 的發展,各種酷炫的頁面和應用層出不窮,倒是讓新入行的小夥伴忽略了,HTML 的本質其實是文檔(document)。

現實生活中,説到文檔,我們的第一反應就是 Microsoft Word ,其實 Word 當年也有可能成為 Web 標準,當然這是另外一個故事。

文檔大綱

所以我們從 Word 説開去,希望你 Word 用的很順溜哦。

Word 中的標題有一級標題、二級標題……的説法,所有的標題就構建出了這個 Word 文檔的大綱。下圖的左邊欄就是大綱視圖,可以很好的觀察文檔大綱。

word-outline

在 HTML 文檔中呢,H1 ~ H6 就是一級到六級標題,它們構成了 HTML 文檔的大綱。下面是騰訊網首頁的文檔大綱。一個好的頁面,必定先是一個好文檔,好文檔必然有着嚴謹的文檔大綱。

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

web-outline

再來看“萬維網”

最初的萬維網是一個文檔共享的網絡,現在的萬維網則是一個資源共享的網絡,包括圖片、多媒體等等。

HTML 則是萬維網的粘合劑,也是萬維網的載體,但是現在 HTML 給我們的感覺啊,就是給人看的,其實,HTML 同時也會給機器看,比如下圖,除了 human 在讀 HTML ,各式各樣的機器也在讀 HTML ,比如搜索引擎的爬蟲和讀屏設備。

視覺上的各種酷炫會給人以視覺衝擊,但對機器來説,並沒有什麼用,它們更看重的是語義,這樣才能更好地解析內容。這也是為什麼樣式會從結構裏面分離出來的原因之一。

exploiting-html

現在才到 HTML 語義

本文的開頭就提到了,HTML 語義當然不僅僅只是幾個 HTML 語義標籤。在 HTML 本身這個層面上,語義也有更多的東西,關於這些已經有不少前輩為我們總結好了,參考如下:

二、讓 IE8 支持 HTML5 語義化標籤

HTML5是 HTML 最新的修訂版本,於2014年10月由萬維網聯盟(W3C)完成標準制定。而 IE8 面世時間為2009年3月19日,時間相差如此之大,所以 IE8 作為比較古老的瀏覽器,不支持 HTML 5 引入的語義化標籤(如 header、nav、menu、section、article 等)也是很正常的。

默認情況下 IE8 對 HTML5 標籤的處理

在 IE8 裏面,未定義的標籤——IE8 不認識所有新引入的 HTML5 標籤,所以定義樣式是不會生效。比如下面這段代碼(抽取主要代碼):

section { color: red; }

hello world

期待展示的效果如下:

chrome

但在 IE8 中實際展示效果如下:

IE8

如何讓 IE8 支持 HTML5 標籤

雖然默認不支持,但是我們可以通過 JS 使用 document.createElement 來“欺騙” IE 的 CSS 引擎,讓它知道某個標籤的存在,具體做法如下:

HTML5 test


document.createElement('section');

section { color: red; }

Hello!

顯示效果如下:

IE_HTML5

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

article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary {

display: block;

}

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

其實不只是 IE8 , IE6-9、 Safari 4.x (以及 iPhone 3.x)、還有Firefox 3.x 等等,對 HTML5 的支持都不完善。所以有了一個庫 html5shiv.js 來做統一處理,shiv 意為用作武器的小刀(實際上是一個拼寫錯誤,應該為 shim),在機械工程中的專業釋義為墊片,比喻給那些老舊的瀏覽器加個墊片,讓它們基本能用。

更多閲讀

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

前面我們已經學習了盒模型(box model),知道了元素會被渲染成一個個盒子。那麼這些盒子在屏幕上的位置又是怎麼放置的呢?這就是我們現在要學習的——CSS 視覺格式化模型(visual formatting model)。視覺格式化模型是 CSS 佈局的一個基礎理論體系,需要你有一定的 CSS 功底,所以一時半會是很難掌握的,但是隻要你一掌握,對於 CSS 佈局就會豁然開朗。(建議整個 CSS 佈局學完後再重新細讀深入下。)

盒子的位置擺放

默認情況下,盒子按照元素在 HTML 中的先後位置從左至右自上而下一個接着一個排列擺放。如下圖:

image

在圖中我們可以看到,有些元素的盒子被渲染為完整的一行,如h1、p、div;而有些元素的盒子則被渲染為水平排列,直到該行被佔滿然後換行,如span、a、strong。

這是因為不同的盒子使用的是不同的格式化上下文(formatting context)來佈局,每個格式化上下文都擁有一套不同的渲染規則,它決定了其子元素將如何定位,以及和其他元素的關係和相互作用。(就如我們參加結婚喜宴一樣,有父母長輩席,好友席,同事席,甚至前男/女朋友席等,不同的身份坐到對應位置即可。)

格式化上下文(formatting context)

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

BFC

塊級盒(block-level boxes)

當元素的 CSS 屬性 display 的計算值為 block,list-item,table,flex 或 grid 時,它是塊級元素。視覺上呈現為塊,豎直排列。典型的如



元素等都是塊級元素。

每個塊級元素至少生成一個塊級盒,稱為主要塊級盒(principal block-level box)。一些元素,比如

,生成額外的盒來放置項目符號,不過多數元素只生成一個主要塊級盒。


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

渲染規則

默認根元素(html 元素)會創建一個 BFC,其塊級盒子元素將會按照如下規則進行渲染:

塊級盒會在垂直方向,一個接一個地放置,每個盒子水平佔滿整個容器空間

塊級盒的垂直方向距離由上下 margin 決定,同屬於一個 BFC 中的兩個或以上塊級盒的相接的 margin 會發生重疊

BFC 就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此

計算 BFC 的高度時,浮動元素也參與計算

除此之外,還有其他方法可以創建一個新的 BFC,具體可參看:塊格式化上下文。除此之外,flexbox 佈局和 grids 佈局中的 item 都會創建一個新的 BFC。

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

更多關於 BFC 相關內容可參看:

IFC

行內級盒(inline-level boxes)

當元素的 CSS 屬性 display 的計算值為 inline,inline-block,inline-table,inline-flex 或 inline-grid 時,它是行內級元素。視覺上它將內容與其它行內級元素排列為一行,直到該行被佔滿然後換行。典型的如段落內容,文本或圖片,都是行內級元素。

注:由於目前幾乎所有資料都將行內元素當做行內級元素,所以前面的課程我們也遵循這個美麗的錯誤。嚴格來説,行內元素不包括 inline-block 的,行內級元素才包括。我們要理解其中的區別,知曉這個美麗的錯誤。

行內級元素生成行內級盒,參與行內格式化上下文(inline formatting context),被渲染為水平排列, 直到當行被佔滿然後換行。

行內級盒分為行內盒(inline boxes)和原子行內級盒(atomic inline-level boxes)。前者由非置換元素且 display 值為 inline 的元素生成;後者由行內級置換元素,或 display 值為 inline-block, inline-table, inline-flex, inline-grid 的元素生成。

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

image

渲染規則

當塊容器盒(block container box)不包括任何塊級盒(block-level boxes)時,就會創建一個行內格式化上下文(IFC)。(一般來説一個塊級盒也是一個塊容器盒,具體可參看: Block-level elements and block boxes)

IFC 中的行內級盒將會按照如下規則進行渲染(規則有點多,大概主要點就是行盒,折行機制,水平對齊方式,垂直高度及垂直對齊方式):

盒子一個接一個地水平擺放,當容器寬度不夠時就會換行

在水平方向上,這些盒的外邊距、邊框、內邊距所佔用的空間都會被計算,但行內盒的垂直的border,padding 與 margin 都不會撐開行盒的高度

在垂直方向上,這些盒可能會以不同形式來對齊,可通過 vertical-align 來設置,默認對齊為 baseline

每一行將生成一個行盒(line box),包括該行所有的盒子,行盒的寬度是由包含塊和存在的浮動來決定

行盒一般左右邊都貼緊其包含塊,但是會因為浮動盒(float 元素)的存在而發生變化。浮動盒會位於包含塊邊緣與行盒邊緣之間,這樣行盒的可用寬度就小於包含塊的寬度

當所有盒的總寬度小於行盒的寬度,那麼行盒中的水平方向排版由 text-align 屬性來決定

當所有盒的總寬度超過一個行盒時,就會形成多個行盒,多個行盒相互之間垂直方向不能分離,不能重疊

當一個行內盒超過行盒的寬度時,它會被分割成多個盒,這些盒被分佈在多個行盒裏。如果一個行內盒不能被分割(比如只包含單個字符,或word-breaking機制被禁用,或該行內框受white-space屬性值為nowrap或pre的影響),那麼這個行內盒將溢出這個行盒

當一個行內盒發生分割時,分割處的 margins, borders 和 padding 不會有任何視覺效果(或者其他任何分裂,只要是有多個行盒)

行盒的高度由內部元素中實際高度最高的元素計算出來。每個行盒的高度由於內容不一樣,所以高度也可能不一樣

在一個行盒中,當他包含的內部容器的高度小於行盒的高度的時候,內部容器的垂直位置可由自己的 vertical-align 屬性來確定

注:在 IFC 的環境中,是不能存在塊級元素的,如果將塊級元素插入到 IFC 中,那麼此 IFC 將會被破壞掉變成 BFC,而塊級元素前的元素或文本和塊級元素後的元素或文本將會各自自動產生一個匿名塊盒其包圍。

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

更多關於 IFC 相關內容可參看:

其他格式化上下文

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

表格佈局:可以創建一個表格包裹盒(table wrapper box),包括了表格盒(table box)及任何標題盒(caption boxes)。

多列布局:可以在容器盒與內容之間創建列盒(column boxes)

彈性佈局:將會創建一個彈性容器盒(flex container box)

網格佈局:將會創建一個網格容器盒(grid container box)

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

更多關於盒子的介紹可參看:

定位方案

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

常規流(normal flow):盒一個接一個排列,不同的盒子採用不同的格式化上下文渲染。

浮動(float):盒將從常規流裏提出來,放在當前盒的旁邊。

絕對定位(absolute positioning):盒將脱離常規流,其座標是絕對的(通過 top / bottom / left / right 來設置)。

常規流(normal flow)

默認盒的定位方案就是常規流,但是如果觸發了以下任何一個條件,將不會使用常規流:

position 的值非 static 或 relative

float 的值非 none

在常規流中,不同的盒子將採用不同的格式化上下文渲染,也就是上面所講的部分。

浮動(float)

對於浮動定位方案, 盒稱為浮動盒(floating boxes)。它位於當前行的開頭或末尾。這導致常規流環繞在它的周邊,除非設置 clear 屬性。

要使用浮動定位方案,元素 CSS 屬性 position 必須為 static 或 relative,然後 float 不為 none 。如果 float 設為 left, 則浮動定位到當前位置的開始位置,如果設為 right, 則浮動定位到當前位置的最後位置。

具體介紹可學習下面章節:元素浮動——float。

絕對定位(absolute position)

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

對於絕對定位方案,盒從常規流中被移除,不影響常規流的佈局。 它的定位相對於它的包含塊,定位座標可通過屬性 top、bottom、left、right 來設置 。

固定定位元素(fixed positioned element)也是絕對定位元素,它的包含塊是視口。當頁面滾動時它固定在屏幕上,因為視口沒有移動。

具體介紹可學習下面章節:元素定位——position。

參考資料

四、深入瞭解 inline-block

在之前的課程中,我們學習到了 inline-block 的基礎知識,接下來我將介紹一些使用 inline-block 產生的問題和解決方法以及其常見的應用場景,來進一步加深了大家對 inline-block 的理解。

水平間隙問題

我們創建一個導航列表,並將其列表 item 設置為 inline-block,主要代碼如下:



導航



導航



導航



.nav {

background: #999;

}

.nav-item{

display:inline-block; /* 設置為inline-block */

width: 100px;

background: #ddd;

}

效果圖如下:

image

我們從效果圖中可以看到列表 item 之間有一點小空隙,但是我們在代碼中並沒有設置 margin 水平間距。那麼這個空隙是如何產生的呢?

這是因為我們編寫代碼時輸入空格、換行都會產生空白符。而瀏覽器是不會忽略空白符的,且對於多個連續的空白符瀏覽器會自動將其合併成一個,故產生了所謂的間隙。

對於上面實例,我們在列表 item 元素之間輸入了回車換行以方便閲讀,而這間隙正是這個回車換行產生的空白符。

同樣對於所有的行內元素(inline,inline-block),換行都會產生空白符的間隙。

如何消除空白符

從上面我們瞭解到空白符,是瀏覽器正常的表現行為。但是對於某些場景來説,並不美觀,而且間隙大小非可控,所以我們往往需要去掉這個空白間隙。一般來説我們有兩種方法來去掉這個換行引起間隙:代碼不換行和設置 font-size。

代碼不換行

我們瞭解到,由於換行空格導致產生換行符,因此我們可以將上述例子中的列表 item 寫成一行,這樣空白符便消失,間隙就不復存在了。其代碼如下:



導航



導航



導航



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

設置 font-size

首先要理解空白符歸根結底是個字符,因此,我們可以通過設置 font-size 屬性來控制產生的間隙的大小。我們知道如果將 font-size 設置為 0,文字字符是沒法顯示的,那麼同樣這個空白字也沒了,間隙也就沒了。

於是順着這個思路就有了另一個解決方案:通過設置父元素的 font-size 為 0 來去掉這個間隙,然後重置子元素的 font-size,讓其恢復子元素文字字符。

所以該方法代碼如下:

.nav {

background: #999;

font-size: 0; /* 空白字符大小為0 */

}

.nav-item{

display:inline-block;

width: 100px;

font-size: 16px; /* 重置 font-size 為16px*/

background: #ddd;

}

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

對齊問題

由於 inline-block 屬於行內級元素,所以 vertical-align 屬性同樣對其適用。

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

中線、基線、頂線、底線

中線(middle)、基線(baseline)、頂線(text-top、底線(text-bottom))是文本的幾個基本線,其對應位置如下圖:

image

基線(base line):小寫英文字母x的下端沿。

中線(middle line):小寫英文字母x的中間。

頂線(text-top):父元素 font-size 大小所組成的一個內容區域的頂部

底線(text-bottom):父元素 font-size 大小所組成的一個內容區域的底部

vertical-align 的值

vertical-align 只接受8個關鍵字、一個百分數值或者一個長度值。下面我們將看看各關鍵字如何作用於行內元素。

描述

baseline

默認元素的基線與父元素的基線對齊。

sub

將元素的基線與其父元素的下標基線對齊。

super

將元素的基線與其父代的上標 - 基線對齊。

text-top

將元素的頂部與父元素的字體頂部對齊。

text-bottom

將元素的底部與父元素的字體的底部對齊。

middle

將元素的中間與基線對齊加上父元素的x-height的一半。

top

將元素的頂部和其後代與整行的頂部對齊。

bottom

將元素的底部和其後代與整行的底部對齊。

將元素的基線對準給定長度高於其父元素的基線。

像值,百分比是line-height屬性的百分比。

具體 demo 可參考:行內級元素垂直對齊方式

五、清除浮動詳解

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

清除前面兄弟元素浮動

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



我是左浮動元素



我是右浮動元素



我不受浮動元素的影響



.fl {

float: left;

}

.fr {

float: right;

}

.cb {

clear: both;

}

在 CSS2 以前,clear 的原理為自動增加元素的上外邊距(margin-top)值,使之最後落在浮動元素的下面。在 CSS2.1 中引入了一個清除區域(clearance)——在元素上外邊距之上增加的額外間距,使之最後落在浮動元素的下面。所以如果需要設置浮動元素與 clear 元素的間距,得設置浮動的元素的 margin-bottom,而不是 clear 元素的 margin-top。

閉合子元素浮動

我們知道,在計算頁面排版的時候,如果沒有設置父元素的高度,那麼該父元素的高度是由他的子元素高度撐開的。但是如果子元素是設置了浮動,脱離了文檔流,那麼父元素計算高度的時候就會忽略該子元素,甚至當所有子元素都是浮動的時候,就會出現父元素高度為 0 的情況,這就是所謂的父元素高度坍塌問題。為了能讓父元素正確包裹子元素的高度,不發生坍塌,我們需要閉合子元素的浮動。

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

給最後一個元素設置 clear: both

給父元素新建一個 BFC(塊格式化上下文)

clear:both

由於我們最後一個元素使用 clear:both,所以該元素就能不受浮動元素影響出現在父元素的最底部,而父元素計算高度的時候需要考慮到這個正常元素的位置,所以高度自然包裹到了最底部,也就沒有了坍塌。

對於這個方法,以前我們是利用新增一個空元素( 或 或



等)來實現的,如下:

.box {



float: left;



}



.clear-box {



clear: both;



}



雖然這種辦法比較直觀,但是不是很優雅,因為增加了一個無用的空白標籤,比較冗餘而且不方便後期維護(一般不太建議使用該辦法)。所以後期有了通過父元素的偽元素(::after)實現的著名 clearfix 方法,代碼如下:



.clearfix::after {



content:"";



display:table;



clear: both;



}



上面方法給父元素增加一個專門用於處理閉合子元素浮動的 clearfix 類名,該類使用 ::after 偽元素類選擇器增加一個內容為空的結構來清除浮動,可能你們比較疑惑的是為什麼要設置 display:table 屬性,這其實涉及到一個比較複雜的進化過程,具體可以參考資料——clearfix浮動進化史



新建 BFC



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



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





image





image



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



根元素或其它包含它的元素



浮動 (元素的 float 不是 none)



絕對定位的元素 (元素具有 position 為 absolute 或 fixed)



內聯塊 inline-blocks (元素具有 display: inline-block)



表格單元格 (元素具有 display: table-cell,HTML表格單元格默認屬性)



表格標題 (元素具有 display: table-caption, HTML表格標題默認屬性)



塊元素具有overflow ,且值不是 visible



display: flow-root



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



.container {



overflow: hidden;



}



.box {



float: left;



}



總結



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



六、深入瞭解 z-index



網頁正常文檔流排版可以理解為在一個平面立面裏面,元素佔據空間,依次排列,互不覆蓋。但是當頁面中元素設置了定位屬性的時候,難免會出現元素之間相互重疊的情況,比如下圖小貓和小狗的圖片都設置了絕對定位,2張圖片的位置重疊了,小貓圖顯示在小狗圖上面。現在如果我們想要小狗圖顯示在上面,就需要涉及到 CSS 中的 z-index 屬性了。





z-index



z-index



z-index 屬性用於指定已定位元素在垂直於頁面方向的排列順序,其屬性值有2種:auto(默認值)和整數。這裏有2個需要注意的點:



z-index 屬性只對定位元素元素生效,也就是 position 屬性不為 static 的元素。



除了默認值 auto, z-index 可以設置為任意整數,正數,0,負數都可以。



一般情況下,z-index 值進行比較有下面2條規則:



數值大的在上面(auto 數值上相當於0)。



數值相同的,在 HTML 結構中排後面的在上面。



所以上面例子,2張圖都沒設置 z-index 的值,在 dom 結構上排後面的小貓圖展示在上面。如果想小狗圖展示在小貓圖上面,只需要設置小狗圖的 z-index 屬性值大於0就可以了。



elementui中數字區間編輯控件_html定義盒子距離頂底端像素



elementui中數字區間編輯控件_基線_02



.dog {



position: absolute;



top: 10px;



left: 100px;



z-index: 1; /* 設置小狗圖的 z-index 值大於0 */



}



.cat {



position: absolute;



top: 80px;



left: 70px;



}



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





z-index



層疊上下文



上面説到,z-index 默認值 auto 數值上等於0,那設置了 z-index:0 和 默認的 z-index:auto; 有沒有區別呢? 答案是有區別的。區別在於設置了 z-index 屬性為整數值(包括0)的元素,自身會創建一個層疊上下文。而創建一個層疊上下文之後,其子元素的層疊順序就相對於父元素計算,不會與外部元素比較。這樣説比較抽象,我們來看個例子。



elementui中數字區間編輯控件_html定義盒子距離頂底端像素



elementui中數字區間編輯控件_基線_02



img {



width: 200px;



}



.dog-container {



width: 200px;



height: 100px;



background: red;



position: relative;



z-index: auto; /* 默認值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 都設置了 z-index 值,所以都顯示在紅色背景的 .container 之上,而且 .dog z-index 數值比較大,所以顯示在上面。





z-index



但是當我們設置了 .dog-container 的 z-index 屬性值為0之後,我們發現,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 設置了 z-index:0 之後,.dog-container 就創建了自己的層疊上下文,其子元素 .dog 在比較層疊順序的時候只會在 .dog-container 內比較,而不會與外面的 .cat 比較。如下圖所示:





z-index



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



根元素 (HTML)



z-index 值不為 "auto"的 絕對/相對定位



一個 z-index 值不為 "auto"的 flex 項目 (flex item),即:父元素 display: flex|inline-flex



opacity 屬性值小於 1 的元素



transform 屬性值不為 "none"的元素,



mix-blend-mode 屬性值不為 "normal"的元素,



filter值不為“none”的元素,



perspective值不為“none”的元素,



isolation 屬性被設置為 "isolate"的元素,



position: fixed



在 will-change 中指定了任意 CSS 屬性,即便你沒有直接指定這些屬性的值(參考這篇文章)



-webkit-overflow-scrolling 屬性被設置 "touch"的元素



也就是説,上面例子中, .dog-container 滿足上面任意一條屬性,也會一樣出現上面的情況。比如設置 opacity 屬性:



.dog-container {



width: 200px;



height: 100px;



background: red;



position: relative;



opacity: 0.6; /* 設置 opacity 屬性小於1也會創建層疊上下文 */



}



效果如下:





z-index



總結



z-index 屬性用於描述定位元素在垂直於頁面方向上的排列順序。



z-index 一般比較規則是值大在上,值相同則排後面的在上。



元素在設置了某些屬性的時候會創建層疊上下文,z-index 值比較大小隻有在同一個層疊上下文才有效。



參考文檔



七、flexbox 剩餘空間分配規則



前面我們學習到了 flexbox 佈局。通過使用 flexbox 佈局,我們可以更輕鬆實現以往很難實現的頁面佈局。本文主要講解 flexbox 佈局是如何去分配和計算佈局剩餘空間的。(本文閲讀前要求你對 flexbox 已經有了初步的認知,如果不是很瞭解,建議先學習前面視頻內容。)



基礎概念



為了更好地理解本文內容,我們需要先了解下面一些基礎概念。



flexbox 容器 (flexbox container)



flexbox 容器又稱彈性容器,通過設置 display: flex 而產生,簡單示例如下:



.container {



display: flex; /* 或者 inline-flex */



}



flexbox 項目 (flexbox item)



當設置一個元素為 flexbox 容器時,其直接子元素將自動成為容器成員,也可以稱之為:flexbox 項目。



注:因為 flexbox 佈局是發生在父元素和子元素之間的,所以下面為了行文方便,統一將 flexbox 容器稱為“父容器”,而 flexbox 項目統一稱為“子元素”



剩餘空間



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





image



item1



item2



上面代碼,我們定義了一個寬度為600px的父容器 container,以及寬度為200px的子元素 item1 和 item2 ,那麼我們得出其剩餘空間為200px,計算公式為:



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



剩餘空間分配相關屬性



flexbox 佈局中的子元素可以通過設置 flex 屬性來改變其所分配到的空間大小。flex 屬性包括了 flex-basis、 flex-grow、flex-shrink



flex-basis



flex-basis 用來定義子元素的默認寬或高。如果父容器 flex-direction 屬性的方向為水平方向則為寬度,如為垂直方向則為高度。相當於給子元素設置寬或高。如果同時設置了該屬性與寬或高,則該屬性權重大於寬或高的值。



flex-grow



flex-grow 用來指定父容器多餘空間的分配比率,默認值為0。看到這裏,大家可能還是沒有概念。為了更形象地理解,我們一起看下下面的例子。



例子: 只設置 item1 的 flex-grow 為1





image



其 HTML 代碼如下:



item1



item2



其 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 設置flex-grow:1 後,我們可以看到 item1 的所佔空間寬度變成400px,也就是説 item1 把之前我們所説的父容器剩餘的200px空間都佔用了。



在這裏我們可以得出:其實 flex-grow 即定義如何去分配父容器的剩餘空間 ,當值為0時,則子元素都不會索取父容器的剩餘空間。當 item1 設置 flex-grow: 1 的時候,由於 item2 沒有設置 flex-grow 的值,則剩餘空間將會被分成一份,並且分別分給了 item1。



例子: 設置 item1 的 flex-grow 為1,且 item2 的 flex-grow 為3



如果此時我們設置 item2 的flex-grow:3 ,item2 也將會參與索取父容器的剩餘空間,此時父容器的剩餘空間將分為4份,然後1份分配到 item1,而分配3份到 item2,如下圖:





image



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



上面的例子,我們只考慮了子元素的寬度總和都是沒有超過父容器的寬度的情況,則其可以使用 flex-grow 來分配父容器的剩餘空間。那麼當子元素的寬度總和超過父容器的寬度時,這時剩餘空間還可以分配嗎?此時 flex-grow 是否還有效呢?讓我們先看看下面的例子:



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





image



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



通過之前學到的如何計算剩餘空間的方法,我們可以算出本例子的剩餘空間為600px - 700px即 -100px,這裏可以得出由於沒有剩餘空間,則定義了 flex-grow 的子元素能分配到的空間為0,故不生效。另外我們需要知道的是 flexbox 環境的父容器的寬度600px並不會因為子元素的總寬而改變,即子元素的寬度總和最多等於父容器的寬度,所以為了讓子元素完整顯示在父容器內,只有兩個辦法:



通過設置 flex-wrap 來使子元素換行



通過壓縮子元素來使其能容納在父容器內



flex-shrink



flex-shrink 用來指定父容器空間不夠時子元素的縮小比例,默認為1。如果一個 flexbox 項目的 flex-shrink 屬性為0,則該元素不會被壓縮。



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



上面我們可以知道,當子元素的寬度總和大於 flexbox 父容器的寬度時,其剩餘空間將為負數,如果沒有設置換行的情況下,其將會通過壓縮子元素來使其能夠容納在父容器內。那麼我們如何控制子元素的壓縮比例呢?答案就是通過將通過設置 flex-shrink 這個屬性。



例子:設置項目的flex-shrink



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





image



代碼如下:



/* 這裏只展示關鍵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。



例子:設置項目的 flex-shrink 為0



在上面的知識中,我們瞭解到 flex-shrink 默認值為1,即默認子元素在父容器空間不足時會被壓縮。現在我們把項目的 flex-shrink 設為0來看下不壓縮的情況。如下所示:



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



代碼如下:



/* 這裏只展示關鍵css */



.container {



display: flex;



width: 600px;



height: 140px;



}



.item {



width: 350px;



height: 120px;



flex-shrink: 0;



}



更多閲讀



八、grids 佈局系統



我們之前有提到過網格系統,比如960s,bootstrap 的網格系統,但是這些網格系統都是模擬出來的(使用 float 或 flexbox),而並非天生的,雖然可以解決一些常見佈局問題,但面臨 Win10 UI 還是有點力所不及,如下圖:





image



但是隨着 CSS 的不斷髮展及完善,一種新的網格佈局方式被納入規範,它將會解決所有的網格問題,這就是我們要説的 CSS Grid Layout。



網格系統基礎概念



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





excel css gri layout



以我們的 Sheet1 的 A1 單元格為例,先是有上下左右四條線圍着,然後定位是由豎直的 A 欄與橫向的1行二維座標表示 A1。如果有需要甚至還還可以和鄰近的單元格合併。



現在我們提煉下上面的幾個概念:線條,欄(豎直),行(橫向),單元格,合併。接下來我們把這些概念對應到我們的網格系統:





CSS Grid Layout



Grid Container:首先我們要設置父元素的佈局為 grid,通過使用 display 屬性給元素顯式設置屬性值grid或inline-grid,此時這個元素將自動變成網格容器,對應上圖的Sheet1



Grid Item:Item 是 container 的直接子元素,如果不考慮單元格的合併跟下面的 cell 是一樣的,如果有單元格合併,在該 item 可能包括幾個cell,對應上圖的一個個格子,如藍色的 A1



Grid Lines:網格線分為水平線和垂直線,對應上圖的橙色線條



Grid Track:就是由lines構成的水平和垂直空間,對應到上圖的水平和垂直灰色區域,而對於table來説就是row和column



Grid Cell:簡單來説就是單元格了,對應到上圖就是藍色的A1,而對於table來説就是單元格



Grid Area:網格區域是由任意四條網格線組成的空間,可能由一個或多個單元格組成。對應到上圖就是紅色區域,相當於表格中的合併單元格之後的區域



網格系統基本屬性



網格系統佈局其實跟 flexbox 佈局差不多,都是由父子元素構成的佈局。所以屬性分為父元素屬性和子元素屬性。



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



父元素(Grid Container)屬性



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



第一類:如何去定義一個網格系統,行列及間距等。



display:grid/inline-grid,定義使用網格系統



grid-template-columns:定義垂直欄



grid-template-rows:定義水平行



grid-template-areas:定義區域



grid-column-gap:定義垂直欄與垂直欄之間的間距,如上圖的A與B之間的間距



grid-row-gap:定義水平行與水平行之間的間距,如上圖的1與2之間的間距



grid-gap:上面兩個欄與行間距的縮寫



第二類:自動分配形式,當定義的行或列數量不夠時,多出 item 的自動排列方式:



grid-auto-columns:定義多出的 item 的自動column的寬度大小



grid-auto-rows:定義多出的 item 自動 row 的高度大小



grid-auto-flow:定義自動 item 是按照先水平方向排列還是垂直方向排列



第三類:分佈對齊的方式(屬性跟 flexbox 的有點像)。



justify-items:item 在水平行中的對齊方式



align-items:item 在垂直欄中的對齊方式



justify-content:整個水平行在 grid 範圍的對齊方式,這裏有個好用的 space-evenly 值,補足了以前flex的 space-around 和 space-between 的不足



align-content:整個垂直欄在 grid 範圍的對齊方式



子元素(Grid Item)屬性



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



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



grid-column-start:item 的起始欄



grid-column-end:item 的結束欄



grid-column:起始欄和結束欄的簡寫



grid-row-start:item 的起始行



grid-row-end:item 的結束行



grid-row:起始行與結束行的簡寫



grid-area:item所在區域



第二類:單元格的自定義對齊方式,這個跟 flexbox 的 item 有點相似。



justify-self:自定義 item 的水平方向對齊方式



align-self:自定義 item 的垂直方向對齊方式



實例演示



百説不如一練,我們接下來使用網格系統來實戰下我們的 Win10 UI,如下圖:





grids demo



)



html結構為:



1



2



3



4



5



6



7



8



9



10



11



寫好結構後,我們就開始使用剛才説得 grid 來實現我們的效果了。先拆分成最小的單元格為 6欄 * 3行,最小單元格的大小為140px,整體內容一屏水平垂直居中。



html,body {



height: 100%;



}



.grid {



height: 100%;



display: grid; /* 網格佈局 */



/* 整體水平垂直居中 */



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;



}



現在我們可以看到效果如下:





image



接下來要合併單元格實現我們的最終效果。合併單元格有兩種實現方式一種是 line 的開始與結束(包括 colunm 與 row),另一種就是在 grid 上面定義的 area,這裏我們使用第一種方法。



這裏重提下上面的 Grid Lines 概念,如要實現 n欄 * m行的網格,則需要n+1條垂直line,m+1條水平線。



第一個 item 元素單元格佔了兩列,第一列和第二列,那麼就垂直欄開始於第一條 line,結束於第三條 line,同樣第5個 item 元素也是如此



.item:nth-child(1),



.item:nth-child(5) {



grid-column: 1 / 3; /* 起始於1,結束於3 */



}



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



.item:nth-child(2) {



grid-column: 3 / 5; /* column起始於3,結束於5 */



grid-row: 1 / 3; /* row起始於1,結束於3 */



}



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



.item:nth-child(7) {



grid-column: 6;



grid-row: 2 / 4; /* row起始於2,結束於4 */



}



.item:nth-child(9) {



grid-column: 2 / 4; /* column起始於2,結束於4 */



}



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



瀏覽器支持



現代瀏覽器最新版本基本上都支持了 CSS Grid Layout,下圖是 caniuse 上的支持情況:





caniuse



有些瀏覽器舊版本的已經實現但是沒有默認開啓(chrome < 57,firefox < 52)則需要通過下面的方式手動設置開啓:



chrome 在地址欄輸入“chrome://flags”,找到"experimental web platform features"開啓



firefox在地址欄輸入"about:config",找到"layout.css.grid.enabled"開啓



總結



上面只是 grid 佈局的簡單使用,實際上 grid 佈局十分強大,使用起來也十分方便,未來將會是佈局的主力軍,但是目前由於兼容問題暫時不建議在生產環境中使用,不過我們相信很快就可以見識到 grid 佈局的強大威力了。



參考資料



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



我們現在已經知道,使用 HTML & CSS 可以搭建出一個漂亮的 Web 頁面。



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



雖然具體渲染過程很複雜,但是還是可以將其分為幾個關鍵路徑,如下:



處理 HTML 標記並構建 DOM 樹



處理 CSS 標記並構建 CSSOM 樹



將 DOM 與 CSSOM 合併成一個渲染樹



根據渲染樹來佈局,以計算每個節點的幾何信息,再將各個節點繪製到屏幕上



構建 DOM 樹



首先瀏覽器渲染頁面前會根據 HTML 結構構建成對應的 DOM 樹。



以下面的 HTML 代碼為例:


Critical Path


Hello web performance students!





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



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



轉換: 瀏覽器從磁盤或網絡讀取 HTML 的原始字節,並根據文件的指定編碼(例如 UTF-8)將它們轉換成各個字符。



令牌化: 瀏覽器將字符串轉換成 W3C HTML5 標準規定的各種令牌,例如,“”、“”,以及其他尖括號內的字符串。每個令牌都具有特殊含義和一組規則。



詞法分析: 發出的令牌轉換成定義其屬性和規則的“對象”。



DOM 構建: 最後,由於 HTML 標記定義不同標記之間的關係(一些標記包含在其他標記內),創建的對象鏈接在一個樹數據結構內,此結構也會捕獲原始標記中定義的父項-子項關係:HTML 對象是 body 對象的父項,body 是 paragraph 對象的父項,依此類推。



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



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



CSSOM



在瀏覽器構建上面的 DOM 時,在文檔的 head 部分遇到了一個 link 標記,該標記引用一個外部 CSS 樣式表:style.css。由於預見到需要利用該資源來渲染頁面,它會立即發出對該資源的請求,並返回以下內容:



/* style.css */



body { font-size: 16px }



p { font-weight: bold }



span { color: red }



p span { display: none }



img { float: right }



與處理 HTML 時一樣,我們需要將收到的 CSS 規則轉換成某種瀏覽器能夠理解和處理的東西。因此,我們會重複 HTML 過程,不過是為 CSS 而不是 HTML:





image



CSS 字節轉換成字符,接着轉換成令牌和節點,最後掛靠到一個稱為“CSS 對象模型”(CSSOM) 的樹結構內:



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



CSSOM 為何具有樹結構?這是因為瀏覽器為頁面上的任何對象計算最後一組樣式時,都會先從適用於該節點的最通用規則開始(例如,如果該節點是 body 元素的子項,則應用所有 body 樣式),然後通過應用更具體的規則(即規則“向下級聯”)以遞歸方式優化計算的樣式。



以上面的 CSSOM 樹為例進行更具體的闡述。span 標記內包含的任何置於 body 元素內的文本都將具有 16 像素字號,並且顏色為紅色 — font-size 指令從 body 向下級聯至 span。不過,如果某個 span 標記是某個段落 (p) 標記的子項,則其內容將不會顯示。



合併渲染樹



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



渲染樹會網羅網頁上所有可見的 DOM 內容,以及每個節點的所有 CSSOM 樣式信息。



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



注:渲染樹只包含渲染網頁所需的節點,如display: none;的元素是不會出現在渲染樹種的。



佈局及繪製



有了渲染樹,我們就可以進入“佈局”階段。



到目前為止,我們計算了哪些節點應該是可見的以及它們的計算樣式,但我們尚未計算它們在設備視口內的確切位置和大小---這就是“佈局”階段,也稱為“自動重排”。



為弄清每個對象在網頁上的確切大小和位置,瀏覽器從渲染樹的根節點開始進行遍歷。讓我們考慮下面這樣一個簡單的實例:


Critial Path: Hello world!


Hello world!



以上網頁的正文包含兩個嵌套 div:第一個(父)div 將節點的顯示尺寸設置為視口寬度的 50%;而裏面內嵌的第二個 div 將其寬度設置為其父項的 50%,即視口寬度的 25%。如下圖:





image



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



最後,既然我們知道了哪些節點可見、它們的計算樣式以及幾何信息,我們終於可以將這些信息傳遞給最後一個階段:將渲染樹中的每個節點轉換成屏幕上的實際像素形成我們可見的頁面。這一步通常稱為“繪製”或“柵格化”。



注:執行渲染樹構建、佈局和繪製所需的時間將取決於文檔大小、應用的樣式,以及運行文檔的設備:文檔越大,瀏覽器需要完成的工作就越多;樣式越複雜,繪製需要的時間就越長(例如,單色的繪製開銷“較小”,而陰影的計算和渲染開銷則要“大得多”)。



參考資料



十、重排與重繪



一個頁面渲染完畢後,隨着用户的操作,或者數據變化,網頁還會進行重新渲染。根據不同的觸發條件,重新渲染分為兩種情況:重排(reflow)和重繪(repaint)。



所有對元素視覺表現屬性的修改,都會導致重繪(repaint)。比如修改了背景顏色、文字顏色等。



所有會觸發元素佈局發生變化的修改,都會導致重排(reflow)。比如窗口尺寸發生變化,刪除、添加 DOM 元素,修改了影響元素盒子大小的 CSS 屬性如 width、 height、 padding 等。



通常情況下,重排的影響更大,重排會導致文檔局部或全部的重新運算:重新計算元素位置,大小。(改變一個元素的佈局,可能會影響很多個元素的佈局)



不管是重繪還是重排導致的重新渲染,都會阻塞瀏覽器。重新渲染的的過程中,JavaScript 會被阻塞,用户的交互行為也會被卡住。複雜的 CSS 動畫甚至會拖慢 JavaScript 的運行速度。



注:本文涉及到的 JavaScript 部分,可以先忽略,等以後學習了 JavaScript 再來查看。



導致重排和重繪的場景



CSS 屬性改變



網站 CSS trigglers 列出了所有 CSS 屬性對 layout (佈局)、paint (繪製)的影響。通過這個表,可以查到不同內核下,對 CSS 屬性的修改會導致重繪、重排還是兩者都會發生。





re-render



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



對 CSS 屬性進行修改,包括但不限於以下場景:



通過 display: none 隱藏 DOM 節點(導致重繪和重排)



通過 visibility: hidden 隱藏 DOM 節點 (導致重繪,因為它沒有影響其它元素位置佈局)



CSS 動畫



通過 JavaScript 添加樣式,修改樣式



用户交互



對瀏覽器窗口進行縮放操作會導致重排



對 DOM 節點進行操作,添加、刪除、更新 DOM 節點都會導致重排



光標 :hover 、進入文本輸入框、修改瀏覽器的字體都會導致重排



最佳實踐



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



CSS 屬性的讀、寫操作分開



瀏覽對 CSS 屬性的連續修改做了優化,比如下面的連續修改兩次 style,不會導致兩次重新渲染:



div.style.color = 'blue';



div.style.marginTop = '30px';



上面代碼只會進行一次重新渲染。但是如果寫的不好,則會觸發兩次重新渲染,如下:



div.style.color = 'blue';



var margin = parseInt(div.style.marginTop);



div.style.marginTop = (margin + 10) + 'px';



上面代碼對 div 元素設置背景色以後,第二行要求瀏覽器給出該元素的位置,所以瀏覽器不得不重新渲染然後得到該元素的位置。



除此之外,樣式的寫操作之後,如果有下面這些屬性的讀操作,都會引發瀏覽器立即重新渲染:



offsetTop/offsetLeft/offsetWidth/offsetHeight



scrollTop/scrollLeft/scrollWidth/scrollHeight



clientTop/clientLeft/clientWidth/clientHeight



getComputedStyle()



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



上面對通過對 style 對 CSS 屬性一個一個修改,其實更好的方式應該是通過 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,而不是真實的網頁 DOM 來改變元素樣式。比如,操作 Document Fragment對象,完成後再把這個對象加入 DOM。再比如,使用 cloneNode() 方法,在克隆的節點上進行操作,然後再用克隆的節點替換原始節點。



使用 display: none 進行樣式批量更新:先將元素設為 display: none(需要1次重排和重繪),然後對這個節點進行100次操作,最後再恢復顯示(需要1次重排和重繪)。這樣一來,你就用兩次重新渲染,取代了可能高達100次的重新渲染。



善用 position:position 屬性為 absolute 或 fixed 的元素,重排的開銷會比較小,因為不用考慮它對其他元素的影響。



將元素設置為不可見:只在必要的時候,才將元素的 display 屬性為可見,因為不可見的元素不影響重排和重繪。另外,visibility : hidden 的元素只對重繪有影響,不影響重排。



減少樣式的更新頻率:使用虛擬 DOM 腳本庫,比如 React 等。



調節 js 運行幀率:使用 window.requestAnimationFrame()、window.requestIdleCallback() 這兩個方法調節重新渲染的頻率。



慎用 table 佈局:table 的單元格具有非常好的自適應特性,但是同時代價也很高,能不用就不用。如果非要使用 table ,給 table 添加 table-layout: fixed 屬性,這個屬性的目的是讓後面單元格的寬度由表頭的寬度來決定——減少佈局的計算量。