英文: Rachel Andrew
译文:大漠
www.w3cplus.com/css/css-grid-gotchas-stumbling-blocks.html
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:https://codepen.io/airen/pen/mxOdYE
第二个示例使用CSS Grid实现相同的布局,但是,你可以看到,最后一行中的项目始终保持在它们的列中。这是因为在网格中,我们将项目排列成行和列 —— 二维布局。
DEMO2:https://codepen.io/airen/pen/qoqBGe
你还可以在第二个示例中看到,在CSS Grid布局中,我们不需要向网格添加任何内容来进行布局。所有东西都被放在容器上。在Flexbox布局中,你必须针对Flex项目来设置flex-grow
、flex-shrink
和flex-basis
属性。这是理解网格布局关键所在,也可能是大家有很多困惑的地方。Grid主要是关于包含元素的,而我们之前的所有布局方法都依赖于我们在布局中设置的宽度,使某些东西看起来像一个网格。
如果你使用一个简化版本的浮动12列“网格”,我们必须计算每一列的百分比大小,加上每个列之间间距的百分比大小。要创建跨多个列的项,需要将所有项的宽度加上用于分隔它们的边界宽度。
DEMO3:https://codepen.io/airen/pen/eMBYqQ
使用Flexbox创建的网格也是如此。当我们在父节点上通过display:flex
创建Flex布局时,Flex所有的大小都需要在单个Flex项目上进行。为了制作一个Flexbox的“网格”,我们必须阻止Flexbox做灵活的操作,而是应该设置百分比宽度,就像我们前面的浮动网格示例一样。使用Flexbox要比浮动更有一些优势,比如控制对齐和列等高之类的要简易得多。然而,在Flexbox和浮动的方法中仍然没有网格,只是通过设置项目的大小,并将它们排列起来,让其看起来像网格的东西。
DEMO4:https://codepen.io/airen/pen/PRbwwp
在网格中,所有的大小都发生在容器上。一旦我们创建了我们的网格轨道,我们就可以告诉单个项目(Grid项目)有多少个轨道可以跨越,但我们却有一个实际的网格。我们可以完全抛弃行的容器,因为网格已经有行了。这也意味着,我们也可以使用相同的方式进行跨列。这对于以前而言是件很难做的事情。
DEMO5:https://codepen.io/airen/pen/JLboYP
是否应该将网格用于主布局和Flexbox用于组件布局
随着大家开始接触和学习CSS Grid的布局,这个神话不断涌现。也许它来自于网格系统的使用,比如在Bootstrap或Foundation,大家关心的是一个整体网格上放置项目。这当然是使用网格布局的一种方法。不过,我还是会考虑在上一节提到的不同之处。问问你自己,这个布局是一维的还是二维的?
如果你可以使用你的组件,并且用行和列在它的上面绘制一个网格。它是二维的,那就使用CSS Grid来布局。
如果相反,你希望单个项目在一行中进行扩展,而不考虑上面一行中发生的情况,那就应该使用Flexbox布局更为合适。
不管你想要展示的是一个完整的页面,还是一个很小的组件。重要的是你想在布局里面的项目分配空间和相互关联。
网格轨道大小是否由内容来决定?
我们已经看到了如何在使用网格布局时,在容器上设置网格和网格大小。但是,网格中的项可以指定网格轨道大小。这里要记住的关键是,一个单元格大小的改变将会改变整个轨道的大小。如果你不希望这种情况发生,你可能需要一个单一维度的Flexbox布局。
最简单的方法就是使用auto
,因为它会默认在隐式网格中创建网格轨道。一个自动大小的网格轨道将扩展到包含所有的内容。在下面的示例中,我有一个两列布局,在右边的列中添加更多的内容会导致整个行的扩展。第二行也是自动大小,再扩展以包含内容。
DEMO6:https://codepen.io/airen/pen/NYbPdY
我们可以使用两个参数来控制网格轨道大小,例如创建一个最小的网格轨道,但其仍然会增长以适应较大的网格项目。我们可以使用minmax()
函数来做这个。传给minmax()
函数的第一个值,它是网格轨道最小的值,第二个值是网格轨道最大的值。因此,你可以设置200px
的行,但通过auto
设置为网格轨道最大值,那么当有较多的内容时,不会出现内容溢出。
DEMO7:https://codepen.io/airen/pen/oqYgWx
也有一些有趣的关键词可以设置大小,将在以后的文章中对它们进行适当的阐述。这些关键词在指定网格中允许内容来改变网格轨道大小,并且可以在CSS内部和外部的大小模块(CSS Intrinsic and Extrinsic Sizing Module)中找到相关的详细内容。例如min-content
关键词的示例,使用它创建一个网格轨道时,将会创建尽可能小的网格轨道。
在我的例子中,这个词意味着其成为最宽的东西,网格轨首缩小以适应它。
DEMO8:https://codepen.io/airen/pen/geLbXG
相反,如果你使用的是max-content
,你会得到一个尽可能大的网格轨道。这可能会导致溢出情况,在下面的示例中,使用了overflow: scroll
设置了网格溢出,所以max-content
的网格轨道会导致滚动条出现。
DEMO9:https://codepen.io/airen/pen/WzobdK
关键要记住的是,这将会发生在整个网格轨道上。你需要确保网格轨道的其他网格项目也能巧妙地吸收额外的空间。
了解了如何对网格轨道大小进行调整,以及内容将如何改变网格轨道大小,这可能是新手使用CSS Grid布局中会感到最为困惑的事情之一。这需要花一点时间来理解 —— 我们之前没有任何类似的行为。这是理解事物如何运作的最好方法。
可以使用CSS Grid来实现瀑布流布局?
很多同学有一种误解,认为网格布局与瀑布流或Pinterest布局一样的。这通常是基于在网格布局中自动放置网格项目,这样的效果看上去的确有点像瀑布流布局。在下一个示例中,我有一个布局,使用grid-auto-flow
设置为dense
,实现网格项目自动流的布局。这将导致网格项目从源程序中取出,并尝试在网格填充空白区域。
DEMO10:https://codepen.io/airen/pen/KoNwRb
然而这并不是真正的瀑布流布局,因为我们仍然有一个网格(具有行和列),并且潜在的网格项目从源代码中移出。一个真正的瀑布流布局将使事物在源代码中工作。项目被推上去填充部分空间。它更像是在两个维度上做Flexbox布局。
你可以通过对所有的Grid项目进行定位处理来得到一个瀑布流外观的网格布局,但是自动流的瀑布流布局,网格布局还无法具备这方面的能力。不过,未来的规范正在做这方面的考虑。
如何向网格区域添加背景和边框?
一个网格尚未完成的问题,网格区域本身的背景和边框的样式。能在网格区域上直接添加背景和边框的样式吗?到目前是不可能的,如果要实现这样的一个效果需要插入一个元素或者添加一个伪元素来完成。
下面的这个示例中,我在网格中通过伪元素来完成,将其放置在基于行的位置,然后添加一个背景和边框到该网格区域。
DEMO11:https://codepen.io/airen/pen/GxNJba
有时候可以绕过背景和边框来实现,比如通过网格间距(grid-gap
) —— 用一个1px
来模拟背景或边框,比如下面的这个示例:
DEMO12:https://codepen.io/airen/pen/KoNdPg
为了能够对网格区域进行适当的样式化,我们需要引入网格区域伪元素的概念,这是一种特殊的生成内容。在 CSS WG上有一个关于这方面的问题,所以你可以在这里参加讨论,把你的想法与大家一起参与讨论。
跨越到网格的末端
网格布局具有隐式和显式网格的概念。显式网格是我们使用grid-template-rows
和grid-template-columns
定义的网格。这个网格轨道定义了显式网格的范围。当我们在显式网格之外放置一个网格项目,或者我们通过自动旋转更多的网格项目时,隐式网格就将被创建。
除非你使用grid-auto-rows
或grid-auto-columns
创建的网格轨道,否则在隐式网格中创建的网格轨道的大小将是自动的。
在很多情况下,隐式和显式网格的渲染行为是相同的,对于很多的布局,你会发现你定义了列,然后允许将行创建为隐式网格。不同的是,当你开始使用负的行号来引用网格的最后一行时,你会发现还是有一定区别的。
对于网格布局中的写作模式。在从左到右的语言(ltr
)中,列第一行是在左边,而你可以用-1
来指向右边的列。在从右到左的语言(rtl
)中,列的第一行在右侧,而-1
则指向左边的列。
DEMO13:https://codepen.io/airen/pen/NYbGNN
或许你已经发现了,只有显式的网格才可以向后计数。如果你在隐式网格中添加了行,然后尝试以-1
来指定目标,你将会发现你得到是显式网格的最后网格线,而不是实际网格最末端的网格线。
DEMO14:https://codepen.io/airen/pen/xWRwOJ
百分比的问题
在文章开头之处,我描述了网格布局与之前的布局方法与众不同之处。由于浮动和基于Flexbox的网格的限制,我们需要变得擅长计算百分比来做布局,所以大多数人做的第一件事就是尝试在他们的网格布局中使用相同的方法。然而,在这样做之前不要忘记我们有一个新单位fr
。这个单位是专门为网格布局设计的,因为网格设置父元素的大小。
fr
单位允许我们分配可用网格布局中的可用空间。其通过查看网格容器中可用的空间(去掉间距所需的空间、固定宽度的网格项目或定义网格轨道),然后按照我们为网格轨道指定的比例来对剩余的网格空间进行分配。这意味着,我们使用浮动或Flexbox布局的场景,必须有灵活的间距。
DEMO15:https://codepen.io/airen/pen/VXmvpN
在大多数情况下,fr
单位是一个比百分比更好的选择。你可能选择使用百分比的原因是你需要一个网格布局,以便与其他元素匹配使用其他布局方法,并依赖于百分比大小。然而,如果不是这样的话,看看fr
单位是否能满足你的需求,然后对其进行计算。
网格可以嵌套使用?
网格项目也可以成为网格容器,就好比Flex项目也可以成为一个Flex容器一样。但是,这些嵌套网格也父网格没有任何关系,因此不能使用它们与其他嵌套网格对齐内部元素。
DEMO16:https://codepen.io/airen/pen/GxNpvg
在将来的网格布局中,很可能会有一种创建嵌套网格的方法,它可以维护与父网格的关系。这意味着,除了网格的直接子节点,其他网格项目可能参与整个网格布局。
网格布局有对应的Polyfill吗?
我经常会被问到是否有网格布局的Polyfill,大家都想知道是否有一种方法可以支持旧的浏览器。
我的建议是,这并不是你需要做的事情。这可能会为那些已经在努力渲染现代网站的浏览器造成一定的性能影响,带来不好的用户体验。如果你南非要较旧的浏览器与现代浏览器相同,那么你可能要考虑在这个项目中是否使用网格布局。不过,在大多数情况下,可以使用较老的方法来为不支持的设备创建一个简单的降级处理,而不需要创建两个完全不同的CSS代码。这方面真的需要用一篇文章来详细阐述,所以我将尽快在Smashing Magazine发布这方面的教程。
调试网格布局
当你开始使用网格布局时,你肯定希望能看到你的网格和其网格项目是如何布局的。我建议你使用Firefox Nightly,并在Firefox 浏览器开发者工具中使用网格检查器。如果你选择一个网格,可以点击这个小网格图标 —— 我喜欢把它想像成一个华夫饼(Waffle) —— 来显示网格。
Firefox已经在这方面做得很好了,而且Chrome也在着手在Chrome开发者工具中实现这方面的功能。
有关于在Firefox浏览器中怎么使用网格检查器来调试网格布局,可以阅读以前翻译的一篇文章《使用Firefox 网格检查器调试 CSS网格布局》。
这对于我们所有人来说仍然是新东西
我很了解CSS网格规范,但是我从3月份就开始使用它了,就像其他人一样。当我们从创建小示例开始,也可以说真正的在生产中开始推动Grid相关的规范,我们将开始寻找使用网格的新方法,当然还有新问题要解决!我很乐意看到你自己编写的有关于网格相关的案例。在接下来的几个月的时间里,我还将在Smashing Magazine中深入探讨网格布局相关的问题。
感兴趣的小伙伴,可以关注公众号【grain先森】,回复关键词 “小程序”,获取更多资料,更多关键词玩法期待你的探索~