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

कम शब्दों में कहा जाए तो

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

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

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

परिचय

Old 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;

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

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++ में सीएसएस वैल्यू को एक जैसा समझता है. टैब अकिंस ने परफ़ॉर्मेंस के कुछ शुरुआती बेंचमार्क दिखाए हैं. इनके मुताबिक, सीएसएसओएम और स्ट्रिंग का इस्तेमाल करने की तुलना में, टाइप किए गए ओएम की मदद से एक सेकंड में ~30% ज़्यादा ऑपरेशन किए जा सकते हैं. requestionAnimationFrame() का इस्तेमाल करके, तेज़ी से सीएसएस ऐनिमेशन बनाने के लिए यह ज़रूरी हो सकता है. crbug.com/808933 पर, Blink की परफ़ॉर्मेंस को बेहतर बनाने से जुड़ी अन्य जानकारी ट्रैक की जाती है.

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

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

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

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

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

if (window.CSS && CSS.number) {
  // Supports CSS Typed 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!

New Typed OM

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

वैल्यू को क्लैंप करना / राउंड करना

नए ऑब्जेक्ट मॉडल की एक बेहतरीन सुविधा यह है कि स्टाइल की कैलकुलेट की गई वैल्यू अपने-आप सीमित हो जाती हैं और/या राउंड हो जाती हैं. उदाहरण के लिए, मान लें कि आपने opacity को स्वीकार्य सीमा [0, 1] से बाहर की वैल्यू पर सेट करने की कोशिश की है. टाइप किए गए ओएम क्लैंप, स्टाइल की गिनती करते समय वैल्यू को 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.

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

टाइप्ड ओएम में, संख्याओं को दो तरह के 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);

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

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))
]);

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

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 डेटा को अपडेट करके चलाया जाता है. इससे परफ़ॉर्मेंस बेहतर होती है.

डेमो

अगर आपका ब्राउज़र Typed 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

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

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

पूरी स्टाइल पार्स करें:

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);
}

नतीजा

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