概要
CSS Typed OMがChrome66でリリースされNew in Chrome 66とWorking with the new CSS Typed Object Modelという記事が出ていたのでそれを読んでみてどんな感じかまとめた。
※記載しているコードは参考にした記事のものをそのまま拝借しております。
本題
これまでjavascriptからCSSプロパティを使用する際は、element.style
を利用してCSSOMから値を取得していた。しかしながら、取得していた値の型はstringであった。
el.style.opacity = 0.3;
console.log(typeof el.style.opacity);
> 'string' // A string!?
ここでopacity
プロパティの値を変える場合を例に挙げる。opacity
プロパティを変更するには、取得したstring型の値をnumber型にcastし、値を変更してあげる必要があるが、これは理想的なことではない。
function step(timestamp) {
const currentOpacity = parseFloat(el.style.opacity); // こんな感じ
const newOpacity = currentOpacity + 0.01;
element.style.opacity = newOpacity;
if (newOpacity <= 1) {
window.requestAnimationFrame(step);
}
}
そのため、CSS Typed OMでは従来のようなelement.style
を利用してcssの値を取得するのではなく.attributeStyleMap
プロパティもしくは.styleMap
プロパティを利用する。
// Element styles.
el.attributeStyleMap.set('opacity', 0.3);
typeof el.attributeStyleMap.get('opacity').value === 'number' // Yay, a number!
// Stylesheet rules.
const stylesheet = document.styleSheets[0];
stylesheet.cssRules[0].styleMap.set('background', 'blue');
.attributeStyleMap
プロパティと.styleMap
プロパティではStylePropertyMap
オブジェクトを取得できるが、StylePropertyMap
はmap-like
オブジェクトのため、get/set/keys/values/entriesの操作が可能で、取得した値に対して容易で柔軟に操作が可能である。
// All 3 of these are equivalent:
el.attributeStyleMap.set('opacity', 0.3);
el.attributeStyleMap.set('opacity', '0.3');
el.attributeStyleMap.set('opacity', CSS.number(0.3)); // see next section
// el.attributeStyleMap.get('opacity').value === 0.3
// StylePropertyMaps are iterable.
for (const [prop, val] of el.attributeStyleMap) {
console.log(prop, val.value);
}
// → opacity, 0.3
el.attributeStyleMap.has('opacity') // true
el.attributeStyleMap.delete('opacity') // remove opacity.
el.attributeStyleMap.clear(); // remove all styles.
利点
これまでのCSSOMに対する操作と比較すると30%のパフォーマンス向上があるため、JavaScriptアニメーションにおいて重宝する。
el.attributeStyleMap.set('opacity', 0.3);
el.attributeStyleMap.has('opacity'); // true
el.attributeStyleMap.delete('opacity');
el.attributeStyleMap.clear(); // remove all styles
加えて、string型からnumber型への変換忘れによるバグを取り除いたり、プロパティへ代入する値を適切な値に変換してくれたりする。
el.style.opacity = 3;
const opacity = el.computedStyleMap().get('opacity').value;
console.log(opacity);
> 1 <- 3ではなく1が入っている
他にも単位変換や数式や値の一致を扱う際に利用できるメソッドも用意されている。
// Convert px to other absolute/physical lengths.
el.attributeStyleMap.set('width', '500px');
const width = el.attributeStyleMap.get('width');
width.to('mm'); // CSSUnitValue {value: 132.29166666666669, unit: "mm"}
width.to('cm'); // CSSUnitValue {value: 13.229166666666668, unit: "cm"}
width.to('in'); // CSSUnitValue {value: 5.208333333333333, unit: "in"}
CSS.deg(200).to('rad').value // 3.49066...
CSS.s(2).to('ms').value // 2000
CSS.deg(45).mul(2) // {value: 90, unit: "deg"}
CSS.percent(50).max(CSS.vw(50)).toString() // "max(50%, 50vw)"
// Can Pass CSSUnitValue:
CSS.px(1).add(CSS.px(2)) // {value: 3, unit: "px"}
// multiple values:
CSS.s(1).sub(CSS.ms(200), CSS.ms(300)).toString() // "calc(1s + -200ms + -300ms)"
// or pass a `CSSMathSum`:
const sum = new CSSMathSum(CSS.percent(100), CSS.px(20)));
CSS.vw(100).add(sum).toString() // "calc(100vw + (100% + 20px))"
const width = CSS.px(200);
CSS.px(200).equals(width) // true
const rads = CSS.deg(180).to('rad');
CSS.deg(180).equals(rads.to('deg')) // true
エラーハンドリングもできるらしい。
try {
const css = CSSStyleValue.parse('transform', 'translate4d(bogus value)');
// use css
} catch (err) {
console.err(err);
}
ブラウザでサポートされているかを判定
Chrome66では実装済み、Firefoxでは現在実装中であるためサポートされていない場合の処理も必要である。
if (window.CSS && CSS.number) {
// Supports CSS Typed OM.
}
結論
スタイルに関する操作を行う際のバグを減らしたりパフォーマンス向上を見込めるのでぜひ使ってみたいと感じた。