Özet
CSS artık JavaScript'teki değerlerle çalışmak için uygun bir nesne tabanlı API'ye sahip.
el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'
Dizeleri birleştirme ve küçük hatalarla uğraşma dönemi sona erdi.
Giriş
Eski CSSOM
CSS, uzun yıllardır bir nesne modeline (CSSOM) sahiptir. Hatta JavaScript'te .style okuduğunuz veya ayarladığınız her seferde bu özelliği kullanırsınız:
// 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;
Yeni CSS Typed OM
Houdini projesinin bir parçası olan yeni CSS Typed Object Model (Typed OM), CSS değerlerine türler, yöntemler ve uygun bir nesne modeli ekleyerek bu dünya görüşünü genişletir. Değerler, dizeler yerine CSS'nin performanslı (ve mantıklı) şekilde işlenmesini kolaylaştırmak için JavaScript nesneleri olarak sunulur.
element.style yerine, öğeler için yeni bir .attributeStyleMap özelliği ve stil sayfası kuralları için bir .styleMap özelliği aracılığıyla stillere erişeceksiniz. Her ikisi de StylePropertyMap nesnesi döndürür.
// 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, Map benzeri nesneler olduğundan, tüm olağan şüphelileri (get/set/keys/values/entries) destekler ve bu da onları esnek bir şekilde kullanmanızı sağlar:
// 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.
İkinci örnekte opacity değerinin dize ('0.3') olarak ayarlandığını ancak özellik daha sonra tekrar okunduğunda sayının geri döndüğünü unutmayın.
Avantajları
Peki CSS Typed OM hangi sorunları çözmeye çalışıyor? Yukarıdaki örnekleri (ve bu makalenin geri kalanını) incelediğinizde, CSS Typed OM'nin eski nesne modelinden çok daha ayrıntılı olduğunu düşünebilirsiniz. Katılıyorum.
Typed OM'yi kullanmayı bırakmadan önce sunduğu temel özelliklerden bazılarını göz önünde bulundurun:
- Daha az hata: Örneğin, sayısal değerler her zaman dize olarak değil, sayı olarak döndürülür. - el.style.opacity += 0.1; el.style.opacity === '0.30.1' // dragons!
- Aritmetik işlemler ve birim dönüştürme: Mutlak uzunluk birimleri (ör. - px->- cm) arasında dönüştürme yapın ve temel matematik işlemlerini gerçekleştirin.
- Değer sınırlama ve yuvarlama. Yazılan OM, değerleri bir özellik için kabul edilebilir aralıklar içinde olacak şekilde yuvarlar ve/veya sınırlar. 
- Daha iyi performans. Tarayıcının, dize değerlerini serileştirme ve seri durumdan çıkarma konusunda daha az iş yapması gerekir. Artık motor, JS ve C++'da CSS değerlerini benzer şekilde anlıyor. Tab Akins, erken performans karşılaştırmaları gösterdi. Bu karşılaştırmalara göre, eski CSSOM ve dizeleri kullanmaya kıyasla Typed OM, işlemler/sn cinsinden % 30 daha hızlı. Bu, - requestionAnimationFrame()kullanılarak yapılan hızlı CSS animasyonları için önemli olabilir. crbug.com/808933, Blink'teki ek performans çalışmalarını izler.
- Hata işleme. Yeni ayrıştırma yöntemleri, CSS dünyasında hata işlemeyi mümkün kılıyor. 
- "Deve harfiyle yazılmış CSS adları mı yoksa dizeler mi kullanmalıyım?" Adların camel case mi yoksa dize mi olduğu (ör. - el.style.backgroundColorve- el.style['background-color']) konusunda artık tahminde bulunmanız gerekmiyor. Typed OM'deki CSS özelliği adları her zaman dizelerdir ve CSS'de yazdıklarınızla eşleşir. :)
Tarayıcı desteği ve özellik algılama
Yazılan OM, Chrome 66'da kullanıma sunuldu ve Firefox'ta uygulanıyor. Edge, destek sinyalleri gösterse de henüz platform kontrol paneline eklemedi.
Özellik algılama için CSS.* sayısal fabrikalardan birinin tanımlanıp tanımlanmadığını kontrol edebilirsiniz:
if (window.CSS && CSS.number) {
  // Supports CSS Typed OM.
}
API Temel Bilgileri
Stillere erişme
CSS Typed OM'de değerler birimlerden ayrıdır. Stil alma işlemi, value ve unit içeren bir CSSUnitValue döndürür:
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
Hesaplanmış stiller
Hesaplanmış stiller
window üzerindeki bir API'den HTMLElement üzerindeki yeni bir yönteme taşındı.
computedStyleMap():
Eski CSSOM
el.style.opacity = 0.5;
window.getComputedStyle(el).opacity === "0.5" // Ugh, more strings!
Yeni Yazılı OM
el.attributeStyleMap.set('opacity', 0.5);
el.computedStyleMap().get('opacity').value // 0.5
Değer kısıtlama / yuvarlama
Yeni nesne modelinin güzel özelliklerinden biri de hesaplanmış stil değerlerinin otomatik olarak sınırlandırılması ve/veya yuvarlanmasıdır. Örneğin, opacity değerini kabul edilebilir aralığın [0, 1] dışında bir değere ayarlamaya çalıştığınızı varsayalım. Yazılan OM, stili hesaplarken değeri 1 olarak sabitler:
el.attributeStyleMap.set('opacity', 3);
el.attributeStyleMap.get('opacity').value === 3  // val not clamped.
el.computedStyleMap().get('opacity').value === 1 // computed style clamps value.
Benzer şekilde, z-index:15.4 ayarı 15 olarak yuvarlanır ve değer tam sayı olarak kalır.
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.
CSS sayısal değerleri
Sayılar, Typed OM'de iki tür CSSNumericValue nesneyle temsil edilir:
- CSSUnitValue- Tek bir birim türü içeren değerler (ör.- "42px").
- CSSMathValue- Matematiksel ifade (ör.- "calc(56em + 10%)") gibi birden fazla değer/birim içeren değerler.
Birim değerleri
Basit sayısal değerler ("50%"), CSSUnitValue nesneleriyle temsil edilir.
Bu nesneleri doğrudan oluşturmanız mümkün olsa da (new CSSUnitValue(10, 'px'))
, çoğu zaman CSS.* fabrika yöntemlerini kullanırsınız:
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.* yöntemlerinin tam listesi için teknik özelliklere bakın.
Matematik değerleri
CSSMathValue nesneleri matematiksel ifadeleri temsil eder ve genellikle birden fazla değer/birim içerir. Yaygın örnek, bir CSS calc() ifadesi oluşturmaktır ancak tüm CSS işlevleri (calc(), min(), max()) için yöntemler vardır.
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)"
İç içe yerleştirilmiş ifadeler
Daha karmaşık değerler oluşturmak için matematik işlevlerini kullanmak biraz kafa karıştırıcı olabilir. Başlamanıza yardımcı olacak birkaç örneği aşağıda bulabilirsiniz. Daha kolay okunabilmeleri için bu paragraflara ek girinti ekledim.
calc(1px - 2 * 3em) şu şekilde oluşturulur:
new CSSMathSum(
  CSS.px(1),
  new CSSMathNegate(
    new CSSMathProduct(2, CSS.em(3))
  )
);
calc(1px + 2px + 3px) şu şekilde oluşturulur:
new CSSMathSum(CSS.px(1), CSS.px(2), CSS.px(3));
calc(calc(1px + 2px) + 3px) şu şekilde oluşturulur:
new CSSMathSum(
  new CSSMathSum(CSS.px(1), CSS.px(2)),
  CSS.px(3)
);
Aritmetik işlemler
CSS Typed OM'nin en yararlı özelliklerinden biri, CSSUnitValue nesneleri üzerinde matematiksel işlemler yapabilmenizdir.
Temel işlemler
Temel işlemler (add/sub/mul/div/min/max) desteklenir:
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))"
Dönüşüm
Mutlak uzunluk birimleri diğer uzunluk birimlerine dönüştürülebilir:
// 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
Eşitlik
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
CSS dönüştürme değerleri
CSS dönüştürmeleri CSSTransformValue ile oluşturulur ve dönüştürme değerleri dizisi (ör. CSSRotate, CSScale, CSSSkew, CSSSkewX, CSSSkewY) iletilir. Örneğin, şu CSS'yi yeniden oluşturmak istediğinizi varsayalım:
transform: rotateZ(45deg) scale(0.5) translate3d(10px,10px,10px);
Yazılı OM'ye çevrilenler:
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))
]);
Çok uzun olmasının (lolz!) yanı sıra, CSSTransformValue'da bazı harika özellikler var. 2D ve 3D dönüşümleri ayırt etmek için bir Boole özelliği ve dönüşümün DOMMatrix gösterimini döndürmek için bir .toMatrix() yöntemi vardır:
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
Örnek: küpe animasyon ekleme
Dönüşümleri kullanmayla ilgili pratik bir örneğe göz atalım. Bir küpe animasyon eklemek için JavaScript ve CSS dönüştürmelerini kullanacağız.
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.
})();
Unutmayın:
- Sayısal değerler, açıyı doğrudan matematik kullanarak artırabileceğimiz anlamına gelir.
- Her karede DOM'a dokunmak veya bir değeri geri okumak yerine (ör. box.style.transform=`rotate(0,0,1,${newAngle}deg)`yok), animasyon temelCSSTransformValueveri nesnesi güncellenerek yönlendirilir ve performans iyileştirilir.
Demo
Tarayıcınız Typed OM'yi destekliyorsa aşağıda kırmızı bir küp görürsünüz. Fare imlecini küpün üzerine getirdiğinizde küp dönmeye başlar. Animasyon, CSS Typed OM tarafından desteklenir. 🤘
CSS özel özellik değerleri
CSS var(), Typed OM'de CSSVariableReferenceValue nesnesi haline gelir.
Değerleri herhangi bir türü (px, %, em, rgba() vb.) alabileceğinden CSSUnparsedValue olarak ayrıştırılır.
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'
Özel bir özelliğin değerini almak istiyorsanız yapmanız gereken birkaç işlem vardır:
<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>
Konum değerleri
object-position gibi boşlukla ayrılmış bir x/y konumu alan CSS özellikleri CSSPositionValue nesneleriyle gösterilir.
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
Değerleri ayrıştırma
Typed OM, web platformuna ayrıştırma yöntemleri sunuyor. Bu sayede, CSS değerlerini programatik olarak ayrıştırabilirve kullanmaya çalışmadanönce doğrulayabilirsiniz. Bu yeni özellik, hataları ve hatalı CSS'leri erken aşamada yakalamak için hayat kurtarıcı olabilir.
Tam stili ayrıştırma:
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)'
Değerleri CSSUnitValue içine ayrıştırın:
CSSNumericValue.parse('42.0px') // {value: 42, unit: 'px'}
// But it's easier to use the factory functions:
CSS.px(42.0) // '42px'
Hata işleme
Örnek: CSS ayrıştırıcının bu transform değerinden memnun olup olmayacağını kontrol edin:
try {
  const css = CSSStyleValue.parse('transform', 'translate4d(bogus value)');
  // use css
} catch (err) {
  console.err(err);
}
Sonuç
CSS için nihayet güncellenmiş bir nesne modeline sahip olmak güzel. Dizelerle çalışmak hiç bana göre olmadı. CSS Typed OM API biraz ayrıntılı olsa da ileride daha az hata ve daha yüksek performanslı kodla sonuçlanacağını umuyoruz.