Web布局新系統:CSS Grid,Flexbox和Box Alignment

questions 7年前發布 | 13K 次閱讀 前端技術 flexbox

Web布局非常困難。它如此困難的原因是自從使用CSS來完成Web布局開始就并沒有真正的完成復雜的Web布局。雖然我們使用很多技術手段能實現Web的固定布局,但是這些方法在響應式設計中又出現很多局限性與不足。不過值得慶幸的是,我們有了Flexbox模塊,或許還有很多讀者已經開始使用CSS Grid和Box Alignment模塊做Web布局。

在這篇文章中,我將解釋如何將這些組合在一起使用,你會發現通過對Flexbox的理解,你也能更好的理解Grid布局。

瀏覽器支持性

CSS Grid布局目前僅在Google Chrome Canary、Webkit Safari、Firefox Nightly瀏覽器可以看到效果。如果你需要在Chrome、Safari、Firefox或者Opera瀏覽器中看到CSS Grid布局的效果,需要通過瀏覽器的 flag 或者 available 開啟開發者實驗屬性。我盡量在維護一個有關于瀏覽器對 CSS Grid布局的支持列表

display 的新屬性值

grid 和 flex 是 display 的兩個新屬性值。使用 display:flex 可以讓一個元素變成Flex容器,而使用 display:grid 可以讓一個元素變成Grid容器。

當我們這樣做的時候,Flex容器或Grid容器的子元素就分別變成了Flex項目和Grid項目。它們也直接使用了Flex或Grid項目的初始值。

display:flex

在第一個示例中,設置了 display:flex 的容器有三個子元素。我們需要做的是使用Flexbox。

除非給Flex容器添加不同的屬性,不然就會使用Flex容器的初始值(默認值):

  • flex-direction:row
  • flex-wrap: no-wrap
  • align-items: stretch
  • justify-content: flex-start

Flex項目的初始值:

  • flex-grow: 1
  • flex-shrink: 0
  • flex-basis: auto

本文后面的內容將會介紹這些屬性和值是如何工作的。現在,你需要做的就是在父容器中設置 display:flex ,Flexbox就會開始工作:

有關于Flexbox更多的介紹,可以點擊這里。

display: grid

使用 display: grid 聲明一個網格容器。以便我們可以看到網格的渲染行為,這個例子中拿了五個 .card 做為示例。

只顯式設置 display: grid 并不會有明顯的效果,但是Grid容器的所有子元素都變成了Grid項目。通過它們渲染成單列多行(隱式的行)的一個網格,

我們可以進一步的使用網格特性創建一些列,讓它看起來更像一個網格。這里我們使用 grid-template-rows 屬性。

在下一個示例中,使用一個新的單位創建一個三列等寬的網格。 fr 單位可以讓可用空間按一個的比例進行劃分。你可以看到我們的網格已經創建了,第個單元格顯式定義為網格的列,但網格的行還是隱式的創建。當網格的單元格填滿網格容器時,單元格會自動創建新的一行,讓更多的網格單元格可以排列。

同樣的,它們也有一些默認的行為。我們還沒有顯式的給網格項目設置對應的屬性時,網格的每個單元格將會按自己的默認行為在網格中排列。網格容器和網格項目都具有自身的初始值。下面是網格容器的初始值:

  • grid-auto-flow: row
  • grid-auto-rows: auto
  • align-items: stretch
  • justify-items: stretch
  • grid-gap: 0

這些初始值意味著我們的網格項目放置在網格的每個單元格中,并且一行一行的排列。因為我們有一個三列的網格,第三列后面的單元格將會延伸到下一行(創建新的一行)。行的尺寸是自動計算的,所以它將會自動伸縮以用來適應內容。網格項目將在兩個方向進行延伸(橫向和縱向),用以填充網格區域。

有關于Grid更多的介紹,可以點擊這里。

Box Alignment

在前面的兩個簡單示例中,已經看到了盒子對齊模塊(Box Alignment Module)的身影。Box Alignment Level 3規范本質上它義了Flexbox容器里項目分布方式(也可以用于其他模塊)。也就是說,如果你已經在使用Flexbox的話,那么你其實已經開始在使用盒子對齊模塊相關屬性。

接下來我們一起看看Flexbox和Grid模塊中是怎么使用Box Alignment Module,它又能幫我們解決什么樣的問題。

等高列

有的時候使用老式的表格布局可以很容易創建等高列,但是使用定位和浮動布局就極難實現等高列。下面提出的例子,我們的 .card 里面包含了不同的內容。我們沒有辦法讓其它的 .card 和第一個 .card 有一樣的高度,因為它們彼此之間沒有關系。

當我們給父容器顯式的設置了 display 的屬性值為 grid 或 flex 時,它們的子元素就有了互相的關系。這種關系可以很好的使用Box Alignment Module的屬性,可以很簡單的使每列的高度相等。

在下面的Flexbox的示例中,我們的每個Flex項目有不同的內容。雖然每個基線上都有背景顏色,但它的渲染效果并不像浮動元素。這些Flex項目顯示在一行,并且通過 align-items 屬性來控制項目對齊方式。主要使用這個屬性的初始值(默認值) stretch 來拉伸每個項目,從而創建等高列效果。

在網格布局中我們看到了相同的效果。下面是一個簡單的網格布局,包含了兩列,一個側邊欄和主要內容。我們再次使用了 1fr 這個單位。側邊欄使用可用空間的 1fr ,而主內容使用可用空間的 3fr 。側邊欄背景顏色和主內容底部對齊。再一次使用了 align-items 的默認值 stretch 。

Flexbox中的對齊

我們已經了解了Grid和Flexbox中如何使用 align-items 的默認值 stretch 。

在Flexbox中,當我們使用 align-items 屬性,可以控制Flex項目在Flex容器側軸方向上的對齊方式。不過我們可以使用 flex-direction 屬性來定義Flex項目在Flex容器主軸上的對齊方式。在第一個示例中,主軸是 row ,Flex項目的高度會延伸到Flex容器的高度。在這種情況,Flex容器的高度是由Flex項目內容最多的一項來決定。

我們可以顯式的給Flex容器定義一個高度。

還可以使用其他值為替代默認的 stretch 值:

  • flex-start
  • flex-end
  • baseline
  • stretch

使用 justify-content 控制主軸上的對齊方式。其默認值是 flex-start ,這也就是說,為什么在示例中看到的效果,Flex項目都是左對齊。除此之外,我們還可以使用下列的值:

  • flex-start
  • flex-end
  • center
  • space-around
  • space-between

共中 space-around 和 space-between 尤其有趣。使用 space-between 可以很好的讓Flex項目之間的間距可以得到均勻的分布。

space-around 也有點類似,除了第一個和最后的兩個Flex項目,其他相鄰之間的Flex項目可以均勻分布Flex容器中剩余下的空間。

Codepen中的這個示例可以看到這些屬性和值的使用效果:

我們也可以把Flex項目不按行的方式排列,而是按列的方式排列。如果把 flex-direction 設置為 column ,Flex容器的主軸就變成列,而側軸就變成了行。 align-items 的默認值依舊是 stretch ,并且Flex項目在側軸(行的方向)拉伸占滿容器的寬度。

如果想讓項目從Flex容器開始位置對齊,我們可以使用 flex-start 。

也可以使用 justify-items ,它包括 space-between 和 space-around 。容器需要有一個足夠的高度,你才能看到相應的效果。

Grid布局中的對齊

在網格布局中,渲染行為有點類似,除此之外還可以定義網格區域內網格項目的對齊方式。在Flexbox中,我說討論的是主軸(Main Axis)和側軸(Cross Axis);在Grid中,使用 block 或者列軸( Column Axis )來定義列,使用 inline 或者行軸( Row Axis )來定義行。有關于這方面的詳細介紹,可以閱讀相關的 規范文檔

我們可以使用盒子對齊模塊(Box Alignment)規范中定義的屬性和值來控制網格區域內內容的對齊方式。

網格區域是指一個或多個單元格。在下面的示例中,有一個四行四列的網格。軌道之間有一個默認的 10px 間距,并且基于網格線創建了三個網格區域。后面的我們會介紹怎么使用網格線定義網格的區域,這里使用 / 符,前面的值表示開始,后面的值表示結束。

虛線邊框在一個背景圖像上,能更好的幫助我們看到定義的網格區域。因此,在第一個示例中,每個區域的 align-items (列軸方向)和 justify-items (行軸方向)都使用了其默認值 stretch 。這也意味著,內容區域將會自動延伸填充整個網格區域。

在第二個示例中,我把網格容器的 align-items 值改為 center 。我們也可以在每個網格項目上設置 align-self 值。在這個示例中,我在所有網格項目上設置的 align-self 的值為 center ,但在第二個item上依舊設置的是 stretch 。

在第三個示例中,我改變了 justify-items 和 justify-self 的值,分別設置為 center 和 stretch 。

在上面的示例中,了解了怎么控制網格區域的內容對齊方式以及如何通過開始網格線和結束網格線定義網格的區域。

我們也可以在網格容器上控制網格內元素的對齊方式。在本例中,我們使用了類似于Flexbox中的 align-content 和 justify-content 兩個屬性。

在第一個示例中,我們看到網格的對齊模式是默認的,網格的行和列定義了絕對單位,并且占用了較少的容器空間。默認值( align-content 和 justify-content )是 start 。

如果把值換成 end ,網格軌道就會移動到右下角。

和Flexbox類似,我們可以使用 space-around 和 space-between 兩個值。這可能會導致一些我們并不想要的行為,比如說改變 grid-gap 的值。下圖就是Codepen中的第三個示例,看到的效果類似于Flexbox中一樣,網格間距會隨著容器可用空間做出變化。

除此之外,如果網格軌道跨越多個間距時,固定大小的網格軌道將會獲得額外的空間。比如示例中的第二個元素和第四個元素明顯的變得更寬,第三個元素更高。因為它們有額外的空間分配到間網格的間距中(合并單元格)。

也可以把這兩個屬性的值都設置為 center 。看到的效果就類似于最后一個示例。

在Flexbox和Grid中我們能很好的控制元素對齊,而且他們的工作方式普遍一致。在響應式中可以通過調整個別項目或組項目組來防止它們之間的重疊。

默認響應式

上一節中主要了解了對齊方式。Box Alignment屬性是Grid和Flexbox布局領域的一部分,相關的規范也出來了,那么在響應式方面它是如何做的呢?比如其值 space-between 、 space-around 和 stretch 的響應式能力如何?

除此之外,還有更多。響應式設計往往是要保持一定比例的。當我們計算列的響應式設計,一般是按這樣的公式來計算 target ÷ context 。@Ethan Marcottes在介紹 流式網格的文章 中有提到,我們要操持絕對寬度設計的原始比例。Flexbox和Grid布局可以讓我們使用更簡單的方式來處理設計中的比例。

Flexbox可以讓我們更靈活的控制內容。當我們使用 space-between 值時,容器可用空間可以均勻的分配到Flex項目上。首先,Flex項目會按所占用的空間進行計算,然后容器所剩余的空間(可用空間)將會均勻的劃分到每一個Flex項目中。為了更好的控制Flex項目,我們還可以使用下面的一些屬性:

  • flex-grow
  • flex-shrink
  • flex-basis

這三個屬性簡寫起來對應的就是 flex 。如果在Flex項目中設置 flex: 1 1 300px ,那么 flex-grow 的值為 1 ,表示Flex項目可的擴展; flex-shrink 的值為 1 ,表示Flex項目可以縮小; flex-basis 的值為 300px (相當于Flex項目在分配Flex容器剩余空間之前的一個默認尺寸)。如果把這個運用到卡片上的布局,就能看到類似下面這樣的效果:

這里一行有三張卡片,而且 flex-basis 的值是 300px 。如果Flex容器的寬度大于 900px ,那么剩余的空間將會分成三等份,分布到每一個Flex項目中。這是因為我們設置了 flex-grow:1; ,以便Flex項目可以擴大。同時,也設置了 flex-shrink 的值為 1 ,這也意味著,如果可用的空間分配給三個Flex項目不到 300px 時,那么每個Flex項目空間也會被收縮。

如果我們想要讓不同的Flex項目擴展的比例不同,只需要在不同的Flex項目中設置 flex-grow 的不同值。比如,想讓第一個Flex項目可用空間是分布空間的三倍,我們只需要設置 flex-grow:3; 。

有效空間是在 flex-basis 之后做的分配。這也就是為什么第一個Flex項目不是其他Flex項目的三倍(寬度),而僅僅是占容器剩余空間的三份。如果 flex-basis 設置為 0 ,你可以看到一個很大的變化,比如下面的示例,Flex項目寬度是按比例分配容器的寬度。

有一個 Flexbox的測試工具 ,可以很好的幫助你理解這些值。在測試工具的輸入框中輸入不同的值,它會根據你輸入的值做一些變化,并且告訴詳細告訴你計算的一個過程。

如果你使用 auto 做為 flex-basis 的值,它將使用任何值作為 flex-basis 值,設置在每個Flex項目上。如果沒有顯式的設置大小值,那么其默認值是和內容寬度一樣。因此,使用 auto 是非常有用的,可以用于可重用的組件中,你可能只需要在一個Flex項目中設置大小。你可以使用 auto 值和在需要明確定義大小的Flex項目中設置值,Flexbox會根據它們做相應的計算。

在接下來的示例中,我設置了 flex-basis 的值為 auto 。然后再把第一張卡片的寬度設置為 350px 。現在第一張卡片的 flex-basis 就變成了 350px ,用于解決如何分配空間。另外兩張卡片的 flex-basis 就是基于其內容的寬度。

如果我們回到最初設置的 flex: 1 1 300px; ,并且在Flex容器上添加 flex-wrap:wrap; ,那么Flex項目會保持盡可能的靠近 flex-basis 的值。如果我們有五張卡片,那么第一行會放置三個上卡片,接下來的一行放置兩張卡片。Flex項目也會擴展,使我們得到兩個相同寬度的卡片。如果縮小瀏覽器的視窗寬度,可以看到每行兩張卡片,最后一張卡片放在第三行,而且寬度變成和容器一樣大小。當你繼續縮小視窗,可以看到每個卡片占據一行,寬度和容器寬度一樣。

我們經常會問一個問題“我們怎么把最后面的一個Flex項目排到最前面,而且最底部還留一個間距?” 答案是,Flexbox沒辦法做到。這樣的效果需要一個Grid布局。

網格布局中的比例

對于網格布局,正如我們前面看到的,它有一個概念,可以創建列和行的網格軌道用于網格項目的定位。當我們創建一個靈活的網格布局時,可以在網格容器上設置網格軌道的比例,而不是像Flexbox在Flex項目中設置比例。我們可以使用 fr 來創建網格單元格。這個單位工作方式類似于 flex-basis 值為 0 時的 flex-grow 方式。它可以將網格容器中可用空間分配到網格項目中。

在下面的這個示例中,第一個網格軌道設置 2fr ,其他兩個軌道設置的都是 1fr 。也就是說,把網格容器分成了四份,其中第一個網格軌道占了兩份,其他兩個網格軌道各占一份。

在網格布局中,絕對單位和 fr 單位混合使用都是有效的。比如下面的這個示例,我們有一個 2fr , 1fr 和 300px 的網格軌道。首先網格容器空間將會剔除絕對寬度的網格軌道,然后將網格容器剩余的空間分成三等分,其中 2fr 的網格軌道占兩份, 1fr 的網格軌道占一份。

從示例中你可以看到,網格項目填充到了定義好的網格軌道中,而不像Flexbox中的示例會自動分配行。這主要是因為,網格布局創建了一個二維布局,然后在把網格項目放進來。Flexbox,讓我們根據內容適合度來控制,看多少適合在一個行或列的維度中放置,然后將額外的放到另一行或列。

什么都好,如果有一個方法可以在容器中盡可能的放置列,那將更完美。我們可以使用 grid 和 repeat() 。

在下一個示列中,我們將使用 repeat() 語法在容器中創建盡可能多的 200px 列。我使用 repeat ,并且給其設置關鍵詞 auto-fill 和具體的尺寸( 200px )來創建我想要重復的網格軌道的大小。

我們可以做得更好一點,把 fr 單位和絕對寬度結合起來,告訴創建的網格,盡可能多的放置 200px 的網格軌道(網格容器中放置盡可能多的接近 200px 的網格項目)。

簡單的解釋一下,給 repeat 設置了一個 auto-fill 值,告訴網格容器盡可能的填充網格項目, repeat 的第二個值 minmax(200px, 1fr) 是用來控制網格項目的寬度的。首先會根據網格容器的寬度進行計算,在容器中盡可能的放置更多個 200px 的網格項目,如果網格容器放置了盡可能多的 200px 網格項目之后,還有剩余空間,那么將會把網格容器剩余空間平均分配到每一個網格項目中。

使用這種方法布局,我們可以得到一個二維布局,其好處是具有靈活的軌道(行或列),因此不需要任何的媒體查詢。這里我們也看到了Grid和Flexbox的不同之處。Flexbox是一維的布局,而Grid是二維的布局。

分離源順序和視覺顯示

在Flexbox中,對于Flex項目定位并不能做得很好。我們可以通過設置 flex-direction 的值為 row 、 row-reverse 或者 column 、 column-reverse 來控制Flex項目的排列方向。設置 order 的值來設置Flex項目的順序位置。

在網格布局中,我們可以準確的定義網格項目的位置。在上面的示例中,我們一直依賴于網格的自動排列規則定義網格項目。在下面的示例中,我們將基于網格線來定義網格項目在網格中的位置。

grid-column 和 grid-row 屬性分別是 grid-column-start , grid-column-end 和 grid-row-start , grid-row-end 的簡寫屬性。其值使用 / 來分隔,其中 / 前面的值表示開始的值,后面的值表示結束的值。

你也可以創建網格線名稱。當你創建網格容器的時候就創建了網格線,這些網格線你使用名稱來命名,然后在網格項目中使用聲明好的網格線名稱,而不是使用網格線的索引值。

還可以給多個網格線命名相同的名稱,然后通過網格線的名稱和索引值一起使用。

還可以使用 span 關鍵詞,用來合并多個行或列。這種方式用于創建組件放在不同的位置非常有用。在下面的示例中,我想創建的一些元素中合并六個列,其他的合并三個列。我將使用自動排列來控制網格項目。當碰到類名為 .wide 的,開始的值為 auto ,結束的值為 span 2 。因此,開始于正常的網格線上,然后根據自動排列的規則,合并兩個列。

使用自動排列的規則,在網格布局中有可能會造成一些空白網格。默認情況之下,網格一旦留有缺口,其它網格項目是無法填充進去的。除非將 grid-auto-flow 設置為 dense 。這種情況下,網格項目就會自動填充上去。

除了前面介紹的方法之外,還可以通過 grid-template-areas 的值來創建一個可視的網格布局。如果要做到這一點,首先需要知道網格容器子元素(網格項目)放置在哪個位置。

更有意思的是,也可以把 grid-template-areas 的值命名為ASCII碼。如果你想基于媒體查詢重新定義布局,只需要在不同的媒體查詢條件下改變這個屬性的值。

從這個示例中,你可以看出來,如果沒有的地方,我們采用一個或者一個系列的 . 來描述,表示它們之間沒有空格。讓一個元素可以起到合并單元格的作用。

重新排列影響可訪問性

不管是在Flexbox布局中還是Grid布局中,都需要非常小心的避開使用這些方法來重新給內容排序。 Flexbox規范 中是這樣描述的:

Authors must use order only for visual, not logical, reordering of content. Style sheets that use order to perform logical reordering are non-conforming.

Grid中是這樣做出描述 的:

Grid layout gives authors great powers of rearrangement over the document. However, these are not a substitute for correct ordering of the document source. The order property and grid placement do not affect ordering in non-visual media (such as speech). Likewise, rearranging grid items visually does not affect the default traversal order of sequential navigation modes (such as cycling through links).

在這兩種情況之下,到目前為止,重新排序都只是視覺上的效果,它并不會改變文檔流的邏輯順序(默認順序)。此外,我們需要小心用戶訪問頁面的時候使用鍵盤。原因非常的簡單,改變順序有可能讓你的用戶在訪問的時候,突然有錯亂的順序。比如人家在訪問導航,你改變順序之后,有可能把人家直接帶到了頁面的底部。

布局的新系統

只通過一篇文章沒辦法涵蓋到Grid和Flexbox的方方面面。我的目的是只是展示了他們之間的異同。從而證明,這些規范可能讓我們構建一個全新的布局系統,能讓我們更好的建設一個網站或者應用程序。

目前我們使用較多的是Flexbox,但Grid也將慢慢的得到支持。Flexbox最初出現的時候,開發人員用于生產需要添加瀏覽器前綴。目前為止對于Grid要開啟瀏覽器實驗性選項,你就可以看到對應的效果。對于Grid規范現在處于候選人推薦狀態。當Grid在明年年初得到更友好的支持時,它將是處于一個跨瀏覽器兼容狀態。

除了文章中的示例之外,下面還有一大堆的詳細資料可以供你參考。我特別喜歡寫這方面的示例,實現各式各樣的布局效果。如果你有其他特殊的布局,可以告訴我,我非常喜歡接受這方面的挑戰。

 

 

來自:http://www.w3cplus.com/css/css-grids-flexbox-and-box-alignment-our-new-system-for-web-layout.html

 

 本文由用戶 questions 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!