1. 程式人生 > >CSS躬行記(9)——網格佈局

CSS躬行記(9)——網格佈局

  網格佈局(Grid Layout)也叫柵格佈局,與表格佈局類似,也依賴行和列。但與之不同的是,網格佈局能直接控制HTML文件中元素的順序、位置和大小等,而不用再借助輔助元素。

一、術語

  下圖展示了CSS規範中定義的網格。

  (1)網格容器(grid container)由display屬性的兩個特殊值(grid和inline-grid)所建立,由於它不是塊級容器,因此其佈局不受浮動和外邊距塌陷的影響。

  (2)網格項(grid item)也叫網格元素,是網格容器的子元素,注意,它的後代元素不是網格項,但它自身也能變為網格容器。

  (3)網格線(grid line)為網格容器劃分不同的區域,注意,網格線的數量沒有限制。

  (4)網格單元(grid cell)是由四條網格線限定的區域,其內部不會有其它的網格線。

  (5)網格區域(grid area)由一個或多個相鄰的網格單元組成的矩形區域,最大的網格區域會包含所有的網格單元。

  (6)網格軌道(grid track)是由兩條網格線夾住的區域,從容器的一邊延伸到另一邊,水平方向的叫網格行(grid row),垂直方向的叫網格列(grid column),它們的尺寸由網格線的位置決定。

二、行和列

  grid-template-rows和grid-template-columns兩個屬性可以定義網格線的名稱和網格軌道的尺寸,前者可宣告行的數量,後者可宣告列的數量。

  在下面的示例中,將網格容器分成兩行三列,其中第一條網格線距離容器左邊界200px的位置,第二條網格線距離第一條網格線的偏移是容器寬度的一半,第三條網格線距離第二條30px的位置。

<style>
  div {
    display: grid;
    width: 200px;
    height: 100px;
    grid-template-rows: 50px 50px;
    grid-template-columns: 50px 50% 30px;
  }
</style>
<div>
  <section>1</section>
  <section>2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
  <section>6</section>
</div>

  下圖的左側是上述程式碼的效果,右側中的虛線是生成的網格線,後文會反覆使用這種格式的標註圖。注意,網格線可以不與容器的邊界接觸,兩個屬性中的百分數參照的是網格容器的寬度或高度。

  如果要佔滿網格容器的橫向空間,那麼可以使用minmax()函式,如下所示。minmax(30px, 100%)表示該列所佔的空間在30px和容器寬度之間,即不能小於30px並且不能大於容器寬度,效果如下圖所示。

div {
  grid-template-columns: 50px 50% minmax(30px, 100%);
}

  如果要為網格線命名,那麼可以將名稱放在值的恰當位置(例如名稱在值之前,樣式如下),並用方括號包裹。

div {
  grid-template-columns: [col-a] 50px [col-b] 50% [col-c] minmax(30px, 100%);
}

1)fr

  在網格佈局中,包含一個全新的fr單位,能將空間分成一定份數,彈性的分配給行或列,這個單位的作用類似於彈性盒中的flex-grow屬性。在下面的示例中,將容器均分為3列,效果如下圖所示。

div {
  grid-template-columns: 1fr 1fr 1fr;
}

  注意,fr可與其它長度單位混合使用,其作用的是可用空間,即剩餘空間。在下面的示例中,將第一列寬度固定,剩餘的空間由兩列均分,效果如下圖所示。

div {
  grid-template-columns: 30px 1fr 1fr;
}

2)min-content和max-content

  當網格軌道的尺寸與其內容有關時,可使用這兩個關鍵字。min-content會佔據內容所需的最小空間,例如將min-content應用於網格容器的第二列,樣式如下,效果如下圖所示,網格項中有大量的斷行。

<style>
  div {
    grid-template-columns: 30px min-content 1fr;
  }
</style>
<div>
  <section>1</section>
  <section>2 2 2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
  <section>6</section>
</div>

  max-content的作用正好與min-content相反,它會佔據內容所需的最大空間。將max-content也應用到上面的第二列,樣式如下,效果如下圖所示,網格項為了防止文字換行,會不斷地擴大其寬度。

div {
  grid-template-columns: 30px max-content 1fr;
}

3)fit-content()

  該函式的引數是一個長度值或百分數,能將佔用空間指定成特定的值,它相當於下面等號右邊的公式。

fit-content(argument) = min(max-content, max(min-content, argument))

  先將接收的引數與min-content比較,得出較大值,再將該值與max-content比較,得到較小值。理解起來比較拗口,如果換成minmax()函式,含義就比較清晰了,與fit-content(argument)等價的公式如下。

minmax(min-content, max-content)

4)repeat()

  該函式能為網格軌道宣告重複的行或列,它的第一個引數是重複次數,第二個引數是軌道尺寸,例如將網格容器均分成三列,樣式如下。

div {
  grid-template-columns: repeat(3, 1fr);
  /* 等價於 */
  grid-template-columns: repeat(1fr, 1fr, 1fr);
}

  注意,軌道尺寸不侷限於一個,可以指定多個,樣式如下,效果如下圖所示。

div {
  grid-template-columns: repeat(2, 20px 1fr);
}

三、網格項

  通過起始和終止處的網格線的名稱或編號可指定網格項的位置,其中列的編號從左到右依次遞增,行的編號從上到下依次遞增,如下圖所示。

  在下面的示例中,將網格容器均分成三行四列,並且把第一個section元素指定到第二行第二列的位置,效果如下圖所示。

<style>
  div {
    grid-template-rows: repeat(3, 1fr);
    grid-template-columns: repeat(4, 1fr);
  }
  .one {
    grid-row-start: 2;
    grid-row-end: 3;
    grid-column-start: 2;
    grid-column-end: 3;
  }
</style>
<div>
  <section class="one">1</section>
  <section>2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
  <section>6</section>
  <section>7</section>
  <section>8</section>
  <section>9</section>
  <section>10</section>
</div>

   將下面的樣式新增給第一個section元素,可以得到跨列的效果,如下圖所示。

.two {
  grid-row-start: 2;
  grid-row-end: 3;
  grid-column-start: 2;
  grid-column-end: 4;
}

  網格線的編號支援負數,也就是以相反的方式計數,從後往前或從下往上,例如將第一個section元素放置在容器的右下角,樣式如下,效果如下圖所示。

.three {
  grid-row-start: -2;
  grid-row-end: -1;
  grid-column-start: -2;
  grid-column-end: -1;
}

1)span

  還有另一種方式可用來指定網格項的位置,即用span和正整數組合,表示要跨的網格軌道數目。在下面的樣式中,“span 2”用於跨過兩列。

.four {
  grid-row-start: 2;
  grid-row-end: 3;
  grid-column-start: 2;
  grid-column-end: span 2;
}

  當把span應用於grid-row-start或grid-column-start屬性時,將會沿著網格開始的方向計數,樣式如下,效果如下圖所示。

.five {
  grid-row-start: 2;
  grid-row-end: 3;
  grid-column-start: span 2;
  grid-column-end: 3;
}

  注意,span後面可以省略數字,預設是1,但不能跟零或負數。

2)簡寫屬性

  grid-row是grid-row-start和grid-row-end的簡寫屬性,grid-column是grid-column-start和grid-column-end的簡寫屬性。

  grid-row和grid-column可分別將行和列的起止網格線宣告在一行,並用斜槓(/)分隔,如下所示。

.six {
  grid-row: 2 / 3;
  grid-column: 2 / span 2;
}

  注意,由於預設跨度是1,因此可以省略grid-row和grid-column斜槓後的數字,如下所示。

.seven {
  grid-row: 2;
  grid-column: 2;
}

  grid-area是grid-row-start、grid-row-end、grid-column-start和grid-column-end的簡寫屬性,也可用斜槓(/)將值分隔,如下所示。

.eight {
  grid-area: 2 / 2 / 3 / span 2;
}

  注意,網格線值的順序是:起始行、起始列、終止行、終止列,圍繞網格項逆時針排列。

3)重疊

  當兩個網格項發生重疊時,可通過z-index屬性來決定誰在上,誰在下。例如為兩個section元素分別新增下面的樣式,效果如下圖所示,第二個section元素覆蓋了第一個section元素。

<style>
  .overlap1 {
    grid-row: 1;
    grid-column: 1 / span 2;
  }
  .overlap2 {
    grid-row: 1;
    grid-column: 2 / span 2;
  }
</style>
<div>
  <section class="overlap1">1</section>
  <section class="overlap2">2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
  <section>6</section>
  <section>7</section>
  <section>8</section>
  <section>9</section>
  <section>10</section>
</div>

  當為第一個section元素新增z-index屬性後(樣式如下),就會覆蓋第二個section元素,效果如下圖所示。

.overlap1 {
  z-index: 1;
}

4)自動增加網格線

  當網格項超出容器邊界時,可通過grid-auto-rows和grid-auto-columns分別控制行和列的尺寸。

  在下面的示例中,將網格容器分成兩行三列,它的第七個超出邊界的網格項的高度預設是內容的高度,即“height:auto”,效果如下圖所示。

<style>
  div {
    grid-template-rows: 50px 50px;
    grid-template-columns: repeat(3, 1fr);
  }
</style>
<div>
  <section>1</section>
  <section>2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
  <section>6</section>
  <section>7</section>
</div>

  當為網格容器新增grid-auto-rows屬性後,就能改變網格項的高度,如下所示,效果如下圖所示。

div {
  grid-auto-rows: 50px;
}

  grid-auto-columns的用法與之類似,但修改的是超出邊界的網格項的寬度。在下圖中,左側是預設超出時的寬度,即內容的寬度(width:auto);右側是為容器新增下面樣式後的寬度。

div {
  grid-auto-flow: column;
  grid-auto-columns: 50px;
}

  注意,grid-auto-flow是一個網格流屬性,可讓網格項按列排序,後文第五節會重點講解。

四、網格區域

   通過grid-template-areas屬效能夠以視覺化的方式宣告網格佈局,其值是用空格或換行符分隔的字串列表,每段字串又是以空格分隔的自定義識別符號,如下所示。

div {
  grid-template-areas: "a b b" 
              "a . c";
}

  每個識別符號表示一個網格單元,注意,組合在一起的形狀必須得是矩形。當只需要佔位時,可以使用點號(.)建立匿名單元。

  為了將網格項與網格區域關聯,需要使用grid-area屬性。通過它來引用命名好的網格區域,如下所示,grid-area的屬性值就是grid-template-areas屬性中的識別符號,效果如下圖所示。

<style>
  .a {
    grid-area: a;
  }
  .b {
    grid-area: b;
  }
  .c {
    grid-area: c;
  }
</style>
<div>
  <section class="a">a</section>
  <section class="b">b</section>
  <section class="c">c</section>
</div>

 

五、網格流

  網格流可由grid-auto-flow屬性控制,它分為兩種填充方式:逐行和逐列,並且兩者都可通過稠密模式更高效地佔用網格單元。

  在下面的示例中,第一個div元素中的網格項會先填滿一行再換到下一行,效果如圖5-57的左側;第二個div元素中的網格項會先填滿一列再換到下一列,效果如下圖的右側。

<style>
  .row {
    grid-auto-flow: row;
  }
  .column {
    grid-auto-flow: column;
  }
</style>
<div class="row">
  <section>1</section>
  <section>2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
  <section>6</section>
</div>
<div class="column">
  <section>1</section>
  <section>2</section>
  <section>3</section>
  <section>4</section>
  <section>5</section>
  <section>6</section>
</div>

  grid-auto-flow屬性預設是稀疏模式,而稠密模式會盡力找出最前面的空位置。假設網格容器包含三個網格項,其中有兩個會佔用兩個網格單元,樣式如下,效果如下圖所示,第一個網格項之後的網格單元閒置著。

<style>
  .across {
    grid-column: auto / span 2;
  }
</style>
<div class="row">
  <section class="across">1</section>
  <section class="across">2</section>
  <section>3</section>
</div>

  當為網格容器的grid-auto-flow屬性新增dense後,就能觸發稠密模式,得到的效果如下圖所示,第三個網格項會移動到第一個之後。

.row {
  grid-auto-flow: row dense;
}

 

六、間距

  間距(gutter)也叫空距或欄距,用於控制兩個網格軌道之間的距離,類似於將網格線加粗,使其具有寬度。

  間距可由grid-row-gap和grid-column-gap兩個屬性設定,但要注意,現在這兩個屬性已改名成row-gap和column-gap,它們相當於表格樣式中的border-spacing屬性。在下面的示例中,為網格容器添加了兩個方向的間距,效果如下圖所示。

div {
  row-gap: 10px;
  column-gap: 20px;
}

  grid-gap是一個簡寫屬性,可將行和列的間距組合在一起定義,下面樣式的效果與上圖一樣。注意,該屬性現已改名成gap。

div {
  gap: 10px 20px;
}

  順便說一句,CSS規範提供了一個簡寫屬性:grid,可將上述grid-template-rows、grid-auto-rows、row-gap等屬性組合在一起宣告。

七、對齊方式

   網格佈局中的對齊屬性與彈性盒中的類似,也是使用下表中的相關屬性。

屬性 作用
justify-self 控制一個網格項的水平對齊
align-self 控制一個網格項的垂直對齊
justify-items 控制網格容器中的所有網格項的水平對齊
align-items 控制網格容器中的所有網格項的垂直對齊
justify-content 控制網格軌道的水平對齊
align-content 控制網格軌道的垂直對齊

1)對齊網格項

  justify-self和align-self兩個屬性適用於單個網格項,它們的預設值都是stretch,其它可選的關鍵字包括start、end和center等。

  假設有一個兩行三列的網格容器,為它的四個網格項分別新增四個不同關鍵字的justify-self屬性,效果如下圖所示。

<style>
  div {
    display: grid;
    grid-template-rows: repeat(2, 1fr);
    grid-template-columns: repeat(3, 1fr);
  }
  .justify-self-start {
    justify-self: start;
  }
  .justify-self-end {
    justify-self: end;
  }
  .justify-self-center {
    justify-self: center;
  }
  .justify-self-stretch {
    justify-self: stretch;
  }
</style>
<div>
  <section class="justify-self-start">start</section>
  <section class="justify-self-center">center</section>
  <section class="justify-self-end">end</section>
  <section class="justify-self-stretch">stretch</section>
</div>

  將這四個關鍵字賦給align-self屬性,再應用到另一個兩行三列的網格容器中,得到的效果如下圖所示。

  justify-items和align-items兩個屬性適用於網格容器,它們的預設值也是stretch,可選的關鍵字與之前的兩個屬性類似,下面的樣式在為之前的div元素(網格容器)新增justify-items屬性。

div {
  justify-items: start;
}

2)對齊網格軌道

  justify-content和align-content兩個屬性適用於網格容器,可用的關鍵字包括start、end、center、space-between和space-around等。

  下圖展示了這五個關鍵字賦給justify-content屬性後,再分別應用於網格容器的效果。

  下圖展示了這五個關鍵字賦給align-content屬性後,再分別應用於網格容器的效果。

八、排序

  在網格佈局中,也可以像彈性盒那樣使用order屬性調整網格項的順序。注意,order屬性作用於網格項,其值是一個整數,包括零和負數。在下面的示例中,將第一個和第三個section元素調換了次序,效果如下圖所示。

<style>
  .first {
    order: 1;
  }
  .second {
    order: 2;
  }
  .third {
    order: 3;
  }
</style>
<div>
  <section class="third">1</section>
  <section class="second">2</section>
  <section class="first">3</section>
</div>

&n