सीएसएस टाइप किए गए नए ऑब्जेक्ट मॉडल के साथ काम करना

एरिक बिडेलमैन

बहुत ज़्यादा शब्द हैं, पढ़ा नहीं गया

JavaScript में वैल्यू के साथ काम करने के लिए, अब सीएसएस के पास एक सही ऑब्जेक्ट-आधारित एपीआई है.

el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'

स्ट्रिंग जोड़ने और गड़बड़ियां बताने वाले दिन अब खत्म हो गए!

शुरुआती जानकारी

पुराना CSSOM

सीएसएस का कई सालों से ऑब्जेक्ट मॉडल (सीएसएसओएम) है. दरअसल, जब भी आप JavaScript में .style को पढ़ते/सेट करते हैं, तो आप उसका इस्तेमाल कर रहे होते हैं:

// Element styles.
el.style.opacity = 0.3;
typeof el.style.opacity === 'string' // Ugh. A string!?

// Stylesheet rules.
document.styleSheets[0].cssRules[0].style.opacity = 0.3;

सीएसएस टाइप किया गया नया OM

नया सीएसएस टाइप किया गया ऑब्जेक्ट मॉडल (टाइप किया गया ओएम) यह Houdini की कोशिशों का हिस्सा है. यह सीएसएस वैल्यू में टाइप, तरीके, और सही ऑब्जेक्ट मॉडल को जोड़कर, इस वर्ल्डव्यू को बड़ा करता है. स्ट्रिंग के बजाय, वैल्यू को JavaScript ऑब्जेक्ट के तौर पर दिखाया जाता है, ताकि सीएसएस में काम करने वाले और सही तरीके से बदलाव किए जा सकें.

element.style का इस्तेमाल करने के बजाय, स्टाइल को ऐक्सेस करने के लिए, एलिमेंट के लिए नई .attributeStyleMap प्रॉपर्टी और स्टाइलशीट के नियमों के लिए .styleMap प्रॉपर्टी का इस्तेमाल करें. ये दोनों नतीजे StylePropertyMap ऑब्जेक्ट दिखाते हैं.

// 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');

StylePropertyMap, मैप जैसे ऑब्जेक्ट होते हैं. इसलिए, वे सभी सामान्य चीज़ों (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.

ध्यान दें कि दूसरे उदाहरण में, opacity को स्ट्रिंग ('0.3') पर सेट किया गया है, लेकिन प्रॉपर्टी को बाद में पढ़ने पर एक नंबर वापस आता है.

फ़ायदे

आइए जानते हैं कि सीएसएस टाइपेड ओएम किन समस्याओं को हल करना चाहता है? ऊपर दिए गए उदाहरण (और इस पूरे लेख में) देखें, तो यह तर्क दिया जा सकता है कि सीएसएस टाइप किया गया ओएम, पुराने ऑब्जेक्ट मॉडल की तुलना में ज़्यादा वर्बोस है. मैं सहमत हूं!

टाइप किया गया ओएम को लिखने से पहले, इससे टेबल में शामिल होने वाली कुछ मुख्य सुविधाओं पर ध्यान दें:

  • बहुत कम गड़बड़ियां. जैसे, संख्या वाली वैल्यू हमेशा संख्याओं के तौर पर दी जाती हैं, स्ट्रिंग के तौर पर नहीं.

    el.style.opacity += 0.1;
    el.style.opacity === '0.30.1' // dragons!
    
  • अंकगणितीय संक्रियाएं और इकाई रूपांतरण. निरपेक्ष लंबाई की इकाइयों (जैसे px -> cm) और बुनियादी गणित के बीच बदलें.

  • वैल्यू क्लैंपिंग और राउंडिंग. ओएम के राउंड और/या क्लैंप वैल्यू टाइप की गई हैं, ताकि वे किसी प्रॉपर्टी के लिए स्वीकार की जाने वाली रेंज के अंदर हों.

  • बेहतर परफ़ॉर्मेंस. ब्राउज़र को स्ट्रिंग वैल्यू को क्रम में लगाने और डिसरियलाइज़ करने का काम कम करना पड़ता है. अब यह इंजन JS और C++ में, सीएसएस वैल्यू की मिलती-जुलती जानकारी का इस्तेमाल करता है. Tab Akins ने कुछ शुरुआत के मानदंड दिखाए हैं. इनकी वजह से, पुराने CSSOM और स्ट्रिंग के इस्तेमाल की तुलना में, टाइप किए गए OM को कार्रवाइयों/सेकंड में ~30% ज़्यादा पर रखा जाता है. requestionAnimationFrame() का इस्तेमाल करके, तेज़ी से होने वाले सीएसएस ऐनिमेशन के लिए यह काम ज़रूरी हो सकता है. crbug.com/808933 ब्लिंक में अतिरिक्त परफ़ॉर्मेंस को ट्रैक करती है.

  • गड़बड़ी ठीक करना. पार्स करने के नए तरीकों से, सीएसएस की दुनिया में गड़बड़ी से निपटने की सुविधा मिलती है.

  • "क्या मुझे ऊंट के केस वाले सीएसएस नाम या स्ट्रिंग का इस्तेमाल करना चाहिए?" अब अनुमान लगाने की कोई ज़रूरत नहीं है कि नाम ऊंट के जैसे या स्ट्रिंग (जैसे कि el.style.backgroundColor बनाम el.style['background-color']) हैं. टाइप किए गए ओएम में सीएसएस प्रॉपर्टी के नाम हमेशा स्ट्रिंग होते हैं और सीएसएस में लिखे गए नामों से मेल खाते हैं :)

ब्राउज़र सहायता और सुविधा का पता लगाने की सुविधा

टाइप किया गया OM 66 में लॉन्च हुआ और Firefox में लागू किया जा रहा है. Edge ने सहायता के लिए साइन अप दिखाए हैं, लेकिन अभी इसे अपने प्लैटफ़ॉर्म डैशबोर्ड में शामिल नहीं किया है.

सुविधा की पहचान करने के लिए, यह देखें कि CSS.* में न्यूमेरिक (संख्या वाली) फ़ैक्ट्री में से कोई एक तय की गई है या नहीं:

if (window.CSS && CSS.number) {
  // Supports CSS Typed OM.
}

एपीआई की बुनियादी बातें

स्टाइल ऐक्सेस करना

वैल्यू, सीएसएस टाइप किए गए OM में दी गई इकाइयों से अलग होती हैं. स्टाइल पाने से, ऐसे CSSUnitValue मिलते हैं जिनमें value और unit होते हैं:

el.attributeStyleMap.set('margin-top', CSS.px(10));
// el.attributeStyleMap.set('margin-top', '10px'); // string arg also works.
el.attributeStyleMap.get('margin-top').value  // 10
el.attributeStyleMap.get('margin-top').unit // 'px'

// Use CSSKeyWorldValue for plain text values:
el.attributeStyleMap.set('display', new CSSKeywordValue('initial'));
el.attributeStyleMap.get('display').value // 'initial'
el.attributeStyleMap.get('display').unit // undefined

कंप्यूट किए गए स्टाइल

कंप्यूटेड स्टाइल, window को एपीआई से HTMLElement computedStyleMap() को नए तरीके पर चले गए हैं:

पुराना सीएसएसओएम

el.style.opacity = 0.5;
window.getComputedStyle(el).opacity === "0.5" // Ugh, more strings!

नए टाइप किया गया ओएम

el.attributeStyleMap.set('opacity', 0.5);
el.computedStyleMap().get('opacity').value // 0.5

वैल्यू क्लैंपिंग / राउंडिंग

नए ऑब्जेक्ट मॉडल की एक अच्छी सुविधा यह है कि ऑटोमैटिक तरीके से क्लैंप करना और/या कंप्यूट किए गए स्टाइल की वैल्यू को राउंडिंग करना. उदाहरण के तौर पर, मान लें कि आपने opacity को ऐसी वैल्यू पर सेट करने की कोशिश की है जो स्वीकार की जा सकने वाली रेंज [0, 1] से बाहर है. टाइप किया गया OM, स्टाइल की गणना करते समय, को 1 को वैल्यू देता है:

el.attributeStyleMap.set('opacity', 3);
el.attributeStyleMap.get('opacity').value === 3  // val not clamped.
el.computedStyleMap().get('opacity').value === 1 // computed style clamps value.

इसी तरह, z-index:15.4 को 15 पर सेट किया जाता है, ताकि वैल्यू एक पूर्णांक बनी रहे.

el.attributeStyleMap.set('z-index', CSS.number(15.4));
el.attributeStyleMap.get('z-index').value  === 15.4 // val not rounded.
el.computedStyleMap().get('z-index').value === 15   // computed style is rounded.

सीएसएस की संख्या वाली वैल्यू

टाइप किए गए OM में दो तरह के CSSNumericValue ऑब्जेक्ट से संख्याएं दिखाई जाती हैं:

  1. CSSUnitValue - ऐसी वैल्यू जिनमें एक तरह की यूनिट शामिल है (उदाहरण के लिए, "42px").
  2. CSSMathValue - ऐसी वैल्यू जिनमें एक से ज़्यादा वैल्यू/इकाई हों, जैसे कि गणित के एक्सप्रेशन (उदाहरण के लिए, "calc(56em + 10%)").

इकाई मान

सामान्य संख्यात्मक वैल्यू ("50%") को CSSUnitValue ऑब्जेक्ट से दिखाया जाता है. हालांकि, ये ऑब्जेक्ट सीधे new CSSUnitValue(10, 'px')) बनाए जा सकते हैं, लेकिन ज़्यादातर मामलों में CSS.* फ़ैक्ट्री के तरीकों का इस्तेमाल किया जाएगा:

const {value, unit} = CSS.number('10');
// value === 10, unit === 'number'

const {value, unit} = CSS.px(42);
// value === 42, unit === 'px'

const {value, unit} = CSS.vw('100');
// value === 100, unit === 'vw'

const {value, unit} = CSS.percent('10');
// value === 10, unit === 'percent'

const {value, unit} = CSS.deg(45);
// value === 45, unit === 'deg'

const {value, unit} = CSS.ms(300);
// value === 300, unit === 'ms'

CSS.* तरीकों की पूरी सूची की जानकारी देखें.

गणित की वैल्यू

CSSMathValue ऑब्जेक्ट गणित के व्यंजकों को दिखाते हैं और उनमें आम तौर पर एक से ज़्यादा मान/इकाई होते हैं. इसका सामान्य उदाहरण सीएसएस calc() एक्सप्रेशन बनाना है, लेकिन सभी सीएसएस फ़ंक्शन के लिए तरीके उपलब्ध हैं: calc(), min(), max().

new CSSMathSum(CSS.vw(100), CSS.px(-10)).toString(); // "calc(100vw + -10px)"

new CSSMathNegate(CSS.px(42)).toString() // "calc(-42px)"

new CSSMathInvert(CSS.s(10)).toString() // "calc(1 / 10s)"

new CSSMathProduct(CSS.deg(90), CSS.number(Math.PI/180)).toString();
// "calc(90deg * 0.0174533)"

new CSSMathMin(CSS.percent(80), CSS.px(12)).toString(); // "min(80%, 12px)"

new CSSMathMax(CSS.percent(80), CSS.px(12)).toString(); // "max(80%, 12px)"

नेस्ट किए गए एक्सप्रेशन

ज़्यादा जटिल वैल्यू बनाने के लिए, गणित के फ़ंक्शन का इस्तेमाल करना थोड़ा मुश्किल होता है. शुरू करने के लिए, नीचे कुछ उदाहरण दिए गए हैं. मैंने उन्हें पढ़ने में आसान बनाने के लिए अतिरिक्त इंडेंट जोड़ा है.

calc(1px - 2 * 3em) को इस तरह बनाया जाएगा:

new CSSMathSum(
  CSS.px(1),
  new CSSMathNegate(
    new CSSMathProduct(2, CSS.em(3))
  )
);

calc(1px + 2px + 3px) को इस तरह बनाया जाएगा:

new CSSMathSum(CSS.px(1), CSS.px(2), CSS.px(3));

calc(calc(1px + 2px) + 3px) को इस तरह बनाया जाएगा:

new CSSMathSum(
  new CSSMathSum(CSS.px(1), CSS.px(2)),
  CSS.px(3)
);

अंकगणितीय संक्रियाएं

सीएसएस टाइप किए गए ओएम की सबसे काम की सुविधाओं में से एक यह है कि इससे CSSUnitValue ऑब्जेक्ट पर गणितीय ऑपरेशन किए जा सकते हैं.

बुनियादी कार्रवाइयां

बुनियादी कार्रवाइयां (add/sub/mul/div/min/max) काम करती हैं:

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))"

कन्वर्ज़न

निरपेक्ष लंबाई की इकाइयों को दूसरी इकाइयों में बदला जा सकता है:

// 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

Equality

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

सीएसएस ट्रांसफ़ॉर्म वैल्यू

सीएसएस ट्रांसफ़ॉर्म, CSSTransformValue की मदद से बनाए जाते हैं और ट्रांसफ़ॉर्म वैल्यू की कलेक्शन को पास करते हैं (जैसे कि CSSRotate, CSScale, CSSSkew, CSSSkewX, CSSSkewY). उदाहरण के लिए, मान लें कि आपको यह सीएसएस फिर से बनाना है:

transform: rotateZ(45deg) scale(0.5) translate3d(10px,10px,10px);

टाइप किए गए OM में अनुवाद किया गया:

const transform =  new CSSTransformValue([
  new CSSRotate(CSS.deg(45)),
  new CSSScale(CSS.number(0.5), CSS.number(0.5)),
  new CSSTranslate(CSS.px(10), CSS.px(10), CSS.px(10))
]);

कितने शब्दों में जानकारी दी जाए (lolz!) के अलावा, CSSTransformValue में कुछ शानदार सुविधाएं हैं. इसमें 2D और 3D ट्रांसफ़ॉर्म के बीच अंतर करने के लिए एक बूलियन प्रॉपर्टी है. साथ ही, ट्रांसफ़ॉर्म का DOMMatrix रिव्यू देने के लिए, .toMatrix() तरीका भी है:

new CSSTranslate(CSS.px(10), CSS.px(10)).is2D // true
new CSSTranslate(CSS.px(10), CSS.px(10), CSS.px(10)).is2D // false
new CSSTranslate(CSS.px(10), CSS.px(10)).toMatrix() // DOMMatrix

उदाहरण: क्यूब को ऐनिमेट करना

आइए, ट्रांसफ़ॉर्म को इस्तेमाल करने का एक व्यावहारिक उदाहरण देखते हैं. हम क्यूब को ऐनिमेट करने के लिए JavaScript और सीएसएस का इस्तेमाल करेंगे.

const rotate = new CSSRotate(0, 0, 1, CSS.deg(0));
const transform = new CSSTransformValue([rotate]);

const box = document.querySelector('#box');
box.attributeStyleMap.set('transform', transform);

(function draw() {
  requestAnimationFrame(draw);
  transform[0].angle.value += 5; // Update the transform's angle.
  // rotate.angle.value += 5; // Or, update the CSSRotate object directly.
  box.attributeStyleMap.set('transform', transform); // commit it.
})();

ध्यान दें:

  1. नंबर वाली वैल्यू का मतलब है कि हम सीधे गणित का इस्तेमाल करके ऐंगल बढ़ा सकते हैं!
  2. ऐनिमेशन के ज़रिए, हर फ़्रेम को टच करने या हर फ़्रेम पर वैल्यू को पढ़ने (जैसे कि box.style.transform=`rotate(0,0,1,${newAngle}deg)` नहीं) के बजाय, पहले से मौजूद CSSTransformValue डेटा ऑब्जेक्ट को अपडेट करके, इसकी परफ़ॉर्मेंस को बेहतर बनाया जाता है.

डेमो

अगर आपके ब्राउज़र पर टाइप किया गया OM काम करता है, तो नीचे आपको लाल रंग का क्यूब दिखेगा. जब आप क्यूब पर माउस ले जाते हैं, तो वह घुमना शुरू हो जाता है. यह ऐनिमेशन, CSS टाइप किए गए OM से चलता है! 🤘

सीएसएस कस्टम प्रॉपर्टी की वैल्यू

टाइप किए गए OM में सीएसएस var(), CSSVariableReferenceValue ऑब्जेक्ट बन जाता है. उनकी वैल्यू CSSUnparsedValue में पार्स कर दी जाती हैं, क्योंकि वे किसी भी तरह का (px, %, em, RGBa() वगैरह) ले सकते हैं.

const foo = new CSSVariableReferenceValue('--foo');
// foo.variable === '--foo'

// Fallback values:
const padding = new CSSVariableReferenceValue(
    '--default-padding', new CSSUnparsedValue(['8px']));
// padding.variable === '--default-padding'
// padding.fallback instanceof CSSUnparsedValue === true
// padding.fallback[0] === '8px'

अगर आपको किसी कस्टम प्रॉपर्टी की वैल्यू जाननी है, तो आपको ये काम करने होंगे:

<style>
  body {
    --foo: 10px;
  }
</style>
<script>
  const styles = document.querySelector('style');
  const foo = styles.sheet.cssRules[0].styleMap.get('--foo').trim();
  console.log(CSSNumericValue.parse(foo).value); // 10
</script>

रैंक के मान

ऐसी सीएसएस प्रॉपर्टी जो स्पेस से अलग की गई x/y पोज़िशन लेते हैं, जैसे कि object-position उन्हें CSSPositionValue ऑब्जेक्ट से दिखाया जाता है.

const position = new CSSPositionValue(CSS.px(5), CSS.px(10));
el.attributeStyleMap.set('object-position', position);

console.log(position.x.value, position.y.value);
// → 5, 10

वैल्यू को पार्स करना

टाइप किया गया OM, वेब प्लैटफ़ॉर्म पर पार्स करने के तरीके पेश करता है! इसका मतलब है कि सीएसएस की वैल्यू को प्रोग्राम के हिसाब से, इस्तेमाल करने से पहले, उसे पार्स किया जा सकता है! यह नई सुविधा शुरुआती गड़बड़ियों और गलत सीएसएस को पहचानने में काफ़ी मदद कर सकती है.

पूरी शैली पार्स करें:

const css = CSSStyleValue.parse(
    'transform', 'translate3d(10px,10px,0) scale(0.5)');
// → css instanceof CSSTransformValue === true
// → css.toString() === 'translate3d(10px, 10px, 0) scale(0.5)'

वैल्यू को CSSUnitValue में पार्स करें:

CSSNumericValue.parse('42.0px') // {value: 42, unit: 'px'}

// But it's easier to use the factory functions:
CSS.px(42.0) // '42px'

गड़बड़ी ठीक करना

उदाहरण - देखें कि क्या सीएसएस पार्सर इस transform वैल्यू से खुश होगा या नहीं:

try {
  const css = CSSStyleValue.parse('transform', 'translate4d(bogus value)');
  // use css
} catch (err) {
  console.err(err);
}

नतीजा

आखिर में, सीएसएस के लिए एक अपडेट किया गया ऑब्जेक्ट मॉडल होना अच्छा लगता है. स्ट्रिंग के साथ काम करना मुझे कभी ठीक नहीं लगा. सीएसएस टाइप किया गया OM API बहुत ज़्यादा शब्दों में होता है, लेकिन उम्मीद है कि इससे कम गड़बड़ियां और बेहतर परफ़ॉर्म करने वाले कोड मिलेंगे.