CSS Grid中的陷阱和絆腳石

發表於2018-04-02

2017年3月,CSS Grid在幾個星期內就被髮送到Chrome、Firefox和Safari的生產版本中。很高興,大家可以使用它來解決實際問題。

CSS Grid是一種不同的佈局方式,在大家開始使用規範的時候,有很多常見的問題。這篇文章的目的是回答其中的一些問題,並且將會是Smashing Magazine中有關於CSS Grid一系列文章中的一篇。

為什麼使用CSS Grid而不是CSS Flexbox?

在CSS網格佈局在瀏覽器中可用之前,很多人都認為Flexbox是我們所有設計相關問題的答案。然而,Flexbox並沒有提供比浮動更好的網格系統,儘管它確實比浮動建立一個網格系統更簡單。一個真正的網格是二維的。這兩個維度就是行和列,並且使用網格佈局,你可以同時控制它們。使用Flexbox,你可以選擇是否將這些項列成一行或列,一個或另一個,而不是兩個。

這裡有一個簡單的示例,突出其區別。第一個佈局使用Flexbox,為了能儘可能多的使用盒子,以適合可用的寬度。這裡我們控制了整個行中的佈局。允許Flex專案進行包裹,因此會建立新的行,但是每一行都是一個新的Flex容器。空間分佈在行中發生,所以取決於最後一行多少項,它們有時不會與上面的Flex項對齊。DEMO1

第二個示例使用CSS Grid實現相同的佈局,但是,你可以看到,最後一行中的專案始終保持在它們的列中。這是因為在網格中,我們將專案排列成行和列 —— 二維佈局。

你還可以在第二個示例中看到,在CSS Grid佈局中,我們不需要向網格新增任何內容來進行佈局。所有東西都被放在容器上。在Flexbox佈局中,你必須針對Flex專案來設定flex-growflex-shrinkflex-basis屬性。這是理解網格佈局關鍵所在,也可能是大家有很多困惑的地方。Grid主要是關於包含元素的,而我們之前的所有佈局方法都依賴於我們在佈局中設定的寬度,使某些東西看起來像一個網格。

如果你使用一個簡化版本的浮動12列“網格”,我們必須計算每一列的百分比大小,加上每個列之間間距的百分比大小。要建立跨多個列的項,需要將所有項的寬度加上用於分隔它們的邊界寬度。DEMO3

使用Flexbox建立的網格也是如此。當我們在父節點上通過display:flex建立Flex佈局時,Flex所有的大小都需要在單個Flex專案上進行。為了製作一個Flexbox的“網格”,我們必須阻止Flexbox做靈活的操作,而是應該設定百分比寬度,就像我們前面的浮動網格示例一樣。使用Flexbox要比浮動更有一些優勢,比如控制對齊和列等高之類的要簡易得多。然而,在Flexbox和浮動的方法中仍然沒有網格,只是通過設定專案的大小,並將它們排列起來,讓其看起來像網格的東西。DEMO4

在網格中,所有的大小都發生在容器上。一旦我們建立了我們的網格軌道,我們就可以告訴單個專案(Grid專案)有多少個軌道可以跨越,但我們卻有一個實際的網格。我們可以完全拋棄行的容器,因為網格已經有行了。這也意味著,我們也可以使用相同的方式進行跨列。這對於以前而言是件很難做的事情。

是否應該將網格用於主佈局和Flexbox用於元件佈局

隨著大家開始接觸和學習CSS Grid的佈局,這個神話不斷湧現。也許它來自於網格系統的使用,比如在Bootstrap或Foundation,大家關心的是一個整體網格上放置專案。這當然是使用網格佈局的一種方法。不過,我還是會考慮在上一節提到的不同之處。問問你自己,這個佈局是一維的還是二維的?

如果你可以使用你的元件,並且用行和列在它的上面繪製一個網格。它是二維的,那就使用CSS Grid來佈局。grid-preview-opt-1

如果相反,你希望單個專案在一行中進行擴充套件,而不考慮上面一行中發生的情況,那就應該使用Flexbox佈局更為合適。

flex-preview-opt

不管你想要展示的是一個完整的頁面,還是一個很小的元件。重要的是你想在佈局裡面的專案分配空間和相互關聯。

網格軌道大小是否由內容來決定?

我們已經看到了如何在使用網格佈局時,在容器上設定網格和網格大小。但是,網格中的項可以指定網格軌道大小。這裡要記住的關鍵是,一個單元格大小的改變將會改變整個軌道的大小。如果你不希望這種情況發生,你可能需要一個單一維度的Flexbox佈局。

最簡單的方法就是使用auto,因為它會預設在隱式網格中建立網格軌道。一個自動大小的網格軌道將擴充套件到包含所有的內容。在下面的示例中,我有一個兩列布局,在右邊的列中新增更多的內容會導致整個行的擴充套件。第二行也是自動大小,再擴充套件以包含內容。

我們可以使用兩個引數來控制網格軌道大小,例如建立一個最小的網格軌道,但其仍然會增長以適應較大的網格專案。我們可以使用minmax()函式來做這個。傳給minmax()函式的第一個值,它是網格軌道最小的值,第二個值是網格軌道最大的值。因此,你可以設定200px的行,但通過auto設定為網格軌道最大值,那麼當有較多的內容時,不會出現內容溢位。

也有一些有趣的關鍵詞可以設定大小,將在以後的文章中對它們進行適當的闡述。這些關鍵詞在指定網格中允許內容來改變網格軌道大小,並且可以在CSS內部和外部的大小模組(CSS Intrinsic and Extrinsic Sizing Module)中找到相關的詳細內容。例如min-content關鍵詞的示例,使用它建立一個網格軌道時,將會建立儘可能小的網格軌道。

在我的例子中,這個詞意味著其成為最寬的東西,網格軌首縮小以適應它。

相反,如果你使用的是max-content,你會得到一個儘可能大的網格軌道。這可能會導致溢位情況,在下面的示例中,使用了overflow: scroll設定了網格溢位,所以max-content的網格軌道會導致滾動條出現。

DEMO9

關鍵要記住的是,這將會發生在整個網格軌道上。你需要確保網格軌道的其他網格專案也能巧妙地吸收額外的空間。

瞭解瞭如何對網格軌道大小進行調整,以及內容將如何改變網格軌道大小,這可能是新手使用CSS Grid佈局中會感到最為困惑的事情之一。這需要花一點時間來理解 —— 我們之前沒有任何類似的行為。這是理解事物如何運作的最好方法。

可以使用CSS Grid來實現瀑布流佈局?

很多同學有一種誤解,認為網格佈局與瀑布流或Pinterest佈局一樣的。這通常是基於在網格佈局中自動放置網格專案,這樣的效果看上去的確有點像瀑布流佈局。在下一個示例中,我有一個佈局,使用grid-auto-flow設定為dense,實現網格專案自動流的佈局。這將導致網格專案從源程式中取出,並嘗試在網格填充空白區域。DEMO10

然而這並不是真正的瀑布流佈局,因為我們仍然有一個網格(具有行和列),並且潛在的網格專案從原始碼中移出。一個真正的瀑布流佈局將使事物在原始碼中工作。專案被推上去填充部分空間。它更像是在兩個維度上做Flexbox佈局。

macyjs-large-opt

你可以通過對所有的Grid專案進行定位處理來得到一個瀑布流外觀的網格佈局,但是自動流的瀑布流佈局,網格佈局還無法具備這方面的能力。不過,未來的規範正在做這方面的考慮。

如何向網格區域新增背景和邊框?

一個網格尚未完成的問題,網格區域本身的背景和邊框的樣式。能在網格區域上直接新增背景和邊框的樣式嗎?到目前是不可能的,如果要實現這樣的一個效果需要插入一個元素或者新增一個偽元素來完成。

下面的這個示例中,我在網格中通過偽元素來完成,將其放置在基於行的位置,然後新增一個背景和邊框到該網格區域。DEMO11

有時候可以繞過背景和邊框來實現,比如通過網格間距(grid-gap) —— 用一個1px來模擬背景或邊框,比如下面的這個示例:

為了能夠對網格區域進行適當的樣式化,我們需要引入網格區域偽元素的概念,這是一種特殊的生成內容。在 CSS WG上有一個關於這方面的問題,所以你可以在這裡參加討論,把你的想法與大家一起參與討論。

跨越到網格的末端

網格佈局具有隱式和顯式網格的概念。顯式網格是我們使用grid-template-rowsgrid-template-columns定義的網格。這個網格軌道定義了顯式網格的範圍。當我們在顯式網格之外放置一個網格專案,或者我們通過自動旋轉更多的網格專案時,隱式網格就將被建立。

除非你使用grid-auto-rowsgrid-auto-columns建立的網格軌道,否則在隱式網格中建立的網格軌道的大小將是自動的。

在很多情況下,隱式和顯式網格的渲染行為是相同的,對於很多的佈局,你會發現你定義了列,然後允許將行建立為隱式網格。不同的是,當你開始使用負的行號來引用網格的最後一行時,你會發現還是有一定區別的。

對於網格佈局中的寫作模式。在從左到右的語言(ltr)中,列第一行是在左邊,而你可以用-1來指向右邊的列。在從右到左的語言(rtl)中,列的第一行在右側,而-1則指向左邊的列。

或許你已經發現了,只有顯式的網格才可以向後計數。如果你在隱式網格中新增了行,然後嘗試以-1來指定目標,你將會發現你得到是顯式網格的最後網格線,而不是實際網格最末端的網格線。

百分比的問題

在文章開頭之處,我描述了網格佈局與之前的佈局方法與眾不同之處。由於浮動和基於Flexbox的網格的限制,我們需要變得擅長計算百分比來做佈局,所以大多數人做的第一件事就是嘗試在他們的網格佈局中使用相同的方法。然而,在這樣做之前不要忘記我們有一個新單位fr。這個單位是專門為網格佈局設計的,因為網格設定父元素的大小。

fr單位允許我們分配可用網格佈局中的可用空間。其通過檢視網格容器中可用的空間(去掉間距所需的空間、固定寬度的網格專案或定義網格軌道),然後按照我們為網格軌道指定的比例來對剩餘的網格空間進行分配。這意味著,我們使用浮動或Flexbox佈局的場景,必須有靈活的間距。

在大多數情況下,fr單位是一個比百分比更好的選擇。你可能選擇使用百分比的原因是你需要一個網格佈局,以便與其他元素匹配使用其他佈局方法,並依賴於百分比大小。然而,如果不是這樣的話,看看fr單位是否能滿足你的需求,然後對其進行計算。

網格可以巢狀使用?

網格專案也可以成為網格容器,就好比Flex專案也可以成為一個Flex容器一樣。但是,這些巢狀網格也父網格沒有任何關係,因此不能使用它們與其他巢狀網格對齊內部元素。DEMO16

在將來的網格佈局中,很可能會有一種建立巢狀網格的方法,它可以維護與父網格的關係。這意味著,除了網格的直接子節點,其他網格專案可能參與整個網格佈局。

網格佈局有對應的Polyfill嗎?

我經常會被問到是否有網格佈局的Polyfill,大家都想知道是否有一種方法可以支援舊的瀏覽器。

我的建議是,這並不是你需要做的事情。這可能會為那些已經在努力渲染現代網站的瀏覽器造成一定的效能影響,帶來不好的使用者體驗。如果你南非要較舊的瀏覽器與現代瀏覽器相同,那麼你可能要考慮在這個專案中是否使用網格佈局。不過,在大多數情況下,可以使用較老的方法來為不支援的裝置建立一個簡單的降級處理,而不需要建立兩個完全不同的CSS程式碼。這方面真的需要用一篇文章來詳細闡述,所以我將盡快在Smashing Magazine釋出這方面的教程。

除錯網格佈局

當你開始使用網格佈局時,你肯定希望能看到你的網格和其網格專案是如何佈局的。我建議你使用Firefox Nightly,並在Firefox 瀏覽器開發者工具中使用網格檢查器。如果你選擇一個網格,可以點選這個小網格圖示 —— 我喜歡把它想像成一個華夫餅(Waffle) —— 來顯示網格。

grid-inspector-preview-opt

Firefox已經在這方面做得很好了,而且Chrome也在著手在Chrome開發者工具中實現這方面的功能。

有關於在Firefox瀏覽器中怎麼使用網格檢查器來除錯網格佈局,可以閱讀以前翻譯的一篇文章《使用Firefox 網格檢查器除錯 CSS網格佈局》。

這對於我們所有人來說仍然是新東西

我很瞭解CSS網格規範,但是我從3月份就開始使用它了,就像其他人一樣。當我們從建立小示例開始,也可以說真正的在生產中開始推動Grid相關的規範,我們將開始尋找使用網格的新方法,當然還有新問題要解決!我很樂意看到你自己編寫的有關於網格相關的案例。在接下來的幾個月的時間裡,我還將在Smashing Magazine中深入探討網格佈局相關的問題。

相關文章