0
点赞
收藏
分享

微信扫一扫

内容布局(四):Grid布局

耽搁了好久一直没写 Grid 布局,主要是写布局的文章太累人?。这期就朝花夕拾,写写 Grid layout 的入门教程。

Grid Basic

Grid layout 翻译过来叫网格布局,我先介绍一下 Grid 里的几个基本术语:

Grid Container

网格容器就是被设置为display: grid的元素,通俗来说就是所有网格的最外层:

.grid-container {
  display: grid;
}

Grid Items

网格项就是网格容器里的每一个子元素。如下所示,headerasidemainfooter就是grid-container的网格项。(p.s. 孙子元素不会受到祖先网格属性的影响)

<div class="grid-container">
  <header></header>
  <aside></aside>
  <main>
      <!-- doesn’t effected!!! -->
      <div class="grandson"></div>
      <!-- doesn’t effected!!! -->
  </main>
  <footer></footer>
</div>

Grid Columns

既然是网格,自然有列和行的概念,我们先说“列”。网格容器添加grid-template-columns属性便可激活列属性:下方示例中,我们把网格项排成了两列。实现上只需把两列的长度(200pxauto)从左至右枚举出来即可:

.grid-container {
  display: grid;
  grid-template-columns: 200px auto;
}

除了利用单位或 auto 来指定每一列的长度,我们也可以让列按一定比例排布。下方示例中,实现了左右两列 1:2 比例的布局——只需在grid-template-columns指定1fr2fr即可。

.grid-container {
  display: grid;
  grid-template-columns: 1fr 2fr;
}

p.s. fr 是 franction 的缩写

Grid Rows

同理,grid-template-rows会激活行属性,也就是用来定义每一行的高度,属性同样可以是基本单位(px,rem,%),也可以是fr比例:

.grid-container {
  display: grid;
  grid-template-columns: 1fr 2fr;
  grid-template-rows: 100px 200px;
}

Grid Gaps

若想为这些排布的 items 添加间距(槽),可以使用grid-gap属性。单独定制横轴或纵轴的间距,还有grid-column-gapgrid-row-gap这两个属性。

.grid-container {
  display: grid;
  grid-template-columns: 1fr 2fr;
  grid-template-rows: 100px 200px;

  grid-column-gap: 10px;
  grid-row-gap: 10px;
  /* or in simplified form */
  grid-gap: 10px;
}

Grid Cells & Gird Lines

grid-template-columnsgrid-template-rows排布后,横轴和纵轴交汇,就形成如下所示的单元格(grid cell)和标注为 1、2、3... 的网格线(grid line)。这里我提一下,cell 和 item 是不一样的概念:cell 是网格的基本单元,而 item 是可以横跨多个 cell 的 DOM 元素。

Grid Layout

上文我们介绍了几个网格布局的基本术语;若是到此为止,那也顶多是加强版的 table 布局罢了。而网格布局的真正特别之处是在 grid cell 基础上的网格定位。

我们还是从传统布局的弊端说起,下图是一种很常见的页面布局。

这种布局实现上很直白:先把整体分成三行,再把中间那行分成两列。HTML 便签大体如下所示:

<body>
  <header class="row"></header>
  <div class="row">
    <aside class="column-aside"></aside>
    <main class="column-main"></main>
  </div>
  <footer class="row"></footer>
</body>

实现很简单,就不写 CSS 了;只是在语义标签层面上,headerfooterasidemain 理应是同级;但为了布局方便不得不把 asidemain 做成了孙子节点,有点怪怪的。不过整体也没太大问题。如果再成换更复杂的布局呢,比如,回字形?

用传统布局技术(如 flex)实现回字形,代码量会立马暴涨。更糟糕的是,这种排版将牺牲掉所有语义标签——你的关注点只会在各种层层嵌套的 div 上了。

Grid Position

网格布局能帮到什么呢?我前文也提到过,Grid items 可以横跨多个 Grid cells,就从这里入手。

我们再看看上面那个回字形布局,本质上不就是个九宫格嘛?我们用 repeat 方法给网格容器写一个 3×3 的 cell 九宫格:

.grid-container {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
}

最后只要把网格项 header 覆盖在第 1 和第 2 个 cell 上、aside 在 4 和 7 上、main 在 3 和 6、footer 在 8 和 9 上,即可完成布局。

那网格布局怎么为网格项定位这些 cell 呢?用坐标呀!从 (grid-column-start, grid-row-start)(grid-column-end, grid-row-end) 所在的矩形区域来定位网格项的排布。横坐标值就是 Y 轴(Column Grid Line)所显示的数值,纵坐标就是 X 轴(Row Grid Line)所显示的数值,起始数值都是 1。

上图所示,header 位于 (1,1)(3,2) 这块矩形区域内,我们为 header 添加如下 CSS 属性;header 便会自动定位到左上角那块粉红色区域了。

header {
  grid-column-start: 1;
  grid-row-start:  1;
  grid-column-end: 3;
  grid-row-end: 2;
}

其他几块区域的布局我就不写出来了,大家有兴趣的话自己算一下坐标即可。

Grid Areas

除上面这种二维坐标定位的方式,CSS 网格布局还提供了一种更无脑的定位方式——grid-template-areas——可以为网格设置区域模版,比如上面的回字形布局模版就如下所示:

.grid-container {
  display: grid;
  grid-template-areas:
    "h h m"
    "a . m"
    "a f f";
}

这个模版我写得很简单,解释一下:就是由九个字符及空格所示意的九宫格模版。以左上角 h h 为例,两个相邻的 h 组成了一个所谓的“命名网格区”,表示网格区名为 h 的网格项将会被排布在第 1、2 两个 cell 上。amf 分别是另外三个网格项的标识符;中间的 . 是一个空白区域的占位符,我本人习惯用这个字符,大家尽可以挑选自己喜欢的占位符。

之后,我们再为 headerasidemainfooter 分别设置映射区域——grid-area,这四个网格项就自动排布到各自模版位上去了。

header { grid-area: h; }
aside { grid-area: a; }
main { grid-area: m; }
footer { grid-area: f; }

再回头看一下网格布局的 HTML,语义标签不需要为布局做任何改变。这是较传统布局的重大改进,HTML 结构和 UI 终于实现了分离。

<body class="grid-container">
  <header></header>
  <aside></aside>
  <main></main>
  <footer></footer>
</body>

Grid Kiss

grid-template-areas 还需要为各个网格项命名模版区域,模版能不能自己帮我们映射到相应的 html tag 或是 class 上去呢?这样写起来似乎更省事。

嗯,还真有人想到了这一点——一个叫 postcss-grid-kisspostcss 插件。Grid-kiss 为我们实现了一种很有趣的 CSS 布局方案:让所有的布局变成一幅“简笔画”:

.grid-container {
  grid-kiss:
    "+-------------+  +-----+"
    "|   header    |  |     |"
    "+-------------+  |     |"
    "+-----+          |main |"
    "|     |          |     |"
    "|aside|          +-----+"
    "|     | +--------------+"
    "|     | |    footer    |"
    "+-----+ +--------------+"
    ;
}

它的实现就是把这副文本流图转化成 grid 语法树。有一句广告说的好,“Grid-kiss,布局从未如此简单”

IE support

最后再说一下 IE,早在 E10 的时候它就已经支持网格布局了——还是挺超前的。只可惜 IE 的 Grid 语法别树一帜,未被大众认可;方法与现代 Grid 一比,更相形见绌了。那 IE 上要怎么使用网格布局呢?我们还是得依靠 PostCSS——CSS 里的 Babel,它有个叫 autoprefixer 的插件可以帮我们完成 Modern Grid 到 IE Grid 的转换;这里(Autoprefixer online)还有一个在线的转换网站,有兴趣的朋友可以试一下。

若你已经使用现代 JS 框架(如 Vue、React),它们的脚手架大多内置了 postcss 和 autoprefixer,基本上就是无配置使用。上面的 grid-kiss,也只要给 postcss 配置加个插件,亲测 IE11 可用。至于 IE 的 Grid 语法,就让它随风消逝在历史的长河之中吧。

小结

上次看了篇文章,提到 CSS Grid 已是一种很大众化的前端技术。但我身边的 team,只能说很少很少很少有人使用。我猜原因有多种:

  • 熟练掌握 CSS 的开发人员本身就很少,大家形成了一种“默契”——不要增加认知复杂度
  • 技术革新太快,一开始想着“让子弹飞一会儿”,但是之后就再也没人提起了
  • 传统认知中后端重于前端,很多团队也无心推动前端升级

当然,这些种种我们也应理解,毕竟这只是一种布局技巧,在一个产品的范畴中占不了太多分量;甚至于技术本身,只要别太烂,也并不是决定产品成败的关键。所以嘛,对于新技术也,能用则用,不能用也不要太过执着;做人嘛,最重要的是开心。

相关

  • 《内容布局(一):position 布局》
  • 《内容布局(二):水平布局》
  • 《内容布局(三):Flexbox布局》

文章同步发布于an-Onion 的 Github。码字不易,欢迎点赞。

举报

相关推荐

0 条评论