大家還記得我們之前介紹過的CSS_Flex 那些鮮為人知的內(nèi)幕,在文章中我們不是對API
的羅列,而是從內(nèi)部原理方向來解析Flex
中我們常見的屬性和使用方式。該篇文章也得到大家的一致好評。
而今天,我們來講講我們平時可能會忽略,但是在一些應(yīng)用場景中能讓我們得心應(yīng)手
的另外的布局方式 - Grid
。
還是和上一篇Flex
文章一樣,我們不是對Grid
的API進行羅列,而是從更深層次的角度來了解Grid
。也就是意味著,本篇文章需要一定的Grid
的基礎(chǔ)知識。如果大家還不了解,可以翻看阮一峰老師寫的CSS Grid 網(wǎng)格布局教程[1]
好了,天不早了,干點正事哇。
?
Gird
是個啥Grid
是重要的布局算法之一- 開啟
Grid
布局- 創(chuàng)建網(wǎng)格單元
- 分配子項
- 對齊方式
?
網(wǎng)格布局(Grid
)將網(wǎng)頁劃分成一個個網(wǎng)格,可以任意組合不同的網(wǎng)格,做出各種各樣的布局。
上圖這樣的布局,就是 Grid
布局的拿手好戲。
Grid
布局與 Flex
布局有一定的相似性,都可以指定「容器」內(nèi)部多個「項目」的位置。但是,它們也存在重大區(qū)別。
Flex
布局是「軸線布局」,只能指定項目
針對軸線的位置,可以看作是「一維布局」。Grid
布局則是將容器
劃分成行
和列
,產(chǎn)生單元格,然后指定項目
所在的單元格,可以看作是「二維布局」。?
Grid
布局遠比Flex
布局強大。?
容器
是應(yīng)用了 display: grid
樣式的元素。它是所有網(wǎng)格項的「直接父元素」。
<div class="container"> <div class="item item-1"> </div> <div class="item item-2"> </div> <div class="item item-3"> </div></div>
在這個例子中,.container
所對應(yīng)的元素就是就是容器
。
項目
是網(wǎng)格容器的子元素(即「直接后代」)。
<div class="container"> <div class="item"> </div> <div class="item"> <p class="sub-item"> </p> </div> <div class="item"> </div></div>
在這個例子中,item
元素是項目
,但 sub-item
不是。
?
網(wǎng)格線
是構(gòu)成網(wǎng)格結(jié)構(gòu)的分割線
。它們可以是垂直的(列網(wǎng)格線
)或水平的(行網(wǎng)格線
),并位于行或列的兩側(cè)。?
在這里,黃色線是列網(wǎng)格線
的一個例子。
網(wǎng)格單元
是兩個相鄰的行網(wǎng)格線
和兩個相鄰的列網(wǎng)格線
之間的空間。它是網(wǎng)格的單個「單位」。
在這個例子中,這是位于行網(wǎng)格線 1 和 2 之間,以及列網(wǎng)格線 2 和 3 之間的網(wǎng)格單元。
?
軌道
是兩個相鄰網(wǎng)格線之間的空間。?
我們可以將它們看作是網(wǎng)格的列或行。
在這個例子中,這是第二行網(wǎng)格線和第三行網(wǎng)格線之間的
軌道
。
?
網(wǎng)格區(qū)域
是由四條網(wǎng)格線圍成的總空間。?
一個網(wǎng)格區(qū)域可能由「任意數(shù)量的網(wǎng)格單元組成」。
在這個例子中,這是位于行網(wǎng)格線 1 和 3 之間,以及列網(wǎng)格線 1 和 3 之間的網(wǎng)格區(qū)域。
根據(jù) caniuse[2],Grid
支持 97.78%
的用戶。
在我們構(gòu)建復雜頁面時,就會用到各種各樣的布局算法,每種算法用于不同類型的用戶界面。如下圖:
Flexbox
設(shè)計用于沿單個軸分配項目,這個我們在CSS_Flex 那些鮮為人知的內(nèi)幕有過介紹Grid
是我們今天的主角脫離文檔流
的元素文本環(huán)繞
的布局相比,我們比較熟悉的布局算法(flaot/position/table
等)Grid
是最新最強大的布局算法。grid
是2017年才發(fā)布的。
?
Grid
最令人神往的地方就是它的網(wǎng)格結(jié)構(gòu)
,即行和列,具體表現(xiàn)就是這些頁面布局只需在CSS
中定義即可。?
下面的頁面結(jié)構(gòu)是我們常見的「圣杯布局」
<header></header><nav></nav><main></main><footer></footer>
使用 Grid
來實現(xiàn)該布局,我們只需要在CSS
中劃分好具體哪個元素所占的區(qū)域即可。(這里我們就不貼代碼了)
而在其他任何布局模式中,創(chuàng)建這樣的區(qū)塊的唯一方法就是「添加更多的 DOM 節(jié)點」。例如,在表格布局
中,每行都是用 <tr>
創(chuàng)建的,每個行中的單元格則使用 <td>
或 <th>
:
<table> <tbody> <!-- 第一行 --> <tr> <!-- 第一行中的單元格 --> <td></td> <td></td> <td></td> </tr> <!-- 第二行 --> <tr> <!-- 第二行中的單元格 --> <td></td> <td></td> <td></td> </tr> </tbody></table>
?
與其他布局不同,
Grid
允許我們完全在CSS
中管理布局。我們可以將容器切成任意形狀,然后將子元素和這些區(qū)塊對應(yīng)即可。?
我們通過 display
屬性選擇啟用網(wǎng)格布局模式:
.container { display: grid | inline-grid;}
grid
– 生成塊級網(wǎng)格inline-grid
– 生成內(nèi)聯(lián)級網(wǎng)格?
默認情況下,
Grid
使用「單列」,并根據(jù)子元素的數(shù)量動態(tài)創(chuàng)建行。這被稱為「隱式網(wǎng)格」,因為我們沒有明確定義任何結(jié)構(gòu)。?
隱式網(wǎng)格
是動態(tài)的;根據(jù)子元素的數(shù)量將添加和刪除行。每個子元素都有自己的行。
?
默認情況下,網(wǎng)格容器的高度由其子元素確定。
?
它會動態(tài)增長和收縮。其實,網(wǎng)格容器仍然使用流式布局
,而流式布局中的塊級元素會垂直增長以容納其內(nèi)容。「只有子元素使用網(wǎng)格布局進行排列」。
當我們將容器的高度固定后,在這種情況下,其內(nèi)部項目的高度會「均分」容器高度。也就是當擁有多個項目時它們被分成大小相同的行。
默認情況下,Grid
將創(chuàng)建單列布局。我們可以使用grid-template-columns[7]屬性指定列:
通過將兩個值傳遞給grid-template-columns
—— 25%
和75%
—— 告訴Grid
算法將元素分成兩列。
列
可以使用任何有效的CSS <length-percentage>
值定義,包括像素
、rems
、視口單位
等。此外,我們還可以使用新的單位,即fr單位[8]:
這里多說一句,在CSS Values and Units Module Level 4[9]中定義了關(guān)于length
的值
這里的fr
代表分數(shù)
(fraction
)。在這個示例中,我們說第一列應(yīng)該占用1個單位的空間,而第二列占用3個單位的空間。這意味著總共有4個單位的空間,這成為分母。第一列占據(jù)了可用空間的1/4
,而第二列占據(jù)了3/4
。
fr
單位為Grid
帶來了類似Flexbox
樣式的靈活性。百分比
和 <length>
值會創(chuàng)建硬約束,而fr
列可以「根據(jù)需要自由地增長和收縮,以容納其內(nèi)容」。
仔細觀看下面的例子,Grid
的項目一個用了fr
一個用了%
。此時我們?yōu)榈谝涣械念^像賦予了一個指定寬度的圖像。隨著容器寬度發(fā)生變化,當容器寬度小到一定程度,即第一列的寬度小于圖像的設(shè)定寬度時,就會發(fā)生如下的變化。
百分比
的列的寬度大小會按照容器寬度*N%
變化,當列寬度小于圖像寬度時,圖像從列中溢出。fr
單位的列無論如何縮小容器寬度,該列也不會收縮到其最小內(nèi)容大小以下。?
更準確地說:
fr
單位分配額外的空間。首先,列寬將根據(jù)其內(nèi)容計算。如果有剩余空間,它將根據(jù)fr
值進行分配。該特性和flex-grow
是一致的。?
我們再來用一個例子來說明fr
和%
的區(qū)別。此時我們用gap
來設(shè)置所有列和行之間添加了固定量的空間
看看在%
和fr
之間切換時會發(fā)生什么:
當使用基于%
的列時,內(nèi)容會溢出到網(wǎng)格父容器之外。這是因為%
是使用總網(wǎng)格區(qū)域來計算的。這兩列消耗了父容器的內(nèi)容區(qū)域的25%+75%=100%
,并且它們不允許收縮。當我們添加了16px
的gap
時,列別無選擇,只能溢出容器。
相比之下,fr
是「基于額外的空間計算」的。在這種情況下,額外的空間已經(jīng)減少了16px
,以用于設(shè)置gap
。
如果我們向一個兩列網(wǎng)格
添加「超過兩個子元素」會發(fā)生什么呢?
從結(jié)果來看,gird
將第三個元素放置到了第二行。
?
grid
算法希望確保「每個子元素都有自己的網(wǎng)格單元」。它會根據(jù)需要「生成新的行來實現(xiàn)這個目標」。?
這在我們有可變數(shù)量的項目并且我們希望容器自動排布項目的情況下非常方便。
不過,在其他情況下,我們希望「顯式定義行,以創(chuàng)建特定的布局」。我們可以使用grid-template-rows[10]屬性來實現(xiàn):
通過同時定義grid-template-rows
和grid-template-columns
,我們創(chuàng)建了一個顯式網(wǎng)格。我們就可以用幾行代碼,實現(xiàn)了所謂的「圣杯布局」。
假設(shè)我們正在構(gòu)建一個日歷:
Grid
是處理這種情況的絕佳工具。我們可以將其構(gòu)建為一個7列的網(wǎng)格,每列占據(jù)1個單位的空間:
.calendar { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;}
上面方式肯定是有效可行的,但是我們不想重復寫1fr
多次。此時我們就可以使用repeat()
來解決。
.calendar { display: grid; grid-template-columns: repeat(7, 1fr);}
repeat函數(shù)
會為我們進行復制和粘貼。
?
默認情況下,
Grid
算法會將每個子項分配給「第一個未占用的網(wǎng)格單元」?
但是呢,Grid
還賦予我們一種能力-我們可以將我們的項目分配到任何我們想要放置的單元格!子項甚至可以跨越多行/列
。
grid-row[11]和grid-column[12]屬性允許我們指定網(wǎng)格子項應(yīng)該占據(jù)哪些軌道
。
如果我們希望子項占據(jù)單個行或列,我們可以通過其編號來指定。grid-column: 3
將使子項位于第三列。
網(wǎng)格子項還可以跨越多個行/列
。其語法「使用斜杠來劃分起始和結(jié)束位置」:
.child { grid-column: 1 / 4;}
上面的1 / 4
可不是一個分數(shù),在CSS
中,「斜杠字符不用于除法,而是用于分隔值組」。在這種情況下,它允許我們在一個聲明中設(shè)置起始和結(jié)束列。
這本質(zhì)上是這樣的簡寫形式:
.child { grid-column-start: 1; grid-column-end: 4;}
?
我們提供的數(shù)字是「基于列線」,而不是列索引。
?
一個有4列的網(wǎng)格實際上有5條列線。當我們將子項分配到網(wǎng)格時,我們使用這些線來錨定它們。如果我們希望子項跨越前3列,它需要從第1行開始,并在第4行結(jié)束。
在從左到右
的語言中,比如英語,我們從左到右計算列。然而,使用負數(shù)行號,我們也可以反向計算,從右到左
。
.child { /* 位于從右數(shù)的第2列: */ grid-column: -2;}
?
我們還可以混合使用正數(shù)和負數(shù)。
?
對比上面兩個例子,盡管我們根本沒有改變grid-column
的配置(grid-column:1 /-1
),雖然列數(shù)增加了,但是每個例子中的子項都跨越了網(wǎng)格的整個寬度!
假設(shè)我們正在構(gòu)建這個布局:
根據(jù)我們目前學到的知識,我們可以這樣操作:
.grid { display: grid; grid-template-columns: 2fr 5fr; grid-template-rows: 50px 1fr;}.sidebar { grid-column: 1; grid-row: 1 / 3;}header { grid-column: 2; grid-row: 1;}main { grid-column: 2; grid-row: 2;}
上面例子是可行的,但是Grid
還為我們提供了更好的解決方案 - grid-areas[13]
像之前一樣,我們使用 grid-template-columns
和 grid-template-rows
定義了網(wǎng)格結(jié)構(gòu)。除此之外,我們還使用grid-template-areas
定義了一個區(qū)域的劃分
.parent { grid-template-areas: 'sidebar header' 'sidebar main';}
使用grid-template-areas
我們勾勒出了我們想要創(chuàng)建的網(wǎng)格。
?
每一行代表一行,每個單詞是我們給網(wǎng)格的特定部分命名。
?
然后,我們不是用 grid-column
和 grid-row
分配子項,而是用 grid-area[14]!
當我們想讓特定區(qū)域跨越多行或多列時,我們可以在我們的模板中「重復該區(qū)域的名稱」。在這個例子中,sidebar
區(qū)域跨越了兩行,所以我們在第一列的兩個單元格中都寫了 sidebar
。
在構(gòu)建顯示布局時,我們可以通過使用areas
和行/列
都可以達到目的,但是呢,使用areas
時,它允許我們給grid
分配語義含義,而不是使用晦澀難懂的行/列數(shù)字。也就是說,當網(wǎng)格具有固定數(shù)量的行和列時,areas
效果最佳。grid-column
和 grid-row
可以在隱式網(wǎng)格中很有用。
?
在處理網(wǎng)格分配時存在一個重要的問題:
Tab 鍵
順序仍然基于 DOM 位置,而不是網(wǎng)格位置。?
通過一個示例會更容易理解。在這個示例中,我設(shè)置了一組按鈕,并使用 Grid
對它們進行了排列:
如果我們使用的是帶有鍵盤的設(shè)備,可以通過點擊左上角的第一個按鈕(One
),然后按 Tab 鍵逐個移動按鈕。
你應(yīng)該會看到類似于這樣的情況:
焦點輪廓在頁面上毫無規(guī)律地跳動,這是因為按鈕的焦點是「基于它們在 DOM 中出現(xiàn)的順序而定」的。
為了解決這個問題,我們應(yīng)該重新按視覺順序在 DOM 中重新排列網(wǎng)格子項,以便我可以從左到右,從上到下進行 Tab 鍵瀏覽。
到目前為止我們看到的所有示例中,我們的列和行都會伸展以填滿整個網(wǎng)格容器。然而,我們是通過配置讓內(nèi)容進行別樣的排布。
start
:將網(wǎng)格與容器的開始邊緣對齊end
:將網(wǎng)格與容器的結(jié)束邊緣對齊center
:將網(wǎng)格置于容器的中心stretch
:重新調(diào)整網(wǎng)格項的大小,以使網(wǎng)格填充容器的整個寬度space-around
:在每個網(wǎng)格項之間放置相等量的空間,兩端的空間為一半大小space-between
:在每個網(wǎng)格項之間放置相等量的空間,兩端沒有空間space-evenly
:在每個網(wǎng)格項之間放置相等量的空間,包括兩端例如,假設(shè)我們定義了兩個都是 90px
寬的列。只要網(wǎng)格容器大于 180px
,就會有一些多余的空間:
如果想利用多余空間進行對項目的排布處理,此時我們可以使用 justify-content
屬性來控制列的分布,并且我們接受上面所列舉的各種值。
.container { justify-content: start | end | center | stretch | space-around | space-between | space-evenly; }
justify-content:startjustify-content:centerjustify-content:endjustify-content:space-betweenjustify-content:space-aroundjustify-content:space-evenly
看到space-between/space-around
是否想到Flex
,布局排布的原理是一樣的,只不過Grid
和Flex
最大的區(qū)別在于,我們正在「對齊列,而不是項本身」。本質(zhì)上,justify-content[15] 讓我們更好的操作網(wǎng)格的列,以便可以根據(jù)我們的意愿將它們分布在整個網(wǎng)格中。
如果我們想在列內(nèi)對齊項目本身,我們可以使用 justify-items
屬性:
start
:將項目與其單元格的開始邊緣對齊end
:將項目與其單元格的結(jié)束邊緣對齊center
:將項目置于其單元格的中心stretch
:填充單元格的整個寬度(這是默認值).container { justify-items: start | end | center | stretch;}
當我們將一個 DOM 節(jié)點放入網(wǎng)格父元素時,默認行為是它會跨越整個列,就像流式布局中的 <div>
會橫向拉伸以填滿其容器一樣。但是,使用 justify-items
,我們可以調(diào)整這種行為。
.container { justify-items: stretch;}
.container { justify-items: start;}
.container { justify-items: end;}
.container { justify-items: center;}
我們可以使用justify-self
來控制「特定網(wǎng)格子元素」的對齊方式
其值為以下幾個:
start
:將網(wǎng)格項與其單元格的開始邊緣對齊end
:將網(wǎng)格項與其單元格的結(jié)束邊緣對齊center
:將網(wǎng)格項置于其單元格的中心stretch
:填充單元格的整個寬度(這是默認值).item { justify-self: start | end | center | stretch;}
.item-a { justify-self: start;}
.item-a { justify-self: end;}
.item-a { justify-self: center;}
.item-a { justify-self: stretch;}
到目前為止,我們一直在討論如何在水平方向上對齊內(nèi)容。Grid
還提供了一組額外的屬性來在垂直方向
上對齊內(nèi)容:
其取值為以下幾種:
stretch
:填充單元格的整個高度(這是默認值)start
:將項目與其單元格的開始邊緣對齊end
:將項目與其單元格的結(jié)束邊緣對齊center
:將項目置于其單元格的中心baseline
:沿著文本基線對齊項目。.container { align-items: start | end | center | stretch;}
.container { align-items: start;}
.container { align-items: end;}
.container { align-items: center;}
.container { align-items: stretch;}
align-content
類似于 justify-content
,但它影響的是行而不是列。同樣,align-items
類似于 justify-items
,但它處理的是網(wǎng)格區(qū)域內(nèi)項目的垂直對齊,而不是水平對齊。
?
為了進一步梳理:
justify
— 處理列align
— 處理行content
— 處理網(wǎng)格結(jié)構(gòu)items
— 處理網(wǎng)格結(jié)構(gòu)內(nèi)的 DOM 節(jié)點。?
最后,除了 justify-self
,我們還有 align-self
。這個屬性控制單個網(wǎng)格項在其單元格內(nèi)的垂直位置。
place-content
屬性是一個縮寫。它是這樣的語法糖:
.parent { justify-content: center; align-content: center;}
使用該屬性,我們可以用最少的代碼實現(xiàn)我們平時很難實現(xiàn)的布局。
只使用兩個 CSS 屬性,我們就可以將子元素水平和垂直居中于容器中:
正如我們所學到的,justify-content
控制列的位置。align-content
控制行的位置。在這種情況下,我們有一個隱式網(wǎng)格只有一個子元素,因此我們得到一個 1×1
網(wǎng)格。place-content: center
將行和列都推向中心。
將元素放置在左上角將元素放置在右下角
本文鏈接:http://www.tebozhan.com/showinfo-26-86681-0.htmlCSS Grid 那些鮮為人知的內(nèi)幕
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 2024 年 Q1 全球筆記本電腦出貨量達到 4610 萬臺:同比增長 7%,聯(lián)想繼續(xù)領(lǐng)跑