在 IE11 使用 CSS Grid 实现多列卡片列表布局
我们维护的某网站需要多列布局的、分页的、内容高度不固定的卡片列表,效果如下:
实现这种网格列表,其实使用 CSS Grid 是最为科学的方案,因为它灵活、好用、易于理解,同时对于这种内容不定高的多列卡片列表非常友好:
.grid-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
但是有一个棘手的问题是:这个网站的浏览器兼容性要求是 Chrome 60+ 和 IE11。没错,都 2022 年了,还有苦逼开发者(比如我)还需要想办法让网站能在 IE11 正常工作。
不过幸运的是,通过对 CSS Grid 兼容性 的查询,我们可以得知:
- IE10+ 支持旧版的规范,且需要添加
-ms-
的前缀 - Chrome 57+ 开箱支持
根据前辈的指引,我意识到在 IE11 中用 CSS Grid 实现这种网格列表是完全没问题的,只是需要一些技巧。
我们的项目使用到了 SCSS、PostCSS 和 Autoprefixer,所以,首先在 postcss.config.js
中声明使用针对 IE11 的 CSS Grid 转译:
module.exports = {
plugins: {
autoprefixer: {
grid: "autoplace"
}
}
};
然后,把这个 mixin 丢进项目中:
@mixin gridList($maxRow, $column, $rowSize: auto, $columnSize: 1fr, $gapX: 0, $gapY: 0) {
@for $i from 1 through $maxRow {
&--grid-#{$i} {
display: grid;
grid-template-columns: repeat(#{$column}, #{$columnSize});
grid-template-rows: repeat(#{$i}, #{$rowSize});
gap: $gapY $gapX;
}
}
}
然后在需要的地方使用这个 mixin(假设我们的网格列表一页最多有 9 项,每列有 3 项,即最多会有 3 行):
.grid-list {
padding: 30px;
@include gridList(3, 3, $gapX: 30px, $gapY: 30px)
}
嗯,那么这个 mixin 是干什么的呢?
我们前面提到了 IE11 支持旧版的 CSS Grid 规范,而在该版本的规范中,必须显式声明你会用到的行数和列数;而且它并不支持 gap
属性。
那么,对于需要显式声明行数和列数的问题,我们可以换一个思路:借助 SCSS 函数的力量,把所有可能的卡片列数的 CSS 都生成出来;然后在 HTML 部分,由模板渲染器告诉页面一共有多少行。
以下是 mixin 生成的代码:
.demo-list {
padding: 30px;
}
.demo-list--grid-1 {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(1, auto);
gap: 30px 30px;
}
.demo-list--grid-2 {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, auto);
gap: 30px 30px;
}
.demo-list--grid-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, auto);
gap: 30px 30px;
}
这样,我们就得到了行数有一行、两行和三行时所对应的 CSS 类。我们只要在生成模板时,使用对应的 CSS 类即可:
<!-- 以下伪代码仅供参考,实际使用时请替换为你使用的模板引擎/界面库的语法 -->
<ul class="demo-list demo-list--grid-{{ Math.ceil(Page.PageList.length / 3) }}">
{%- for PageItem in Page.PageList -%}
<li class="demo-card">
<!-- 略 -->
</li>
{%- endfor -%}
</ul>
IE 使用的旧版规范属性是以 -ms-
开头的,同时我们还需要通过空白的网格来模拟 gap
属性。不过,Autoprefixer 可以帮我们进行转译:
.demo-list {
padding: 30px;
}
.demo-list--grid-1 {
display: -ms-grid;
display: grid;
-ms-grid-columns: 1fr 30px 1fr 30px 1fr;
grid-template-columns: repeat(3, 1fr);
-ms-grid-rows: auto;
grid-template-rows: repeat(1, auto);
gap: 30px 30px;
}
.demo-list--grid-1 > *:nth-child(1) {
-ms-grid-row: 1;
-ms-grid-column: 1;
}
.demo-list--grid-1 > *:nth-child(2) {
-ms-grid-row: 1;
-ms-grid-column: 3;
}
.demo-list--grid-1 > *:nth-child(3) {
-ms-grid-row: 1;
-ms-grid-column: 5;
}
.demo-list--grid-2 {
display: -ms-grid;
display: grid;
-ms-grid-columns: 1fr 30px 1fr 30px 1fr;
grid-template-columns: repeat(3, 1fr);
-ms-grid-rows: auto 30px auto;
grid-template-rows: repeat(2, auto);
gap: 30px 30px;
}
.demo-list--grid-2 > *:nth-child(1) {
-ms-grid-row: 1;
-ms-grid-column: 1;
}
.demo-list--grid-2 > *:nth-child(2) {
-ms-grid-row: 1;
-ms-grid-column: 3;
}
.demo-list--grid-2 > *:nth-child(3) {
-ms-grid-row: 1;
-ms-grid-column: 5;
}
.demo-list--grid-2 > *:nth-child(4) {
-ms-grid-row: 3;
-ms-grid-column: 1;
}
.demo-list--grid-2 > *:nth-child(5) {
-ms-grid-row: 3;
-ms-grid-column: 3;
}
.demo-list--grid-2 > *:nth-child(6) {
-ms-grid-row: 3;
-ms-grid-column: 5;
}
/* 后略... you get the idea */
结论
经过一系列的努力,我们终于拥有了在 IE11 正确显示的多列卡片列表。好耶!
尽管业界正积极淘汰 IE11,但现实情况是,还有一些商业应用仍然有着对 IE11 的兼容需求。确实,兼容老旧浏览器是一个让人头疼的问题,然而通过变换思路,我们可以找到一些取巧的解决方案。
当然更好的方法是,提桶跑路,去给历史包袱不重的项目填坑(逃
后记
这篇博文本来是一篇在 2022 年私下写的笔记,因为一开始只是写给我自己看的,加上我想应该需要支持 IE11 的项目越来越少,一直没有发布。
但是现实总是骨感的,就连不久前发布了 4.0.0 BETA 的 jQuery 团队 都说要继续支持 IE11,直到 jQuery 5 再放弃支持。所以这篇博文大概还是能发挥余热吧。