写过 JS 的人都踩过这个坑——CSS 属性读了半天还是个字符串,今天 CSS Typed OM 把这件事彻底原生化了

每次用 JS 读 CSS 属性都要和字符串打交道:el.style.opacity 返回的是字符串,不是数字。想做个简单的加法都要 parseFloat 转一圈。更坑的是,你以为自己在做加法:

el.style.opacity += 0.1; // 结果是字符串拼接,不是数值加法!

Firefox 155 稳定版把这件事彻底原生化了——CSS Typed OM 让所有 CSS 属性在你的 JS 里以类型化对象的形式存在,读出来是什么类型、写进去还是什么类型,数学运算不用再绕弯。

为什么字符串式的 CSSOM 一言难尽

传统 CSSOM 暴露的属性值全是字符串:

const width = div.style.width; // "200px",字符串
const opacity = window.getComputedStyle(div).opacity; // "0.5",字符串

这带来一连串问题:数值运算要手动 parse、字符串拼接 bug、单位处理靠猜、自定义属性读不到 computed 值。

Typed OM 核心:两种 StylePropertyMap

读 computed 样式:computedStyleMap()

const styleMap = element.computedStyleMap();
const opacity = styleMap.get("opacity");
console.log(opacity.value, opacity.unit); // 0.5, "number"
const width = styleMap.get("width");
console.log(width.value, width.unit); // 200, "px"
// 自定义属性也能读 computed 值
const brandColor = styleMap.get("--brand-color");

写行内样式:attributeStyleMap

element.attributeStyleMap.set("opacity", CSS.number(0.3));
element.attributeStyleMap.set("width", CSS.px(200));
element.attributeStyleMap.set("transform", CSS.rotate(45));
// 读出来是什么类型,加进去还是什么类型
const op = element.attributeStyleMap.get("opacity");
op.value += 0.1; // 0.4,不是字符串拼接
element.attributeStyleMap.set("opacity", CSS.number(op.value));

delete():终于可以删单个属性了

Firefox 155 新增的支持:

// 以前删样式只能 clear 全量
// element.attributeStyleMap.clear();
// 现在可以删一个
element.attributeStyleMap.delete("opacity");
// 配合 has/set,形成完整 CRUD
if (element.attributeStyleMap.has("opacity")) {
  const op = element.attributeStyleMap.get("opacity");
  element.attributeStyleMap.set("opacity", CSS.number(op.value * 0.8));
}

实际场景:动画插值

Typed OM 最有价值的场景是动画计算:

// 以前:手动 lerp + 字符串转换
const from = parseFloat(getComputedStyle(el).opacity);
el.style.opacity = String(from + (1 - from) * 0.5);

// Typed OM:直接操作 number
const fromVal = el.computedStyleMap().get("opacity").value;
el.attributeStyleMap.set("opacity", CSS.number(fromVal + (1 - fromVal) * 0.5));

兼容性

Chrome 66+ 从 2018 年就开始支持,Firefox 155 稳定版补全了 StylePropertyMap.delete()。Safari 暂未支持,约 70% 全球覆盖。建议渐进增强:

if (element.attributeStyleMap) {
  element.attributeStyleMap.set("opacity", CSS.number(0.5));
} else {
  element.style.opacity = "0.5";
}

下一步

如果你在写任何涉及 CSS 数值读写的 JS——动画、拖拽、resize 监听——先数一下代码里有多少 parseFloat。Typed OM 可以把这些全部替换掉,bug 会少很多。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 13 阅读