用JS控制CSS

1,900 阅读4分钟

背景

在真实开发场景中,有时候我们会遇到通过JS的逻辑计算结果,我们需要动态改变页面上某个元素的CSS展现方式。通常我的做法会有以下几种:

  • 在原生JS开发中,通过获取DOM节点元素,根据计算结果,设置DOM节点元素的style属性,从而达到更改页面某个元素的样式。
if (change) {
  dom.style.color = "red";
} else {
  dom.style.color = "black";
}
  • 如果要更改的CSS属性过多,则会采用动态加载CSSclass类名的方式,提前写好class类名,在获取DOM节点元素后,根据计算结果,设置DOM节点元素的 className属性或者通过classList属性的add方法,进行一组class或者单个class设置。
.red-font {
  color: red;
}
.black-font {
  color: black;
}
// 设置单个class
dom.className = 'red-font';


// 设置一组class
// 同时也可以使用 add 方法添加一个class
dom.classList.add('red-font', 'black-font');

采用JS控制CSS变量法更改样式

在最近的项目样式重构中,我遇到了这样一个场景,通过JS的逻辑计算结果,我只需要更改某个Button按钮的宽度,但是这个按钮的宽度又会对同级元素宽度产生影响。样例CSS源码如下:

.btn {
  width: 30px;
  ...
}
.btn-next-elment {
  width: calc(100% - 30px);
  ...
}

看到这里,我有一种想法,为啥当初写的时候,不采用 flex 布局?这样就不需要计算同级相邻元素的宽度了,而让相邻元素能够做到自适应,当然,存(懒)在(得)即(探)合(探)理,不然怎么会有获取新知识点的机会?

在这种场景下,前面提到的两种更改样式的方式也都可以生效,但是也会有一定的繁琐。

  • 如果我们采用设置 style 属性的方式,那么在CSS权重里,js设置的这个宽度,则是比较高的。以后如果我们想通过 class 类名的方式再对宽度进行维护,那将变得困难。其次,我们这里需要对两个元素的宽度进行设置,我感觉这并不是一种好的方式。
  • 那我们采用动态设置 class 类名的方式?这种方式需要我们写两套样式,同时仍需要获取两个DOM,进行设置。破坏性、工作量也是极大的,作为一个有追求的人,非我所愿。

难道就没有一种方式,像JS提取变量的方式,修改一处,而影响所有吗?等等,变量?CSS也是支持变量的。所以答案是有的。

Show you my code

.btn {
  width: var(--btn-width);
  ...
}
.btn-next-element {
  width: calc(100% - var(--btn-width));
  ...
}
const btnParentElement = document.querySelector('.btn').parentElement;
// change 变量值是JS逻辑计算的结果
const btnParentElement.style.setProperty('--btn-width', change ? '60px' ? '100px');

通过上面的代码,我们可以看出,通过CSS的 var 方法,通过将按钮的宽度提取成变量,我们将两个DOM元素的宽度绑定在了 --btn-width 这个变量上。在JS层面,我们通过对按钮元素的 父元素 进行变量配置,实现了动态设置按钮宽度,并且让按钮相邻元素的宽度跟着改变的效果。

这里说明一下,在我的这种场景下,变量被设置到按钮元素的 父元素 上是为了让按钮元素的相邻元素获取到 --btn-width 这个变量。在DOM节点上配置var变量,并不会像JS一样,存在变量提升,因此如果点前引用变量的节点的父级节点或者祖级节点不存在这个变量的话,是不能够引用的,所以千万不要认为 --btn-width 设置到 button 元素上就好,我吃过的亏,希望别再有人掉下来。

giphy.gif

结语

从此以后,根据JS的逻辑计算动态更改CSS样式的途径又多了一种。更多通过JS控制CSS变量更改样式的案例可以参考张鑫旭大神的文章《CSS变量对JS交互组件开发带来的提升与变革》。如果你有其他更改CSS样式的方式,欢迎探讨。