很多时候,我们希望有一种方法可以避免某种CSS问题或行为的发生。我们知道,网页内容是动态的,网页上的东西可以改变,从而增加了出现CSS问题或奇怪行为的可能性。
防御式 CSS是一个片段的集合,可以帮助我编写受保护的CSS。换句话说,就是将来会有更少的bug出现。
1.Flexbox 包裹
CSS flexbox 是目前最有用的CSS布局功能之一。在一个包装器上添加 display: flex,让子项挨着排序。
问题是,当空间不足时,那些子项默认不会被包裹成一个新的行。我们需要用 flex-wrap: wrap 来改变这一行为。
下面是一个典型的例子。
.options-list {
display: flex;
}
当空间较少时,会出现水平滚动。这应该是预料之中的,实际上并不是一个 "问题"。
.options-list {
display: flex;
flex-wrap: wrap;
}
使用flexbox时,一般的经验法则是允许包裹,除非你想要一个滚动的包裹。这是另一回事,但尽量使用 flex-wrap 来避免意外的布局行为(在我们的例子中,是水平滚动)。
2.间距
我们开发者需要考虑不同的内容长度。这意味着,间距应该添加到组件中,即使它看起来不需要。
在这个例子中,我们在右边有一个section标题和一个操作按钮。目前,它看起来还不错。但是,如果标题再长一些,会发生什么呢?
注意到文本太靠近按钮了吗?这里,你可能会考虑多行换行,但现在,我们先关注距。
如果标题有空格和文本截断,我们不会看到这样的问题。
.section__title {
margin-right: 1rem;
}
3.长内容
在构建布局时,考虑到长的内容是很重要的。正如你在前面所看到的,当章节的标题太长时就会被截断。这是可选的,但对于某些UI来说,考虑到这一点很重要。
对我来说,这是一种防御性的CSS方法。在 "问题 "真正发生之前就去解决它,这很好。
这里有一份人名清单,现在看起来很完美
然而,由于这是用户生成的内容,我们需要小心如何防御布局,以防某些内容太长。请看下图:
在这种布局中,一致性非常重要。为了实现这一点,我们可以使用 text-overflow和它的好友来简单地截断名称。
4. 防止图像被拉伸或压缩
在无法控制图片高宽比的情况下,如果用户上传的图片与高宽比不符,最好提前考虑并提供解决方案。
在下面的例子中,我们有一个带有照片的卡片组件。它看起来不错。
当用户上传一个不同大小的图像时,它将被拉伸。这可不是什么好事。看看图像是如何被拉伸的!
最简单的修复方法是使用CSS object-fit。
.card__thumb {
object-fit: cover;
}
在项目层面上,我倾向于为所有图像添加 object-fit,以避免出现意外的结果。
5.锁定滚动链接
你是否曾经打开一个模态并开始滚动,然后当你到达终点并继续滚动时,模态下面的内容(主体元素)会滚动?这就是所谓的滚动链。
默认情况下,当触及页面顶部或者底部时(或者是其他可滚动区域),移动端浏览器倾向于提供一种“触底”效果,甚至进行页面刷新。你可能也发现了,当对话框中含有可滚动内容时,一旦滚动至对话框的边界,对话框下方的页面内容也开始滚动了——这被称为“滚动链”。。
在过去的几年里,有一些黑科技来实现这一点,但现在,我们只需要使用CSS 即可,这要感谢overscroll-behavior CSS属性。
在下面的图中,可以看到默认的滚动链接行为。
为了提前避免这种情况,我们可以将其添加到任何需要滚动的组件中(例如:聊天组件、移动菜单...等)。这个属性的好处是,在有滚动之前,它不会产生影响。
.modal__content {
overscroll-behavior-y: contain;
overflow-y: auto;
}
6. CSS变量回退
CSS变量在网页设计中得到了越来越多的应用。我们可以应用一种方法,在CSS变量值因某种原因为空的情况下,以一种不破坏体验的方式使用它们。
通过 JS 输入CSS变量的值时特别有用。下面是一个例子:
.message__bubble {
max-width: calc(100% - var(--actions-width));
}
变量 --actions-width 在 calc() 函数中被使用,其值来自 JS。假设 JS 由于某种原因失败了,会发生什么?max-width 会被计算为零。
我们可以提前避免这种情况,在 var() 中添加一个回退值。
.message__bubble {
max-width: calc(100% - var(--actions-width, 70px));
}
这样,如果变量没有定义,就会使用回退 (70px)。这种方法可以在变量可能失败的情况下使用。
7.使用固定宽度或高度
破坏布局的常见情况之一是对一个有不同长度内容的元素使用固定的宽度或高度。
固定高度
我经常看到主内容部分有固定的高度,而内容却大于这个高度,这就导致了布局的破坏。如下所示:
.main {
height: 350px;
}
为了避免这种情况出现,可以使用 min-height 代替 height:
固定宽度
你有没有见过按钮,它的标签离左右边缘太近?这是由于使用了固定宽度。
.button {
width: 100px;
}
如果按钮的标签大于100px,它将靠近边缘。如果它太长,文本会泄露出来。这是不好的!
为了解决这个问题,我们可以简单地用 min-width 代替 width。
.button {
min-width: 100px;
}
8.忘记 background-Repeat
很多时候,当使用一张大的图片作为背景时,我们往往会忘记考虑设计在大屏幕上观看时的情况。该背景将默认重复。
这在笔记本屏幕上大多不会看到,但在大屏幕上很常见。
为了提前避免这种行为,请确保使用重置 background-repeat。
.hero {
background-image: url('..');
background-repeat: no-repeat;
}
9.垂直媒体查询
有时,我们很想建立一个组件,只通过调整浏览器的宽度进行测试。根据浏览器的高度进行测试可以发现一些有趣的问题。
这里有一个我见过多次的例子。我们有一个带有主要和次要链接组件。次要链接应该位于旁白部分的最底部。
考虑一下下面的例子。主导航和次导航看起来还不错。在我看到的这个例子中,开发者给二级导航添加了 position: sticky,这样它就可以粘在底部了。
然而,如果浏览器的高度较小,bug 就来了。注意这两个导航是如何重叠的。
通过使用CSS垂直媒体查询,我们可以避免这个问题。
@media (min-height: 600px) {
.aside__secondary {
position: sticky;
bottom: 0;
}
}
这样,只有当视口高度大于或等于600px 时,二级导航才会被粘在底部。好多了,对吗?
可能有更好的方法来实现这一行为(比如使用 margin-auto),但在这个例子中专注于垂直查询。
10. 使用 justify-content:space-between
在一个 flex 的容器中,我们可能会使用 justify-content 来使子项目之间有一定的间距。如果有一定数量的子项目,布局看起来会很好。然而,当它们增加或减少时,布局会看起来很奇怪。
考虑以下例子:
我们有一个有四个项目的 flex 容器。每个项目之间的间距不是 gap 或 margin,它之所以存在是因为容器有 justify-content: space-between。
.wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
当项目的数量少于4个时,将发生以下情况:
这并不是好事。对此有不同的解决方案:
- margin
- flexbox gap(谨慎使用)
- padding(可应用于每个子元素的父元素)
- 增加空的元素,作为间隔。
为了简单起见,我使用 gap。
.wrapper {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
11. 图片上的文字
当在图片上放置文本时,必须考虑到图像无法加载的情况。文本会是什么样子。下面是一个例子:
文本看起来是可读的,但当图像加载失败时,它的可读性变得很差。
我们通过给元素添加一个背景色来轻松解决这个问题。这个背景只有在图片加载失败时才会显示出来。
12.小心CSS网格中的固定值
假设我们有一个包含aside和main的网格。CSS看起来是这样的:
.wrapper {
display: grid;
grid-template-columns: 250px 1fr;
gap: 1rem;
}
由于缺乏空间,这在小的视口尺寸上会出现问题。为了避免这样的问题,在使用上述CSS网格时,一定要使用媒体查询。
@media (min-width: 600px) {
.wrapper {
display: grid;
grid-template-columns: 250px 1fr;
gap: 1rem;
}
}
13.只在需要的时候显示滚动条
我们可以控制显示滚动条或不只是在有很长的内容的情况下。尽管如此,强烈建议使用auto作为overflow的值。考虑以下例子:
请注意,即使内容很短,也有一个滚动条可见。这对一个用户界面来说并不是好事。作为用户,在不需要滚动条的情况下看到滚动条是很混乱的。
.element {
overflow-y: auto;
}
使用overflow-y: auto,滚动条只有在内容较长时才可见。否则,它就不显示。
14.Scrollbar Gutter
另一件与滚动有关的事情是 Scrollbar Gutter。以前面的例子为例,当内容变长时,增加一个滚动条会导致布局的转移。布局移动发生的原因是为滚动条保留了一个空间。
Scrollbar Gutter 是内边框边缘和外填充边缘之间的空间。对于经典滚动条,Scrollbar Gutter 的大小与滚动条的宽度相同。这些滚动条通常是不透明的,并从相邻的内容中占用一些空间。
请看下图:
请注意,由于显示了滚动条,当内容变长时,它是如何移位的。我们可以通过使用scrollbar-gutter属性来避免这种行为。
.element {
scrollbar-gutter: stable;
}
15. CSS Flexbox中的最小内容尺寸
如果一个 flex 项目中的文本元素或图像大于或长于该项目本身,浏览器就不会缩小它们。这是Flexbox 的默认行为。考虑以下例子:
.card {
display: flex;
}
当标题有一个很长的词时,它不会被包成一个新行。
即使我们使用 overflow-wrap: break-word,也不会起作用。
.card__title {
overflow-wrap: break-word;
}
要改变这种默认行为,我们需要将 flex 项目的 min-width 设置为 0。这是因为 min-width 的默认值是 auto,溢出会发生。
.card__title {
overflow-wrap: break-word;
min-width: 0;
}
同样也适用于 flex-direction:column 布局,对应的使用 min-height: 0。
16. CSS网格中的最小内容尺寸
与flexbox类似,CSS grid对其子项目有一个默认的最小内容尺寸,即auto。这意味着,如果有一个元素比网格项大,它将溢出。
在上面的例子中,我们在主部分中有一个 carousel。
<div class="wrapper">
<main>
<section class="carousel"></section>
</main>
<aside></aside>
</div>
@media (min-width: 1020px) {
.wrapper {
display: grid;
grid-template-columns: 1fr 248px;
grid-gap: 40px;
}
}
.carousel {
display: flex;
overflow-x: auto;
}
由于 carousel是一个 flex 布局,当内容超出时,默认是不会换行的,所以会出现水平滚动的。
为了解决这个问题,我们有三种不同的解决方:
- 使用 minmax()
- 将 min-width 应用于网格项目
- 在网络中添加 overflow: hidden
作为一种防御性的CSS机制,我会选择第一种,即使用 minmax() 函数。
@media (min-width: 1020px) {
.wrapper {
display: grid;
grid-template-columns: minmax(0, 1fr) 248px;
grid-gap: 40px;
}
}
Auto Fit Vs Auto Fill
在使用CSS网格 minmax() 函数时,决定使用 auto-fit 还是 auto-fill 的关键字很重要。一旦使用不当,会导致意外的结果。
当使用minmax()函数时,auto-fit关键字将扩展网格项目以填补可用空间。而auto-fill将保留可用的空间,而不改变网格项的宽度。
也就是说,使用auto-fit可能会导致网格项目太宽,特别是当它们小于预期时。考虑一下下面的例子。
作者:Ahmad Shadeed 译者:前端小智 来源:ishadeed 原谅:https://ishadeed.com/article/defensive-css/