如果您从事支持旧版浏览器的 Web 应用程序工作,并且像我一样从旁观望过 CSS Grid,我有一些好消息:我发现了一种巧妙的纯 CSS 方法,可以在 IE10+ 中使用网格自动布局!
现在,它不是真正的 CSS Grid,但如果不去看代码本身,你将无法分辨。HTML 结构看起来像 CSS Grid。它定义了一组列,具有不确定的行数,并且它拥有支持单元格边框和阴影的间距,无需任何黑客手段。但幕后实际发生的事情是 Flexbox 和边距的组合。
在本文中,我将逐步介绍这种方法。 这是一个我们正在查看的演示
查看 Pen
IE10 兼容的 CSS-Grid 风格的列布局 by Brian Holt (@bholtbholt)
on CodePen.
使用 Flexbox Wrap 自动流动的行

获取基本的网格设置非常简单。如果您熟悉 Flexbox,我敢肯定您已经猜到 flex-wrap: wrap
是这里的诀窍。而且您是对的。
在我们编写任何 CSS 之前,让我们将 HTML 标记就位。我们希望它类似于使用自动布局时的结构——一个 .grid
容器和不确定的数量的 .grid__cell
。
<div class="grid">
<div class="grid__cell">...</div>
...
</div>
我们设置了三个网格断点。分别针对移动设备、小屏幕和大屏幕的单列、两列和三列布局。在这篇文章中,我使用了 Bootstrap 中使用的断点,但如果我们处理的是真实内容,我们应该在布局断裂的实际点定义它们。
$screen-sm-min: 768px;
$screen-sm-max: 991px;
$screen-md-min: 992px;

移动优先方法意味着我们的单列布局已经完成,因为每个 .grid__cell
本身就是一个块。我们在第一个断点之后将 .grid
设置为 Flexbox 容器,并包装单元格。
@media (min-width: $screen-sm-min) {
.grid {
display: flex;
flex-wrap: wrap;
}
}
我们的两列和三列布局需要显式宽度和 Flex 属性;否则它们将挤压到单行上。在测试 IE10 时,我遇到了 flex-basis
属性的意外行为,发现使用 flex-basis: auto
设置显式宽度更加一致。但这在 IE11 中似乎不是问题。
.grid__cell {
min-width: 0;
flex: 1 1 auto;
}
// Two-column grid
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
$width: 50%;
.grid__cell {
width: $width;
}
}
// Three-column grid
@media (min-width: $screen-md-min) {
$width: 33.33%;
.grid__cell {
width: $width;
}
}
我们不需要在媒体查询中包装 .grid__cell
,因为当父元素不是 Flexbox 容器时,它的 Flex 属性不会起作用。我们还为两列媒体查询定义了一个上限,这样它就不会影响三列网格。
就是这样!现在我们有了响应式、流畅、包装的 Flexbox 网格。简单的部分完成了… 当然,只要我们只有数量是 2 和 3 的倍数的项目。使用 flex: 1 1 auto
,最后一个项目将始终占用最后一行中的任何剩余空间。


对齐最后一行中的单元格
难以捉摸的最后一行是我们来这里的目的,对吧?默认情况下,每个单元格都将在 Flexbox 布局中扩展到行尾,但网格会留下一个空白区域。如何在 Flexbox 中做到这一点?使用伪元素!
诀窍是在 .grid
容器中添加一个伪元素,并将其设置为像单元格一样。我们在每个断点上定义 :after
伪元素单元格,其宽度与真实单元格相同。
@media (min-width: $screen-sm-min) {
.grid {
...
&:after {
content: '';
display: block;
flex: 1 1 auto;
}
}
}
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
$width: 50%;
.grid:after {
width: $width;
}
}
@media (min-width: $screen-md-min) {
$width: 33.33%;
.grid:after {
width: $width;
}
}
这会创建一个假单元格,它将抵住我们的真实单元格,并在单元格数量为奇数时对齐我们的两列网格。将它的高度保持未定义,允许它在单元格数量为偶数时折叠为零。

我们的三列网格稍微复杂一些,因为我们需要处理多种状态,例如当有一个空单元格时,以及当有两个空单元格时。

我们的一个空单元格状态已经处理了,因为它与两列中有一个空单元格没有太大区别。:after
单元格具有设置的宽度,并完成了行。当有两个空单元格时,故事就会发生变化,因为 flex: 1 1 auto
再次发挥作用:最后一个单元格现在被推到伪元素时,会扩展到宽度的 50%。

使用 CSS :nth-of-type
选择器,我们可以定位每行中的第一列。由于我们的行是 3 的倍数,我们使用 3n
定位它们,然后向后计数 2
以获得每行中的第一个元素。
@media (min-width: $screen-md-min) {
.grid__cell {
...
&:nth-of-type(3n-2) {
background-color: red;
}
}
}

我们大体上定位了第一列中的所有单元格,但我们需要将选择范围限制到最后一行。实际上,我们需要将它限制在它作为最后一行第一列中的最后一个单元格时。幸运的是,有一个方便的伪选择器用于定位其类型的最后一个项目。我们将 :last-of-type
连接起来以创建逻辑语句。
@media (min-width: $screen-md-min) {
.grid__cell {
...
&:nth-of-type(3n-2):last-of-type {
background-color: red;
}
}
}
现在我们已经选中了最后一行第一列中的最后一个单元格,我们使用边距将 :after
单元格推到最后一列并填充中间单元格。
@media (min-width: $screen-md-min) {
.grid__cell {
...
&:nth-of-type(3n-2):last-of-type {
margin-right: $width;
}
}
}
这是我们完整的 Flexbox 定义的自动布局网格模仿器。看看它整齐排列的行。我敢打赌你根本无法分辨它不是 CSS Grid!

使用边距添加间距
CSS Grid 的规范有一个 列间距和行间距 用于在每个单元格之间提供空间。在 Flexbox 中创建间距更具挑战性。它看起来将要出现在 Flexbox 中,但我们还没有到那里…… 而且 IE 永远不会出现。
在 Daniel Tonon 的 在 IE 中使用 CSS Grid 的指南中,他使用了一个带有负边距、边框、少量填充和 overflow: hidden
的内部单元格 div。虽然可能有点 hacky,但效果有效,但它破坏了我们保持 CSS Grid 风格的 HTML 结构的愿望。我更喜欢的方法可能感觉有点粗糙,但我发现它最容易阅读和理解。此外,它继续使用 :nth-of-type
伪选择器,这使得整体方法感觉一致。
我们希望单元格之间有间距,但外部没有。我们还希望我们的单元格与容器齐平。

我们的移动或单列网格只需要在单元格上添加底部边距。我们添加了它,并使用 margin-bottom: 0
覆盖了最后一个单元格,以便单元格与容器齐平。通常我会使用 initial
,但 IE 不支持。
$col-gap: 16px;
.grid__cell {
...
margin-bottom: $col-gap;
&:last-of-type {
margin-bottom: 0;
}
}

我们的两列和三列网格需要在单元格的右侧添加边距,最后一列没有右侧边距,并且最后一行中的任何单元格都没有底部边距。由于存在边距,我们还需要重新计算宽度,因为如果单元格不合适,它们将换行。
在两列布局中,使用 :nth-of-type(2n)
或 :nth-of-type(even)
获取右侧(或第二列)非常容易。我更喜欢使用 n 倍数来保持与我们的三列网格一致,并用于计算最后一行。
我们的最后一行稍微棘手一些。当我们有奇数单元格时,我们的移动优先 CSS 会负责移除底部边距,因为单元格是 :last-of-type
,并且我们的 :after
单元格没有应用边距。

当我们有偶数单元格时,我们需要定位倒数第二个单元格,但前提是它位于第一列位置。如果我们没有对其进行限定,倒数第二个单元格将垂直扩展以匹配倒数第二行的高度。我们可以使用 :nth-of-type(2n-1):nth-last-of-type(2)
定位它。
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
$width: calc(50% - #{$col-gap});
.grid__cell {
...
margin-right: $col-gap;
// Remove margin in last column
&:nth-of-type(2n) {
margin-right: 0;
}
// For when the last row is complete
// . .
// * .
&:nth-of-type(2n-1):nth-last-of-type(2) {
margin-bottom: 0;
}
}
}

我们的三列间距采用了相同的方法。我们为它们添加了 margin-right
,从第三列中移除了它,并且从最后一行中移除了底部边距。同样,我们的最后一个单元格由我们的移动优先方法处理,但现在我们需要涵盖最后一行包含两个单元格时的情况,以及包含三个单元格时的情况。我们可以使用 nth-of-type
和 nth-last-of-type
限定我们的选择器。
@media (min-width: $screen-md-min) {
$width: calc(33% - #{$col-gap});
.grid__cell {
...
margin-right: $col-gap;
// Remove margin in last column
&:nth-of-type(3n) {
margin-right: 0;
}
// For when there two items in the last row
// . . .
// * .
&:nth-of-type(3n-2):nth-last-of-type(2) {
margin-bottom: 0;
}
// For when the last row is complete
// . . .
// * * .
&:nth-of-type(3n-1):nth-last-of-type(2),
&:nth-of-type(3n-2):nth-last-of-type(3) {
margin-bottom: 0;
}
}
}

我们需要调整最后一行中最后一个单元格的边距,因为它单独存在于列中。我们使用 33% 加上每侧的间距。
@media (min-width: $screen-md-min) {
$width: calc(33% - #{$col-gap});
.grid__cell {
...
// When there is only one item in the last rpw
// Fill the margin so it's like the last item is
// double the width
// . . .
// *->
&:nth-of-type(3n-2):last-of-type {
margin-right: calc(33% + #{$col-gap * 2});
}
}
}
现在我们的间距已安装,网格已完成!用边框、阴影或您想要的任何东西填充它们。

总结
最后的结果再看一遍
查看 Pen
IE10 兼容的 CSS-Grid 风格的列布局 by Brian Holt (@bholtbholt)
on CodePen.
我相信这种技术可以通过一些小的调整来支持 IE9,例如使用内联块而不是弹性盒。我们也可以通过添加另一个断点并使用与三列网格相同的方法扩展到四列网格。随意使用这种方法,我希望它对您有所帮助!
我已经正式不再支持前端的 IE 了。耶!这就像我停止支持 IE 6 时感受到的自由。互联网的毒瘤已经死了,微软如是说。
https://techcommunity.microsoft.com/t5/Windows-IT-Pro-Blog/The-perils-of-using-Internet-Explorer-as-your-default-browser/ba-p/331732
这对我很正式,一些来自微软的相关引用
“Internet Explorer 是一种兼容性解决方案……”(即,不是浏览器)
因此,如果我的客户说他们的网站看起来很糟糕,我现在可以告诉他们,他们正在使用一个未针对现代网络设计且不再受支持的系统。
因此,我认为你正在鼓励使用过时的技术,这对网页设计师来说有点不负责任。使用 IE 10 的表格可以正常工作,不需要强迫使用弹性盒。
注意:你新设计的评论部分与内容分离,我觉得你正在通过将评论部分放在页脚来迭代地从你的网站中删除评论。
你可以避免使用伪元素,而是使用 `max-width` 来约束内容吗?这似乎是一个更干净的解决方案,而且可以更容易地扩展到更多列/断点。
我创建了一个笔,并在 IE10 中对其进行了测试,它似乎可以正常工作。
想法?
使用 `max-width` 进行约束很不错,但我很难让最后一列在有间距的情况下与 `.grid` 容器对齐。
你可以像在 CodePen 中那样,将边距添加到 `.grid__cell` 的所有边,然后你需要从 `.grid` 容器中减去边距,这可能会对周围元素产生意想不到的影响。
总的来说,我认为这是一个很好的方法!