CSS Grid是CSS中最强大的布局系统能轻松实现各种复杂的二维布局比以前的floatpositionflexbox都更强大更灵活自从主流浏览器支持CSS Grid之后(2017年ChromeFirefoxSafari都支持了)我就开始在项目中使用CSS Grid做布局体验非常好很多以前很难实现的布局用Grid几行代码就搞定了。
但是CSS Grid虽然强大也有不少坑特别是刚开始用的时候,很多概念和以前的布局方式不一样容易搞混踩坑最近我在项目中大量使用CSS Grid踩了不少坑有些问题让我熬夜排查很久才解决今天想记录一下这些踩坑经历包括遇到的问题原因分析以及解决方案希望能帮大家在使用CSS Grid的时候,少走弯路不熬夜。
一、坑1:grid-column和grid-row的起始线和结束线搞反
第一个坑是grid-column和grid-row的起始线和结束线搞反这是我刚开始用Grid的时候,最常犯的错误。
CSS Grid的定位是基于网格线(grid line)的grid-column: 1 / 3意思是从第1条网格线开始到第3条网格线结束也就是跨2列,但是我刚开始用的时候,经常搞反以为1 / 3是从第1列到第3列跨3列结果布局就不对元素的宽度总是差一列排查了很久才发现是网格线和列的区别搞混了。
原因分析:
CSS Grid的网格线是在列和行之间,的,比如3列的网格有4条网格线(第1条在最左边第2条在第1列和第2列之间,第3条在第2列和第3列之间,第4条在最右边)所以grid-column: 1 / 3是从第1条线到第3条线跨第1列和第2列共2列而不是3列。
很多新手容易把网格线的编号和列的编号搞混以为1 / 3是第1列到第3列跨3列结果就少了一列布局不对。
解决方案:
记住grid-column和grid-row的值是网格线的编号不是列或行的编号起始线和结束线之间,的列数或行数是结束线减起始线,比如1 / 3跨2列2 / 5跨3列。
另外也可以用span关键字来指定跨几列或几行,比如grid-column: 1 / span 2就是从第1条线开始跨2列这样更直观不容易搞混推荐用span的写法特别是新手。
还有一个调试技巧是给grid container加background或者用浏览器开发者工具的Grid调试功能(ChromeFirefox都有)能直观地看到网格线和元素的位置方便排查布局问题。
二、坑2:fr单位和百分比、px混用导致布局错乱
第二个坑是fr单位和百分比px混用导致布局错乱。
fr是CSS Grid特有的单位表示剩余空间的比例,比如grid-template-columns: 1fr 2fr就是把剩余空间分成3份第1列占1份第2列占2份非常方便,但是fr和百分比px混用的时候,容易出问题,因为fr是基于剩余空间的而百分比和px是固定的计算方式不一样混用的时候,可能会出现意料之外的结果。
我有一次写grid-template-columns: 200px 1fr 25%本来以为第1列200px第3列占25%剩余的给第2列1fr但是实际效果和我想的不一样第3列的宽度不是容器的25%而是减去200px后的25%导致布局错乱排查了很久才搞清楚fr和百分比混用的计算规则。
原因分析:
CSS Grid的轨道尺寸计算是有顺序的先计算固定尺寸的轨道(pxem等绝对单位)再计算百分比的轨道最后把剩余空间按fr的比例分配给fr轨道。
所以grid-template-columns: 200px 1fr 25%的计算顺序是:
- 第1列200px固定先减去。
- 第3列25%是基于容器宽度的25%还是减去固定尺寸后的25%?这里容易混淆。
- 剩余空间给第2列1fr。
实际上,百分比是基于容器的内容盒宽度的不是减去固定尺寸后的宽度,所以25%是容器宽度的25%然后剩余空间(容器宽度 - 200px - 25%宽度)给1fr这样计算出来的结果可能和预期不一样特别是容器宽度变化的时候,布局可能会错乱。
解决方案:
尽量不要把fr和百分比px混用特别是不要,同时用百分比和fr因为计算规则复杂容易出错,如果需要固定尺寸+ 自适应的布局用px + fr就够了,比如grid-template-columns: 200px 1fr第1列固定200px第2列占剩余空间简单明了不容易错。
如果需要多列自适应用fr的比例就好,比如grid-template-columns: 1fr 2fr 1fr三列按1:2:1分配空间简单直观。
如果确实需要百分比和fr混用一定要搞清楚计算规则多测试不同屏幕宽度下的效果确保布局正确,另外可以用minmax()函数来更灵活地控制轨道尺寸,比如grid-template-columns: minmax(200px, 300px) 1fr第1列最小200px最大300px第2列占剩余空间比混用单位更可控。
三、坑3:grid-gap不包括容器边缘
第三个坑是grid-gap(现在叫gap)不包括容器边缘也就是gap只在网格项之间,有间距容器的边缘没有间距这有时候会导致布局和预期不一样。
我有一次做一个卡片列表用Grid布局设置了grid-gap: 20px以为卡片之间,和容器边缘都有20px的间距结果发现卡片和容器边缘之间,没有间距紧贴着容器边缘不好看排查了一下才发现grid-gap只在网格项之间,有间距容器边缘没有需要单独给容器加padding。
原因分析:
CSS Grid的gap(以前叫grid-gapgrid-row-gapgrid-column-gap)定义的是网格项之间,的间距也就是行和行之间,列和列之间,的间距不包括网格项和容器边缘之间,的间距,所以容器的四个边缘是没有gap的网格项会紧贴容器边缘。
这和很多人的直觉不一样很多人以为gap会在所有地方都有间距包括容器边缘,但是实际上,不是这样gap只在网格项之间。
解决方案:
如果需要容器边缘也有间距给grid container加padding就行,比如padding: 20px这样容器边缘有20px的内边距网格项之间,有20px的gap整体间距就一致了。
另外也可以用margin给网格项加外边距,但是这样比较麻烦,而且和gap混用容易出问题推荐用容器padding + gap的方式简单可靠。
还有一个注意点是gap的百分比值是基于容器的内容盒尺寸的不是基于轨道尺寸的,所以用百分比gap的时候,要注意容器尺寸变化的影响一般用px做gap单位比较稳妥。
四、坑4:grid-auto-flow默认是row不会自动换行
第四个坑是grid-auto-flow默认是row不会自动换行这在做响应式卡片列表的时候,容易踩坑。
我有一次做一个响应式卡片列表用Grid布局设置了grid-template-columns: repeat(4, 1fr)想在大屏显示4列小屏自动换行显示更少的列,但是发现,不管屏幕多小都是4列不会自动换行卡片被挤得很窄很难看排查了很久才发现Grid默认不会自动换行需要用auto-fill或auto-fit配合minmax()才能实现自动换行。
原因分析:
CSS Grid和Flexbox不一样Flexbox默认是不换行的,但是可以用flex-wrap: wrap来换行而Grid的列数是由grid-template-columns决定的设置了几列就是几列不会,因为容器宽度不够就自动换行,因为Grid是二维布局列数是明确定义的不是自动计算的。
所以grid-template-columns: repeat(4, 1fr)就是固定4列,不管容器多宽都是4列不会自动换行,如果要响应式自动换行需要用auto-fill或auto-fit关键字配合minmax()函数。
解决方案:
用auto-fill或auto-fit配合minmax()实现响应式自动换行,比如grid-template-columns: repeat(auto-fill, minmax(250px, 1fr))意思是每列最小250px最大1fr(占满剩余空间)自动填充尽可能多的列这样容器宽的时候,列数多容器窄的时候,列数少自动换行非常适合响应式卡片列表。
auto-fill和auto-fit的区别是auto-fill会保留空的轨道(即使没有内容也占位置)auto-fit会把空的轨道折叠让有内容的轨道占满空间一般做卡片列表用auto-fit更好,因为卡片会占满整行不会有空白。
另外也可以用媒体查询(media query)在不同屏幕宽度下设置不同的列数,比如大屏4列中屏3列小屏2列超小屏1列这样更精确控制布局,但是代码量大一些auto-fill/auto-fit更简洁方便各有优劣根据需求选择。
五、坑5:Grid项目的align-self和justify-self默认是stretch
第五个坑是Grid项目的align-self和justify-self默认是stretch也就是,拉伸占满整个网格单元格这有时候会导致元素被拉伸变形和预期不一样。
我有一次在Grid单元格里放了一个按钮发现按钮被拉伸得很宽占满了整个单元格不好看我以为是按钮的样式问题改了很久按钮的样式都没用后来才发现是,Grid项目默认stretch导致按钮被拉伸了设置justify-self: start就好了。
原因分析:
CSS Grid项目的对齐方式默认是stretch也就是拉伸占满整个网格单元格align-self(垂直方向)默认stretchjustify-self(水平方向)默认stretch所以Grid项目默认会被拉伸占满整个单元格这对于布局容器来说是好的,但是对于按钮图片等不需要拉伸的元素就会导致变形,或者不好看。
这和Flexbox不一样Flexbox项目的align-items默认是stretch但是justify-content默认是,flex-start不会水平拉伸而Grid两个方向默认都是stretch所以更容易出现拉伸的问题。
解决方案:
对于不需要拉伸的元素设置align-self和justify-self为startend或center就行,比如按钮设置justify-self: start就不会被水平拉伸图片设置align-self: centerjustify-self: center就会居中显示不会被拉伸。
也可以在grid container上设置align-items和justify-items来统一设置所有Grid项目的对齐方式,比如justify-items: start所有项目都左对齐不会被水平拉伸这样不用每个项目都单独设置更方便。
另外对于图片还可以用object-fit来控制图片在拉伸的容器里的显示方式,比如object-fit: covercontain等等避免图片变形。
六、坑6:Grid和margin折叠的问题
第六个坑是Grid和margin折叠的问题在普通的块级布局中相邻元素的margin会折叠(margin collapse)但是,在Grid布局中Grid项目的margin不会折叠这有时候会导致间距和预期不一样。
我有一次把一个普通布局改成Grid布局发现元素之间,的间距变大了排查了很久才发现是,因为普通布局中margin折叠了间距是较大的那个margin而Grid布局中margin不折叠间距是两个margin相加,所以变大了。
原因分析:
在CSS的块级格式化上下文(BFC)中相邻的块级元素的垂直margin会折叠也就是两个margin不会相加而是取较大的那个这是CSS的一个特性,但是在Grid布局中Grid项目处于Grid格式化上下文(GFC)中margin不会折叠,所以相邻Grid项目的margin会相加间距比普通布局大。
这是Grid和普通块级布局的一个重要区别很多人容易忽略导致布局间距和,预期不一样。
解决方案:
在Grid布局中尽量用gap来设置网格项之间,的间距而不是用margin因为gap是专门为Grid设计的间距方式不会有折叠问题也更简单可靠。
如果确实需要用margin要注意Grid中margin不折叠的特性计算间距的时候,要把两个margin都算进去,或者只给一个方向加margin避免间距过大。
另外从普通布局迁移到Grid布局的时候,要注意检查margin的问题可能需要调整margin的值,或者改用gap来保持间距一致。
七、坑7:Grid嵌套时子Grid不会继承父Grid的轨道
第七个坑是Grid嵌套时子Grid不会继承父Grid的轨道也就是父元素是Grid容器子元素也是Grid容器的时候,子Grid的轨道是独立的不会和父Grid的轨道对齐这有时候会导致布局对不齐。
我有一次做一个复杂布局外层用Grid内层也用Grid本来以为内层的列会和外层的列对齐结果发现不对齐排查了很久才发现子Grid的轨道是独立的不会继承父Grid的轨道需要单独定义,或者用subgrid(但是2018年浏览器还不支持subgrid)。
原因分析:
CSS Grid的嵌套和普通元素嵌套一样子元素的布局是独立的不会继承父元素的Grid轨道父Grid只负责子元素在父Grid中的位置和尺寸子元素内部的布局由子元素自己的display决定,如果子元素也是Grid容器它的轨道需要自己定义和父Grid无关。
这是合理的,因为每个Grid容器都是独立的布局上下文,但是有时候我们希望子Grid的轨道和父Grid对齐这就需要用subgrid特性,但是subgrid是CSS Grid Level 2的特性2018年主流浏览器还不支持(Firefox后来先支持了ChromeSafari更晚)所以2018年的时候,还不能用。
解决方案:
在不支持subgrid的情况下要实现子Grid和,父Grid轨道对齐可以用以下方法:
- 子Grid定义相同的轨道:给子Grid定义和父Grid相同的grid-template-columns这样列宽一样就能对齐,但是要注意子Grid的宽度和父Grid的单元格宽度一致才能完全对齐。
- 不用嵌套Grid用一个Grid:如果布局不是特别复杂尽量不用嵌套Grid用一个Grid来完成所有布局这样就没有,对齐问题Grid的强大之处就是能实现复杂的二维布局很多以前需要嵌套的布局用一个Grid就能搞定。
- 等subgrid支持:如果确实需要嵌套Grid对齐轨道可以等浏览器支持subgrid再用,或者用polyfill但是2018年还不太行需要等几年。
我当时的解决方案是把嵌套Grid改成一个Grid用grid-column和grid-row来定位各个元素,虽然代码稍微复杂一点,但是布局对齐了也更简洁维护起来也方便。
八、写在最后
以上就是我使用CSS Grid布局踩过的一些坑包括网格线搞反单位混用gap不包括边缘不自动换行默认stretchmargin不折叠嵌套不对齐等等这些坑有些让我熬夜排查很久才解决希望通过这篇文章能帮大家少走弯路不熬夜。
CSS Grid是非常强大的布局系统能实现各种复杂的二维布局比以前的floatpositionflexbox都更强大更灵活,虽然有一些坑,但是,只要理解了它的核心概念和工作原理避开这些坑就能用得很顺手大大提高布局的效率和质量。
现在主流浏览器都已经支持CSS Grid了(ChromeFirefoxSafariEdge都支持)可以放心在项目中使用不用太担心兼容性问题,如果需要兼容老浏览器(比如IE11)可以用渐进增强的方式老浏览器用flexbox或者float降级新浏览器用Grid享受Grid的便利。
最后用一句话结束这篇文章:"CSS Grid很强大也有坑,但是,只要理解了它的原理避开了坑就能发挥它的强大威力做出各种复杂而优雅的布局。"
愿大家都能用好CSS Grid做出漂亮的网页布局少踩坑不熬夜。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录