防御性CSS,防的是谁?我把他总结为:一切使表现和行为偏离预期效果的情景。出现这些场景的原因是因为终端环境的多样化,开发及测试用例只能覆盖大多数使用场景,在其他环境下,解析机制差异、内容动态变化等,都是导致非预期效果的原因。
防御性CSS不仅仅是为了兼容其他少数场景,避免边界情况,更大的价值在于提升团队协作的可能性。防御性CSS的意义类似JS中的try...catch, 他可能无法缩短需求开发的时间,但却是你程序正确运行和稳定运行的最后一道防线,更何况JS的错误只有在用户交互后才有感知,而CSS一旦出错,直接赤裸裸的展现在用户面前,直接影响用户的使用率和留存率。
都说编程风格分为三种:能跑就行风格、中规中矩风格、锦上添花风格。能跑就行风格代表的是:每一个设定和判断都和当次需求贴合的严丝合缝,如同山羊走钢丝,摇摇欲坠,但就是不倒,不得不令人称奇,但这种风格,不仅对编程人员要求极高,而且十分不利于团队协作,一旦意料意外的情景发生或者需求变更,带来的是雪崩式的改动;中规中矩风格概述为,该写注释的地方写注释,该写思路的地方写自己这么做的理由,该兜底处理的地方做拦截处理,程序的鲁棒性和可维护性直接拉满;万无一失风格更多的像是处女座,追求极致和完美,在中规中矩风格上再增一抹亮色,年轻时候的“雷布斯”就是典型代表。防御性CSS的目的就是从技术上尽可能的改变编程者能跑就行的侥幸心理,提升项目的可用性和可维护性。其目的也可以归纳为让你的项目做到:跑起来不出错,改起来不骂人。
防御性CSS的作用是对常规CSS的兜底,是实现项目稳定性建设重要但极其容易被忽视的一环。
属性背景:flex-wrap是flex布局中的属性,其作用是控制flex容器内元素所占空间超出flex容器空间时是否折行。
防御原因:flex-wrap属性默认是不折行,容易忽略多元素溢出兜底;为兜底,请设置flex-wrap: wrap;
意外后果:内部元素被裁剪,或flex容器出现滚动条;
应用场景:
1)开发中flex容器空间够用,但小尺寸屏幕会溢出;
2)内容由服务端下发,元素个数无法提前预支,超于预期时导致flex容器出现滚动条或内部元素被裁剪;
代码:
.options-list {
display: flex;
flex-wrap: wrap;
}
示例场景
属性背景:margin作用是调整元素的外边距。用于指定元素与周围空间的距离关系。
防御原因:防止元素与元素之间挤压空间,造成重叠等情况;
意外后果:元素重叠或被挤压;
应用场景:
1)内容所占空间无法保证与其他元素不存在挤压的场景;
代码:
.section__title {
margin-right: 1rem;
}
示例场景
背景:当文本长度超出容器时,该如何显示。
意外后果:文本折行,样式不统一;
应用场景:
1)要求列表表现一致但文本长度不可控;
此处假设与设计最终商定,超出部分以省略显示,那么,意外兜底的样式代码为:
.username {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
场景示例:
背景:通常,服务器下发的图片尺寸以及用户自定义上传的图片,显示在页面时,不可能百分百与容器尺寸贴合,不可避免的会遇到图片的放缩处理。
意外后果:图像被拉伸或压缩;
应用场景:
1)服务端下发多种不确定尺寸的图片;
2)用户自定义上传图片且需要预览和编辑;
.card__thumb {
object-fit: cover;
}
demo链接:https://monageju.github.io/Blog/object_fit.html
场景示例:
背景:overscroll-behavior是overscroll-behavior-x和overscroll-behavior-y的简写属性,它控制的是元素滚动到边界时的表现。换个能听得懂的说法:在JS世界里,有事件冒泡机制,你可以通过event的stopPropagation方法去阻止冒泡的发生,同样,在CSS世界里,滚动也有冒泡机制,当内部元素滚动到边界时,如果继续滚动,会带动外层祖先元素发生滚动,这种现象被称为滚动链,为了方便记忆,你也可以把他形象的记忆为滚动冒泡。而overscroll-behavior这个属性,就是类似event的stopPropagation方法阻止冒泡事件一样,提供给开发者去控制内层元素是否可以发生”冒泡“带动外层元素滚动的属性。
意外后果:”滚动冒泡“ 或 ”滚动穿透“;
应用场景:
1)页面存在多层滚动元素,需要单独控制每层滚动是否引起外层滚动;
.child {
overscroll-behavior-y: contain;
overflow-y: auto;
}
demo链接:https://monageju.github.io/Blog/overscroll_behavior.html
场景示例:如demo链接示例
理解了overscroll-behavior属性的作用,现在我们来看点拓展的东西:
首先来看下overscroll-behavior的属性值有哪些:
auto - 默认。元素的滚动会传播给祖先元素。
contain - 阻止滚动链接。滚动不会传播给祖先,但会显示元素内的原生效果。例如,Android 上的炫光效果或 iOS 上的回弹效果,当用户触摸滚动边界时会通知用户。注意:overscroll-behavior: contain 在 html 元素上使用可防止滚动导航操作。
none - 和 contain 一样,但它也可以防止节点本身的滚动效果(例如 Android 炫光或 iOS 回弹)。
这里有两个效果:一是下拉刷新,二是炫光回弹,这里有个demo可以看到具体效果:链接传送门
下拉刷新是原生支持的功能,如果项目要求自定义下拉刷新效果,除了要考虑如何实现自定义,还要考虑如何去掉默认原生下拉刷新,否则就会出现两个并存的下拉刷新,而去掉原生的下拉刷新也很简单,只需要在 body 或 html 元素添加如下代码:
body {
/* 禁用滚动冒泡,但是依然可以进行下拉刷新和炫光和回弹效果以及滑动导航 */
overscroll-behavior-y: contain;
}
至于禁用炫光和回弹效果,其实是应用overscroll-behavior属性的none属性值,具体代码如下:
body {
/* 禁用默认的下拉刷新和炫光和回弹效果,但是依然可以进行滑动导航 */
overscroll-behavior-y: none;
}
除了上述描述的两个效果,其实还有一个效果:手势导航,如左滑退出及右滑前进功能;而如果要禁用手势导航,可以使用如下代码:
body {
/* 禁用滑动导航 */
overscroll-behavior-x: none;
}
背景:CSS变量可以实现动态控制元素属性,但是当CSS变量未定义或无效时,造成变量值异常,此时,元素的样式将会脱离预期,而变量默认值可以实现异常兜底,保证变量值异常时页面依然能运行。需要额外说明的是,备用值并不是用于实现浏览器兼容性的。如果浏览器不支持CSS自定义属性,备用值也没什么用。它仅对支持CSS自定义属性的浏览器提供了一个备份机制,该机制仅当给定值未定义或是无效值的时候生效,函数的第一个参数是自定义属性的名称。如果提供了第二个参数,则表示备用值,当自定义属性值无效时生效。
意外后果:因失去宽高等变量值而不显示或变形;
应用场景:
.item {
color: var(--my-var, red); /* Red if --my-var is not defined */
}
背景:当需求要求完整展示某个列表数据,但列表数据所占空间无法固定时,为避免部分内容过宽、过高突破固定空间破坏布局,可以使用弹性尺寸 min-* 或者 max-* , 这样能自动适应部分内容所占空间过大或过小带来的样式美观问题;
意外后果:占用空间过大或过小,破坏布局或不美观;
应用场景:
.hero {
min-height: 350px;
}
场景示例:
max-width的使用场景:
如果对每一个元素使用固定的width,则当内容空间大于容器尺寸时,将发生溢出,此时,需要使用min-width 限制最小宽度,当超出尺寸时,能够实现自动适配。
背景:使用图片作为容器的背景图,当容器的尺寸大于图片尺寸时,默认背景图会重复,如果你在开发中忽略了上述问题,则会出现背景图重复的问题;
场景示例:
解决办法:
代码如下 :
background-image: url('..');
background-repeat: no-repeat;
解决后效果:
背景:媒体查询的使用更像是CSS中的条件判断,它会根据你定义的条件,当条件满足时,条件内的样式生效;
举例:当屏幕的宽度小于600px时,body背景色为红色;当屏幕宽度介于600-800px之间时,body背景色为黄色;当屏幕宽度大于800px时,body的背景色为蓝色;
示例代码:
/* 将 body 的背景色设置为蓝色 */
body {
background-color: blue;
}
/* 在小于或等于 800 像素的屏幕上,将背景色设置为黄色 */
@media screen and (max-width: 800px) {
body {
background-color: yellow;
}
}
/* 在 600 像素或更小的屏幕上,将背景色设置为红色 */
@media screen and (max-width: 600px) {
body {
background-color: red;
}
}
demo链接如下:https://monageju.github.io/Blog/media.html
背景:当需要在图片上层展示文字时,如果图片加载失败,而外层容器的背景色和文字颜色接近,那么文字的展示效果就不理想;
举例:容器背景设置为黑色,图片为橙色,文字颜色为近黑色,当图片加载失败时,文字的背景色直接变为容器的背景色,文字与容器背景色重合,示例如下;
解决后效果:
解决代码:
.card__img {
background-color: #FFF;
}
至此,即使图片加载失败,图片上的问题依然可以正常显示;至于图片加载失败时左上角的“破图”标记,可以使用伪类进行遮挡美化;
背景:当容器的空间固定时,如果内容超出容器,为正常显示完所有内容,同时不扩展所占空间,会使用overflow属性控制超出部分自动滚动展示,同时给与滚动条样式提示有剩余内容,但如果该属性使用不当,会造成样式很难看;
举例:overflow属性有两个作用很相近的属性值,一个是scroll, 另一个是auto; 这两个属性值都能实现当内容大于所占空间时滚动展示,不同点在于使用scroll属性无论内容是否超出容器空间,都会展示滚动条,而auto属性会分辩条件,内容超出时才会展示滚动条,为超出时则会自动隐藏,样式上较为美观;
解决代码:
.box {
overflow-y: auto;
}
场景示例:
背景:接技能十一,当我们正确使用了overflow:auto就万事大吉了吗?也不尽然。
设想这样一个场景:有一个宽度100vw,高度为100vh的容器盒子,容器内展示商品卡片,滑动到页面底部时,触发滑动加载,当触发懒加载时,容器内商品卡片占用的高度已经超出100vh,依据外层容器设置的overflow:auto,内容超出时会展示滚动条,滚动条的出现,使得页面不得不给滚动条让出一定的宽度,这个切换的场景中,由于不得不给滚动条让位置,最外层的元素发生了元素宽度变化,产生了重排的效果,有没有可能避免这一次不必要的重排呢?答案是有的。
大家一定还记得vue的指令中有两个很相像的指令v-if 和 v-show, 他们俩的原理和区别是什么?分别用在什么情景下?提醒到这,是不是有思路了?如果还没有,那也没关系,再提示一点点,既然要避免多余的一次重排,而滑动加载又不可避免,如果我一开始就预留好滚动条的位置,只是你看不见,到了滚动条应该出场的时候再让你看见,是不是就能避免不必要的重排了呢?现在再想想,这是不是就是v-show指令的设计原理?
CSS中有一个scrollbar-gutter属性,当它的值设置为stable时,就能够实现上述的这种功能,代码如下:
.box {
scrollbar-gutter: stable;
}
举例:
内容较短时预留滚动条空间,内容超出时显示滚动条;
背景:当给固定宽高容器设置背景图时,如果背景图尺寸超过容器宽高,图片会溢出,因此,最好在项目的resetCss中按照以下属性属性初始化:
img{
max-width: 100%;
object-fit: cover;
}
实例:
说明:position的粘性定位指的是通过用户的滚动,元素的position属性在 position:relative 与 position:fixed 定位之间切换;这对于需要使用滚动吸顶的场景非常方便;是典型的依据业务场景推动CSS技术发展的典例;
说明:根据W3C标准,批量分组选择选择器,如果分组中,其中一个无效,那么整个选择器都将会失效。因此,在遇到浏览器兼容属性时,切勿批量组合书写;
实例:
如果是如下书写方式,则该选择器没有任何问题,因为该分组选择器的所有选择器都有效:
h1, h2, h3 { font-family: sans-serif }
此时,它的作用等同于:
h1 { font-family: sans-serif }
h2 { font-family: sans-serif }
h3 { font-family: sans-serif }
但如果,是下面这种情况就不同了:
input::-webkit-input-placeholder,
input:-moz-placeholder {
color: #222;
}
该选择器使用了分组选择器,在确定的某一个浏览器中,该分组中只有一个选择器有效,而其他选择都是失效状态,根据规则,整个分组选择器都将会失效,因此,正确的做法应该是分开写,代码如下:
input::-webkit-input-placeholder {
color: #222;
}
input:-moz-placeholder {
color: #222;
}
此时,其效果才是符合预期的。
通常一个项目的稳定性指的都是逻辑层稳定和服务层稳定,CSS是极其容易被忽视的一层;当项目发生线上故障时,逻辑层和服务器可以通过日志查询、抓包等手段定位,而CSS问题则只能凭借经验和项目所运行环境进行大致推断,极难快速准确定位问题。在稳定性建设时,CSS的书写应该遵循“瞻前顾后”的防御性写法,尽可能的避免意外的边界情况,这才是防御性CSS的真实价值。