CSS网格中基于线的定位

探讨网格布局的基础概念的文章中,我们了解了如何用线编号布局。我们会在本文中全面探索这个基础特性的定义如何工作。

了解编号线是最好的探索网格布局方式的起点,当你使用网格布局时,编号线总是存在。这些线从1开始按行和列编号。要注意的是网格是根据书写方向来编号的。在像英文这样从左至右书写的语言中,网格线的1是最左边的线。当使用从右至左书写的语言时,网格线1就是最右边的线。我们会在之后的文章中深入探讨书写模式和网格间的交互。

基础例子

在这个非常简单的例子中,我们使用了3行3列的导轨,从而每个方向上我们有4条线。

网格容器中有4个子元素。如果我们不定义它们的位置,它们会按照自动定位规则布局,即前4个单元格中各1个元素。你可使用Firefox Grid Highlighter查看网格是如何定义行和列的。

Our Grid highlighted in DevTools

.wrapper {
   display: grid;
   grid-template-columns: repeat(3, 1fr);
   grid-template-rows: repeat(3, 100px);
}
<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>

 

按线编号为元素定位

我们可以使用基于线的定位控制这些元素在网格上的位置。比如我想要第一个元素从网格的最左开始,占1个列导轨。它还应该从第1行线开始,延伸至第4行线:

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

当你为某些元素定位,剩下的元素会继续按照自动定位规则定位。我们会在以后的文章中介绍这些规则,但现在你可以看到网格开始把未定位的元素放入空的单元格。

在逐个定义元素后,我们把所有4个元素都填充到了网格中。注意,我们可以留空一些单元格。网格布局的一个优势是:无需给元素周围加上margin来阻止文档流自动填补空白,就能实现设计中的留白区域。

<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box2 {
   grid-column-start: 3;
   grid-column-end: 4;
   grid-row-start: 1;
   grid-row-end: 3;
}
.box3 {
   grid-column-start: 2;
   grid-column-end: 3;
   grid-row-start: 1;
   grid-row-end: 2;
}
.box4 {
   grid-column-start: 2;
   grid-column-end: 4;
   grid-row-start: 3;
   grid-row-end: 4;
}

grid-column 和 grid-row 缩写

我们用了不少代码定义每个元素,你应该不会惊讶有 shorthand缩写。grid-column-startgrid-column-end属性可以合并为 grid-columngrid-row-startgrid-row-end则合并为grid-row

<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box1 {
   grid-column: 1 / 2;
   grid-row: 1 / 4;
}
.box2 {
   grid-column: 3 / 4;
   grid-row: 1 / 3;
}
.box3 {
   grid-column: 2 / 3;
   grid-row: 1 /  2;
}
.box4 {
   grid-column: 2 / 4;
   grid-row: 3 / 4;
}

默认跨度

在上例中,为了展示这些属性,我指定了每个结束行线和列线,但实际上如果一个元素只延伸一个轨道的话,你可以省略 grid-column-end 或 grid-row-end 值。元素默认延伸一个轨道。我们最初的例子的普通写法会是这样:

<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box1 {
   grid-column-start: 1;
   grid-row-start: 1;
   grid-row-end: 4;
}
.box2 {
   grid-column-start: 3;
   grid-row-start: 1;
   grid-row-end: 3;
}
.box3 {
   grid-column-start: 2;
   grid-row-start: 1;
}
.box4 {
   grid-column-start: 2;
   grid-column-end: 4;
   grid-row-start: 3;
}

当省略了跨越一个轨道的元素的/和第二个值,我们的缩写则会是这样

<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box1 {
   grid-column: 1 ;
   grid-row: 1 / 4;
}
.box2 {
   grid-column: 3 ;
   grid-row: 1 / 3;
}
.box3 {
   grid-column: 2 ;
   grid-row: 1 ;
}
.box4 {
   grid-column: 2 / 4;
   grid-row: 3 ;
}

grid-area属性

我们可以更进一步,给每个元素只定义一个属性 grid-area。值的顺序如下

  • grid-row-start
  • grid-column-start
  • grid-row-end
  • grid-column-end
<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box1 {
   grid-area: 1 / 1 / 4 / 2;
}
.box2 {
   grid-area: 1 / 3 / 3 / 4;
}
.box3 {
   grid-area: 1 / 2 / 2 / 3;
}
.box4 {
   grid-area: 3 / 2 / 4 / 4;
}

grid-area 的值的顺序看起来可能有点奇怪,比如说它正好和定义margin和padding的缩写的值的顺序相反。但如果说是因为它与CSS书写模式规范中的书写方向相关就不难理解了。我们会在之后的文章中探讨网格与书写模式的关系,但我们有4个书写流关联的方向:

  • 块起始 (block-start)
  • 块结束 (block-end)
  • 行起始 (inline-start)
  • 行结束 (inline-end)

我们现在的工作语言是英语,一种从左至右书写的语言。块起始 就是网格容器的顶端行线,块结束 就是底端行线。行起始 是左端列线,因为 行起始 与当前书写模式的起点一致,行结束 就是网格右端列线。

当我们用 grid-area 属性来定义网格区域时,我们先定义块起始(block-start) 和行起始(inline-start), 然后再定义块结束(block-end)和行结束(inline-end)。起初,我们可能会因为习惯了顶、右、底、左的属性定义顺序,觉得这个顺序奇怪,但当从网站的书写模式有多个方向的角度思考,这就显得合理了。

反方向计数

我们也可以从行和块结束线开始反方向计数,对于英语来说就是右端的列线和底端的行线。这些线会被记为-1,然后你可以从这往前数,所以倒数第2条线会被记为-2。值得注意的是最后一条线是指显式定义网格的最后一条线,即由grid-template-columns 和grid-template-rows定义的网格,并不把隐式定义网格的加入的行和列纳入考虑。

下面这个例子中,我通过从右端和底端开始定义布局,把之前的例子的布局翻转了

<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box1 {
   grid-column-start: -1;
   grid-column-end: -2;
   grid-row-start: -1;
   grid-row-end: -4;
}
.box2 {
   grid-column-start: -3;
   grid-column-end: -4;
   grid-row-start: -1;
   grid-row-end: -3;
}
.box3 {
   grid-column-start: -2;
   grid-column-end: -3;
   grid-row-start: -1;
   grid-row-end: -2;
}
.box4 {
   grid-column-start: -2;
   grid-column-end: -4;
   grid-row-start: -3;
   grid-row-end: -4;
}

使元素跨越整个网格

拥有从开始计数和从末尾计数这两种定位方法使得使一个元素跨越整个网格变得很方便:

.item {
  grid-column: 1 / -1;
}

间距

CSS网格规范加入了用 grid-column-gapgrid-row-gap属性定义列间距和行间距的能力。这两个属性的行为和多列布局中的column-gap属性很像。

间距只出现在网格轨道与轨道之间,它们并不会出现在网格容器的四周。通过在网格容器上定义这些属性,我们给上例加上了间距:

<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box1 {
   grid-column: 1 ;
   grid-row: 1 / 4;
}
.box2 {
   grid-column: 3 ;
   grid-row: 1 / 3;
}
.box3 {
   grid-column: 2 ;
   grid-row: 1 ;
}
.box4 {
   grid-column: 2 / 4;
   grid-row: 3 ;
}
.wrapper {
     display: grid;
     grid-template-columns: repeat(3, 1fr);
     grid-template-rows: repeat(3, 100px);
     grid-column-gap: 20px;
     grid-row-gap: 1em;
}

网格间距缩写

这两个属性可以用grid-gap简写。如果你只给出一个值,那这个值会同时应用于行间距和列间距。如果你给了两个值,第一个会被用于grid-row-gap,第二个则会被用于grid-column-gap.

.wrapper {
     display: grid;
     grid-template-columns: repeat(3, 1fr);
     grid-template-rows: repeat(3, 100px);
     grid-gap: 1em 20px;
}

从基于线定位的角度来说,间距的行为就像是使基线变得特别宽。所有从这条线开始的东西会从间距结束的地方开始,你并不能定位也不能放任何东西到这个间距的空间中。如果你希望得到行为和轨道行为相似的间距,你当然可以定义轨道来作为间距。

使用span关键字

除了”起始线与结束线“的定位方法,你还可以使用”起始线与跨越轨道数量“的定位方法

<div class="wrapper">
   <div class="box1">One</div>
   <div class="box2">Two</div>
   <div class="box3">Three</div>
   <div class="box4">Four</div>
</div>
.box1 {
   grid-column: 1;
   grid-row: 1 / span 3;
}
.box2 {
   grid-column: 3;
   grid-row: 1 / span 2;
}
.box3 {
   grid-column: 2;
   grid-row: 1;
}
.box4 {
   grid-column: 2 / span 2;
   grid-row: 3;
}

你也可以在grid-row-startgrid-column-start属性中使用span关键字。接下来两个例子会创建同样的网格。第一个例子中我们设定了起始行,然后说我们想结束线在跨越3个线之后。那这个元素就会从1号线开始,跨越3个线,到4号线结束。

.box1 {
    grid-column: 1;
    grid-row: 1 /  span 3;
}

第二个例子中,我们定义了结束行线,然后设置起始线为跨越3个线。意味着这个元素会从指定的线往上跨越3个线。这个元素会从4号线开始,跨越3个线到1号线。

.box1 {
    grid-column: 1;
    grid-row: span 3 / 4;
}

为了熟悉基于线的定位方式,你可以尝试用网格布置几个列宽不同的元素来实现一些常见布局。记住,如果你没有给所有元素定位,那些没被定位的元素就会按照自动定位规则定位。这么做的结果可能恰好和你的目标一样,但如果一些元素在你预料之外的地方出现,先检查一下你有没有给它定位。

再者,记住你明确定位的元素是可以互相重叠的。有时你能用这点创造一些有趣的效果,但有时也会因为你定位错了线而导致不正确的重叠。Firefox Grid Highlighter 可以在你学习时给你帮助,特别是当你的网格比较复杂时。

文档标签和贡献者