CSS 網格佈局和漸進增強

在 2017 年春季,我們第一次看到像網格這樣的主要規範幾乎同時釋出到瀏覽器中,現在我們在 Firefox、Chrome、Opera、Safari 和 Edge 的公開版本中都支援 CSS 網格佈局。但是,雖然常青瀏覽器意味著我們中的許多人會很快看到大多數使用者擁有網格佈局支援,但我們也需要處理舊的或不支援的瀏覽器。在本指南中,我們將介紹各種支援策略。

支援的瀏覽器

CSS 網格佈局在所有現代瀏覽器中都是無字首的。本指南中詳細介紹的所有屬性和值的支援在所有瀏覽器之間是互操作的。這意味著,如果您在 Firefox 中編寫一些網格佈局程式碼,它應該在 Chrome 中以相同的方式工作。這不再是一個實驗性規範,您可以在生產環境中安全地使用它。

使用 CSS 網格進行佈局安全嗎?

是的。與任何前端技術選擇一樣,是否使用 CSS 網格佈局取決於您的網站訪問者通常使用的瀏覽器。

開始在生產環境中使用網格

值得注意的是,您不必以“全有或全無”的方式使用網格。從使用網格來增強設計中的元素開始,這些元素可以使用舊方法顯示。使用網格佈局覆蓋舊方法效果出奇地好,因為網格與這些其他方法的互動方式。

浮動

浮動過去常用於建立多列布局。如果您要支援帶有浮動佈局的舊程式碼庫,則不會發生衝突。網格專案忽略浮動屬性;事實上,網格專案優先。在下面的示例中,我有一個簡單的媒體物件。如果未從舊版 CSS 中刪除 float,因為容器是一個網格容器,則沒有問題。我們可以使用 CSS 網格中實現的對齊屬性。

float 不再適用,我可以使用 CSS 盒子對齊屬性 align-self 將我的內容對齊到容器的末尾

css
* {
  box-sizing: border-box;
}
img {
  max-width: 100%;
  display: block;
}
.media {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  max-width: 400px;
  display: grid;
  grid-template-columns: 1fr 2fr;
  grid-template-areas: "img content";
  margin-bottom: 1em;
}
.media::after {
  content: "";
  display: block;
  clear: both;
}
.media .text {
  padding: 10px;
  align-self: end;
}

/* old code we can't remove */
.media .image {
  float: left;
  width: 150px;
  margin-right: 20px;
}
html
<div class="media">
  <div class="image">
    <img src="https://dummyimage.com/150x150" alt="placeholder" />
  </div>
  <div class="text">
    This is a media object example. I am using floats for older browsers and
    grid for new ones.
  </div>
</div>

下圖顯示了左側為不支援瀏覽器的媒體物件,右側為支援瀏覽器的媒體物件

A simple example of overriding a floated layout using grid. Both have the image aligned left. The text is vertically aligned at top in the float example and at the bottom in the grid example.

使用特性查詢

上面的例子非常簡單,我們可以避免編寫對不支援網格的瀏覽器來說會造成問題的程式碼,舊版程式碼對支援網格的瀏覽器來說也不是問題。然而,情況並不總是如此簡單。

一個更復雜的例子

在接下來的示例中,我有一組浮動的卡片。我已經給卡片設定了 width,以便 float 它們。為了在卡片之間建立間隙,我在專案上使用了 margin,然後在容器上使用了負邊距。

css
.wrapper ul {
  overflow: hidden;
  margin: 0 -10px;
  padding: 0;
  list-style: none;
}
.wrapper li {
  float: left;
  width: calc(33.333333% - 20px);
  margin: 0 10px 20px 10px;
}
html
<div class="wrapper">
  <ul>
    <li class="card">
      <h2>One</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Two</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Three</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Four</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Five</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Six</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
  </ul>
</div>

這個示例演示了我們在浮動佈局中遇到的典型問題:如果向任何一張卡片新增額外的內容,佈局就會被破壞。

A floated cards layout demonstrating the problem caused by uneven content height. The top row has 3 cards. The fourth card is floated under the third card. Then a bottom row has contains the fifth and sixth cards. There is a largish empty space under the fourth card.

為了相容舊瀏覽器,我在專案上設定了 min-height,並希望我的內容編輯器不會新增太多內容,導致佈局混亂!

然後我使用網格來增強佈局。我可以將我的 <ul> 變成一個具有三列軌道的網格容器。但是,我分配給列表項本身的寬度仍然適用,現在它使這些項的寬度變成軌道寬度的三分之一。

Six very tall, very narrow grid items with text overflowing on the right. After applying grid to our container, the width of the items is now incorrect as they display at one third of the item width.

如果我將寬度重置為 auto,那麼這將停止舊瀏覽器中的浮動行為。我需要能夠為舊瀏覽器定義寬度,併為支援網格的瀏覽器刪除寬度。感謝 CSS 功能查詢,我可以在我的 CSS 中直接執行此操作。

使用功能查詢的解決方案

功能查詢 如果您曾經使用 媒體查詢 建立響應式佈局,您會覺得它非常熟悉。我們不是檢查 視窗 寬度或瀏覽器的某些功能,而是使用 @supports 規則來檢查對 CSS 屬性和值對的支援。在功能查詢中,我們可以編寫任何需要應用現代佈局的 CSS,並刪除舊佈局所需的任何內容。

css
@supports (display: grid) {
  .wrapper {
    /* do anything for grid supporting browsers here. */
  }
}

功能查詢擁有出色的瀏覽器支援,所有支援更新的網格規範的瀏覽器也都支援功能查詢。您可以使用它們來處理我們在增強後的浮動佈局中遇到的問題。

我使用 @supports 規則來檢查對 display: grid 的支援。然後,我在 <ul> 上執行我的網格程式碼,將 <li> 上的寬度和 min-height 設定為 auto。我還刪除了邊距和負邊距,並使用 gap 屬性替換間距。這意味著我不會在最後一行的框上獲得最終邊距。現在佈局可以正常工作,即使其中一張卡片中的內容比其他卡片多。

css
.wrapper ul {
  overflow: hidden;
  margin: 0 -10px;
  padding: 0;
  list-style: none;
}
.wrapper li {
  float: left;
  width: calc(33.333333% - 20px);
  margin: 0 10px 20px 10px;
}
@supports (display: grid) {
  .wrapper ul {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    margin: 0;
  }
  .wrapper li {
    width: auto;
    min-height: auto;
    margin: 0;
  }
}
html
<div class="wrapper">
  <ul>
    <li class="card">
      <h2>One</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Two</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Three</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Four</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Five</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Six</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
  </ul>
</div>

覆蓋 display 的其他值

由於使用浮動建立專案網格存在問題,我們中的許多人會使用與上面顯示的浮動方法不同的方法來佈局一組卡片。使用 display: inline-block 是一種替代方法。

我再次可以使用功能查詢來覆蓋使用 display: inline-block 的佈局,而且我仍然不需要覆蓋所有內容。設定為 inline-block 的專案會變成網格專案,因此 inline-block 的行為不再適用。我在 inline-block 顯示模式下對我的專案使用了 vertical-align 屬性,但此屬性不適用於網格專案,因此一旦專案變成網格專案,它就會被忽略。

css
.wrapper ul {
  margin: 0 -10px;
  padding: 0;
  list-style: none;
}

.wrapper li {
  display: inline-block;
  vertical-align: top;
  width: calc(33.333333% - 20px);
  margin: 0 10px 20px 10px;
}
@supports (display: grid) {
  .wrapper ul {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    margin: 0;
  }
  .wrapper li {
    width: auto;
    margin: 0;
  }
}
html
<div class="wrapper">
  <ul>
    <li class="card">
      <h2>One</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Two</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Three</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Four</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Five</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
    <li class="card">
      <h2>Six</h2>
      <p>We can use CSS grid to overwrite older methods.</p>
    </li>
  </ul>
</div>

我們再次需要解決專案上的寬度,然後解決我們想要增強的任何其他屬性。在本例中,我再次使用了 gap 而不是邊距和負邊距來建立我的間距。

規範如何定義這些覆蓋?

CSS 網格佈局規範詳細說明了為什麼當某些東西變成網格專案時我們可以覆蓋某些屬性的行為。規範的關鍵部分是

由於此行為在規範中進行了詳細說明,因此您可以放心地在對舊瀏覽器的支援中使用這些覆蓋。這裡描述的任何內容都不應該被視為“駭客”。相反,我們正在利用網格規範詳細說明了不同佈局方法之間的互動這一事實。

其他顯示值

當元素的父元素設定為 display: grid 時,它將被塊化,如 CSS 顯示規範 中所定義。在我們設定為 inline-block 的專案中,這就是為什麼 display: inline-block 不再適用的原因。

如果您使用 display: table 作為您的傳統佈局,設定為 display: table-cell 的專案會生成匿名框。因此,如果您使用 display: table-cell 且沒有父元素設定為 display-table,則會圍繞任何相鄰的單元格建立一個匿名錶格包裝器,就像您將它們包裝在一個設定為 display: table 的 div 或其他元素中一樣。如果您有一個專案設定為 display: table-cell,然後在功能查詢中將父元素更改為 display: grid,則不會發生此匿名框建立。這意味著您可以覆蓋基於 display: table 的佈局,而無需額外的匿名框。

浮動元素

正如我們已經看到的那樣,float 以及 clear 對網格專案沒有影響。因此,您無需顯式將專案設定為 float: none

垂直對齊

對齊屬性 vertical-align 對網格專案沒有影響。在使用 display: inline-blockdisplay: table 的佈局中,您可能會使用 vertical-align 屬性來執行基本對齊。然後,在您的網格佈局中,您擁有更強大的框對齊屬性。

多列布局

您還可以使用多列布局作為您的傳統瀏覽器計劃,因為 column-* 屬性應用於網格容器時不適用。