原文:# Write Better CSS By Borrowing Ideas From JavaScript Functions
网站布局的许多问题,例如不必要的副作用、痛苦的更新和脆弱的代码,当我们没有像编写 JavaScript 函数时那样小心翼翼地编写 CSS 时,就会发生。让我们看看如何从编写好的JavaScript函数中借用最佳实践和想法来编写CSS。
避免不必要的副作用:
当您更改系统中的某些内容时,它不应该意外地更改其他内容。对于CSS和JavaScript函数来说都是如此。
让我们以圆圈中的这个箭头图标为例:
看起来不错,但假设我们想要一个更窄的箭头图标:
现在包含的圆被压扁了!这是一个不需要的副作用的例子。使用较窄的箭头会破坏圆的形状。
如果我们在 DevTools 中检查该元素,我们可以看到包含圆圈的形状取决于内部图标的大小以及图标周围的填充。
理想情况下,内部图标不应改变包含圆的形状。以下是如何修复压扁图标的演示:
此示例中有两个改进:
1.容器尺寸已与内容隔离。
这样,我们可以使用不同的图标而不会弄乱容器。
2.容器的大小已与图标的位置分开。
由于我们使用 Flexbox 将图标水平和垂直居中,因此当容器大小更改时,图标的位置不会混乱。
此示例中的改进可能不适用于每个用例。例如,如果您需要容器的形状和大小随内部图标而更改,那么代码的“之前”版本可能更适合您。
编写方便的代码
JavaScript 中的函数参数提供了一种方便的方式来定义要控制的输入。这就像决定在电视上安装什么旋钮一样。
我们可以编写同样易于控制的 CSS。为此,让我们看看如何避免在编写 JavaScript 函数时可能遇到的两个问题:
参数太多
- 更多的配置会产生更多的开销,并且变得难以使用。
让我们假设我们有一个 JavaScript 函数来打开和关闭灯泡。如果我们的目标是使函数尽可能易于使用,我们可能需要一个参数来确定灯泡的状态。
这是一个易于使用的便捷功能:
switchLightbulb(ON);
将其与这个非常复杂的函数进行比较:
switchLightbulb(getConnectedWires, isCompleteCircuit, setUpBattery, batteryStatus, isUsingWallOutlet, hasPowerOutage, isElectricityBillPaid, etc);
在这种情况下,我们只想打开灯泡,但是当我们有太多参数时,我们有太多其他步骤要完成。当然,所有这些其他参数都很好,在某些情况下可能很有用。或者也许不是。无论如何,就函数的基本目标而言,它们超出了范围:在状态和 ON
OFF
状态之间切换。
类似的想法也适用于CSS。假设我们有一张带有图像、标题文本、正文文本和按钮的卡片。我们希望轻松更改卡片的宽度。
这个单行代码易于使用和理解:
.card {
max-width: 300px;
}
一种不太方便的方法是显式定义每个 .card
组件的: max-width
.card-image {
max-width: 300px;
}
.card-title-text {
max-width: 300px;
}
.card-body-text {
max-width: 300px;
}
.card-button {
max-width: 300px;
}
如果我们想在第二个示例中调整卡片的大小,我们需要进行四次更新才能实现一次更改。对于我们的用例,第一个示例使用起来要方便得多。
参数不足
- 较少的配置可能无法为您的使用案例提供足够的控制。
现在我们已经了解了参数过多可能出错的情况,让我们看看参数太少会发生什么。假设我们有一个函数,可以设置汽车的速度和转弯角度(以度为单位)。这些属性至少需要两个参数。
我们的函数可能如下所示:
setCarState({ speed: 60, turnAngleDegrees: 2 });
我们可以尝试更简洁并组合参数:
setCarState({ speedAndTurnAngle: 60 });
当然,这很简洁,但使用起来也很可怕。想象一下,一辆汽车加速也会转动方向盘!我们绝对不想将速度和转弯角度混为一谈,因为该参数无法提供足够的控制,并且会产生令人震惊的副作用。
在 CSS 中,也有类似的情况,我们希望控制一个参数而不影响另一个参数。例如,也许我们有另一个卡片组件,这次带有照片、姓名、简介和“阅读更多”按钮,我们希望在不影响照片尺寸的情况下更改卡片的宽度。
不幸的是,我们之前在卡片容器上使用 max-width
的方法将为我们的需求提供太少的参数:
.card {
max-width: 300px;
}
如前所述,卡片的子元素将适应卡的容器宽度,最大 300px
为 .这包括图像,随着容器宽度在以下 300px
缩小,图像将缩小。
我们需要的是照片的另一个参数:
.card {
max-width: 300px;
}
.photo {
width: max(150px, 50%);
}
由于我们想独立于卡片更改照片的宽度,因此我们需要足够的参数来提供该级别的控制。在这种情况下,这意味着通过在限定为照片范围的类上设置该宽度,为照片提供与卡片不同的宽度。
无论您需要更多参数还是更少参数,重要的部分是考虑您的用例。
编写弹性样式
在编写函数时,问一下,当输入发生变化时,输出会发生什么变化
我们可以在 CSS 中询问相同问题,例如:
- 如果元素的宽度受到限制,其高度会发生什么变化?
- 如果元素从窗口一侧滑入,页面的辅助功能会发生什么情况?
- 如果用户从鼠标转到触摸,悬停交互会发生什么情况?
假设我们有一个布局,其中三张卡片水平排列在一个容器中。
CSS设置 max-width: 900px
在容器上,每张卡都有一点喘息的空间 padding: 5vw
。这在表面上看起来不错,但有一个问题:容器有上限,而填充没有。随着屏幕变宽,内容被压碎。
可能的解决方案包括:
- 使用视口或容器断点来控制填充
- 使用 CSS
min()
函数设置填充的上限 - 使用固定单位(如像素),这些单位不会随窗口无限增长
这些解决方案的共同点是,它们考虑了视口宽度更改时发生的情况。同样,我们可以通过将布局视为输出并预测输入更改时会发生什么来避免许多 CSS 问题。
Ahmad Shadeed为这种技术起了一个很好的名字:防御性CSS。这个想法是,我们可以通过将样式视为输出 UI 的输入并预测会降低输出可用性的情况来“面向未来”的样式。
结论
编写布局代码不是在页面上布置内容,而是描述它们如何响应变化。出于这个原因,将CSS视为常量而不是函数是有风险的。
幸运的是,帮助我们编写好函数的相同想法可以帮助我们编写好的 CSS,即:
- 避免不必要的副作用
- 使用正确的参数
- 使用正确的参数