CSS 网格布局作为一个完善的布局系统的一部分,被设计为能和CSS的其它部分协同合作。在这个指南中,我会向你解释网格布局是如何与你正在使用的其它技巧协同合作的。
Grid and flexbox
CSS网格布局和弹性盒布局的主要区别在于弹性盒布局是为一维布局服务的(沿横向或纵向的),而网格布局是为二维布局服务的(同时沿着横向和纵向)。这两个规格有一些相同的特性。如果你已经掌握如何使用弹性盒布局的话,你可能会想知道这些相似之处怎样在能帮助你掌握网格布局。
一维布局 vs. 二维布局
一个简单的例子可以展示一维和二维布局的区别。
这个例子中我用弹性盒布置了一些盒子。容器中有5个元素,它们被设置了伸缩属性,在200px基准上伸缩。
我还设置了flex-wrap
属性,从而当容器变得太窄时,元素会换到新的一行。
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> <div>Four</div> <div>Five</div> </div>
.wrapper { display: flex; flex-wrap: wrap; } .wrapper > div { flex: 1 1 200px; }
你可以看到有两个元素被换到了新行。这两个元素分享了这行的可用空间,并没有与上一行的元素对齐。这是因为当你允许弹性元素换行时,每个新行都变成了一个新的弹性容器。空间分布只在行内进行。
从而产生了一个常见问题:如何能让这些元素对齐?这时你需要一种二维布局的方法,你希望通过行和列来控制对齐,是时候该网格登场了。
用CSS网格达成同样布局
下例中我用网格创建同样的布局。这次我们有3个1fr的列轨道。我们并不需要给这些子元素设置任何属性,它们会自动按顺序填充到网格的单元格中。你可以看到它们按网格规整的排列,行与行、列与列对齐。当有5个子元素时,第二行的尾部会留出一个空隙。
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> <div>Four</div> <div>Five</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(3, 1fr); }
当抉择该用网格还是弹性盒时,你可以问自己一个简单的问题:
- 我只需要按行或者列控制布局?那就用弹性盒子
- 我需要同时按行和列控制布局?那就用网格
从内容出发还是从布局入手?
除了一维和二维布局之间的区别,还有一种办法决定该使用弹性盒还是网格来布局。弹性盒从内容出发。一个使用弹性盒的理想情形是你有一组元素,希望它们能平均地分布在容器中。你让内容的大小决定每个元素占据多少空间。如果元素换到了新的一行,它们会根据新行的可用空间决定它们自己的大小。
网格则从布局入手。当使用CSS网格时,你先创建网格,然后再把元素放入网格中,或者让自动放置规则根据把元素按照网格排列。我们能创建根据内容改变大小的网格轨道,但整个轨道的大小都会随之改变。
当你使用弹性盒,并发现自己禁用了一些弹性特性,那你可能需要的是CSS网格布局。例如,你给一个弹性元素设置百分比宽度来使它和上一行的元素对齐。这种情况下,网格很可能是一个更好的选择。
盒对齐
对于很多人来说,弹性盒特性最让人激动的一点是我们第一次拥有了正当的对齐能力。这让一个盒子在页面里中心对齐变得简单。弹性元素还可以使自己延伸到和容器等高,意味我们能实现等高列。长久以来,我们都想实现这些行为,并一直在用一些所谓“技巧”去达到起码的视觉效果。
弹性盒特性已经被加入到新规范盒 Box Alignment Level 3。意味它们能被用在包括网格布局的其它规范中。它们未来也可能被用在其他的布局方法中。
本系列稍晚的指南中我会正式介绍盒对齐(Box Alignment)和它在网格布局中是如何工作的。不过现在我们先看一个比较弹性盒和网格的简单例子。
在这第一个例子中,我使用了弹性盒,一个容器内有三个元素。wrapper设定了 min-height
,确定了弹性容器的高度。我还在弹性容器中定义了align-items
: flex-end, 因此子元素会从弹性容器尾部开始排列。我还在box1上定义了
align-self
属性,这会覆盖默认值,使它延长至容器的高度;box2上也定义了
align-self
,所以它会与弹性容器的起点对齐。
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3">Three</div> </div>
.wrapper { display: flex; align-items: flex-end; min-height: 200px; } .box1 { align-self: stretch; } .box2 { align-self: flex-start; }
CSS网格中的对齐
这个第二个例子使用了网格创建一个同样的布局。这次我们使用了应用于网格布局的盒对齐属性。因此我们是相对网格而不是弹性盒的起始对齐。对于网格来说,我们是让元素在它们各自的网格区域中对齐。在本例中就是一个单元格,但它也可能是多个单元格组成的一个区域。
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3">Three</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(3,1fr); align-items: end; grid-auto-rows: 200px; } .box1 { align-self: stretch; } .box2 { align-self: start; }
fr单位和flex-basis属性
我们已经看到了fr单元如何在网格容器中分配一定比例的可用空间到我们的网格轨道。当与minmax()
函数结合使用时,fr单元可以给我们与flexbox中的flex属性非常相似的行为 - 同时仍然可以创建二维布局。
If we look back at the example where I demonstrated the difference between one and two dimensional layouts, you can see there is a difference between the way the two layouts work responsively. With the flex layout, if we drag our window wider and smaller, the flex box nicely deals with adjusting the number of items in each row according to the available space. If we have a lot of space all five items can fit on one row, if we have a very narrow container we might just have space for one.
In comparison, the grid version always has three column tracks. The tracks themselves will grow and shrink, but there are always three as we asked for three when defining our grid.
Auto-filling grid tracks
We can create a similar effect to flexbox, while still keeping the content arranged in strict rows and columns, by creating our track listing using repeat notation and the auto-fill
and auto-fit
properties.
In this next example I have used the auto-fill
keyword in place of an integer in the repeat notation and set the track listing to 200 pixels. This means that grid will create as many 200 pixels column tracks as will fit in the container.
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(auto-fill, 200px); }
A flexible number of tracks
This isn’t quite the same as flexbox. In the flexbox example the items are larger than the 200 pixel basis before wrapping. We can achieve the same in grid by combining auto-fill
and the minmax()
function. In this next example I create auto filled tracks with minmax
. I want my tracks to be a minimum of 200 pixels, and I then set the maximum to be 1fr
. Once the browser has worked out how many times 200 pixels will fit into the container–also taking account of grid gaps–it will treat the 1fr
maximum as an instruction to share out the remaining space between the items.
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
We now have the ability to create a grid with a flexible number of flexible tracks, but to see items laid out on the grid aligned by rows and columns at the same time.
Grid and absolutely positioned elements
Grid interacts with absolutely positioned elements, which can be useful if you want to position an item inside a grid or grid area. The specification defines the behavior when a grid container is a containing block and where the grid container is a parent of the absolutely positioned item.
A grid container as containing block
To make the grid container a containing block you need to add the position property to the container with a value of relative, just as you would to make a containing block for any other absolutely positioned items. Once you have done this, if you give a grid item position:
absolute
it will take as its containing block the grid container or, if the item also has a grid position, the are of the grid it is placed into.
In the below example I have a wrapper containing four child items, item three is absolutely positioned and also placed on the grid using line-based placement. The grid container has position:
relative
and so becomes the positioning context of this item.
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3"> This block is absolutely positioned. In this example the grid container is the containing block and so the absolute positioning offset values are calculated in from the outer edges of the area it has been placed into. </div> <div class="box4">Four</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(4,1fr); grid-auto-rows: 200px; grid-gap: 20px; position: relative; } .box3 { grid-column-start: 2; grid-column-end: 4; grid-row-start: 1; grid-row-end: 3; position: absolute; top: 40px; left: 40px; }
You can see that the item is taking the area from grid row line 2 to 4, and starting after line 1. Then it is offset in that area using the top and left properties. However, it has been taken out of flow as is usually for absolutely positioned items and so the auto-placement rules now place items into the same space. The item also doesn’t cause the additional row to be created to span to row line 3.
If we remove position:
absolute
from the rules for .box3
you can see how it would display without the positioning.
A grid container as parent
If the absolutely positioned child has a grid container as a parent but that container does not create a new positioning context, then it is taken out of flow as in the previous example. The positioning context will be whatever element creates a positioning context as is common to other layout methods. In our case if we remove position:
relative
from the wrapper above, positioning context is from the viewport, as shown in this image.
Once again the item no longer participates in the grid layout in terms of sizing or when other items are auto-placed.
With a grid area as the parent
If the absolutely positioned item is nested inside a grid area then you can create a positioning context on that area. In the below example we have our grid as before but this time I have nested an item inside .box3
of the grid.
I have given .box3
position relative and then positioned the sub-item with the offset properties. In this case the positioning context is the grid area.
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .wrapper > div { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; }
<div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3">Three <div class="abspos"> This block is absolutely positioned. In this example the grid area is the containing block and so the absolute positioning offset values are calculated in from the outer edges of the grid area. </div> </div> <div class="box4">Four</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(4,1fr); grid-auto-rows: 200px; grid-gap: 20px; } .box3 { grid-column-start: 2; grid-column-end: 4; grid-row-start: 1; grid-row-end: 3; position: relative; } .abspos { position: absolute; top: 40px; left: 40px; background-color: rgba(255,255,255,.5); border: 1px solid rgba(0,0,0,0.5); color: #000; padding: 10px; }
Grid and display:
contents
A final interaction with another layout specification that is worth noting is the interaction between CSS Grid Layout and display:
contents
. The contents
value of the display property is a new value that is described in the Display specification as follows:
“The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced with its children and pseudo-elements in the document tree.”
If you set an item to display:
contents
the box it would normally create disappears, and the boxes of the child elements appear as if they have risen up a level. This means that children of a grid item can become grid items. Sound odd? Here is a simple example. In the following markup I have a grid, the first item on the grid is set to span all three column tracks. It contains three nested items. As these items are not direct children, they don’t become part of the grid layout and so display using regular block layout.
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .box { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; } .nested { border: 2px solid #ffec99; border-radius: 5px; background-color: #fff9db; padding: 1em; }
<div class="wrapper"> <div class="box box1"> <div class="nested">a</div> <div class="nested">b</div> <div class="nested">c</div> </div> <div class="box box2">Two</div> <div class="box box3">Three</div> <div class="box box4">Four</div> <div class="box box5">Five</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: minmax(100px, auto); } .box1 { grid-column-start: 1; grid-column-end: 4; }
If I now add display:
contents
to the rules for box1
, the box for that item vanishes and the sub-items now become grid items and lay themselves out using the auto-placement rules.
* {box-sizing: border-box;} .wrapper { border: 2px solid #f76707; border-radius: 5px; background-color: #fff4e6; } .box { border: 2px solid #ffa94d; border-radius: 5px; background-color: #ffd8a8; padding: 1em; color: #d9480f; } .nested { border: 2px solid #ffec99; border-radius: 5px; background-color: #fff9db; padding: 1em; }
<div class="wrapper"> <div class="box box1"> <div class="nested">a</div> <div class="nested">b</div> <div class="nested">c</div> </div> <div class="box box2">Two</div> <div class="box box3">Three</div> <div class="box box4">Four</div> <div class="box box5">Five</div> </div>
.wrapper { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: minmax(100px, auto); } .box1 { grid-column-start: 1; grid-column-end: 4; display: contents; }
This can be a way to get items nested into the grid to act as if they are part of the grid, and is a way around some of the issues that would be solved by subgrids once they are implemented. You can also use display:
contents
in a similar way with flexbox to enable nested items to become flex items.
As you can see from this guide, CSS Grid Layout is just one part of your toolkit. Don’t be afraid to mix it with other methods of doing layout, to get the different effects you need.