Özet
CSS artık JavaScript'teki değerlerle çalışmaya uygun nesne tabanlı bir 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 bir araya getirme ve incelikli hata ayıklama dönemi sona erdi.
Giriş
Eski CSSOM
CSS'nin uzun yıllardır bir nesne modeli (CSSOM) vardır. Aslında JavaScript'te .style
kodunu okuduğunuzda/ayarladığınızda:
// 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 Yazılan OM
Houdini çalışmasının bir parçası olan yeni CSS Yazılı Nesne Modeli (OM Türü), CSS değerlerine türler, yöntemler ve uygun nesne modeli ekleyerek bu dünya görünümünü genişletir. Dizeler yerine değerler, CSS'nin performanslı (ve mantıklı) yönetimini kolaylaştırmak için JavaScript nesneleri olarak gösterilir.
element.style
kullanmak yerine, stillere öğeler için yeni bir .attributeStyleMap
özelliği ve stil sayfası kuralları için .styleMap
özelliği aracılığıyla erişirsiniz. Her ikisi de bir 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
öğeleri Harita benzeri nesneler olduğundan, bilindik tüm şüpheli öğeleri (get/set/anahtarlar/değerler/girişler) destekler. Bu nedenle aşağıdakilerle çalışmak için esnektirler:
// 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
dizesinin ('0.3'
) olarak ayarlandığını ancak özellik daha sonra tekrar okunduğunda bir sayı ortaya çıktığını unutmayın.
Avantajları
Peki, CSS ile yazılan OM hangi sorunları çözmeye çalışıyor? Yukarıdaki örneklere (ve bu makalenin geri kalanında) bakarak, CSS Türü OM'nin eski nesne modelinden çok daha ayrıntılı olduğunu iddia edebilirsiniz. Katılıyorum.
Yazılı OM'yi yazmadan önce, tabloya getirdiği temel özelliklerden bazılarını göz önünde bulundurun:
Daha az hata: Örneğin, sayısal değerler her zaman dize 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
) ve temel matematik arasında dönüştürme yapın.Değer sınırlama ve yuvarlama. Yazılan OM yuvarları ve/veya sabitler değerleri, bir mülkün kabul edilebilir aralıkları içinde olur.
Daha iyi performans. Tarayıcının dize değerlerini serileştirme ve serileştirmeden daha az işi gerekir. Artık motor, JS ve C++ genelinde CSS değerlerini benzer bir anlayışla kullanmaktadır. Tab Akins, eski CSSOM ve dizelere kıyasla, yazılan OM'nin işlem/saniyede yaklaşık% 30 daha hızlı olmasını sağlayan bazı erken performans karşılaştırmaları göstermektedir. Bu,
requestionAnimationFrame()
kullanan 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şleme olanağını sunuyor.
"Büyük-küçük harf içeren CSS adları veya dizeleri kullanmalı mıyım?" Adların büyük/küçük harf mi yoksa dize mi (ör.
el.style.backgroundColor
veyael.style['background-color']
) olduğunu tahmin etmenize gerek yok. Yazılan OM'deki CSS özellik adları her zaman, CSS'de yazdıklarınızla eşleşen dizelerdir :)
Tarayıcı desteği ve özellik algılama
Yazılan OM, Chrome 66'da açılır ve Firefox'ta uygulanmaktadır. Edge, destek işaretleri göstermiş olsa da bunu platform kontrol paneline henüz eklememiştir.
Ö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
Değerler, CSS Yazılı OM'deki birimlerden ayrıdır. Bir stil alınması, 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
Hesaplanan stiller
Hesaplanan stiller window
tarihindeki bir API'den HTMLElement
itibarıyla 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ılan OM
el.attributeStyleMap.set('opacity', 0.5);
el.computedStyleMap().get('opacity').value // 0.5
Değer sınırlama / yuvarlama
Yeni nesne modelinin güzel özelliklerinden biri, hesaplanan stil değerlerinin otomatik olarak sınırlanması 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
değerine 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
değerinin ayarlanması, değerin tam sayı olarak kalması için 15
değerine yuvarlanı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, Yazılı OM'de iki tür CSSNumericValue
nesnesiyle temsil edilir:
CSSUnitValue
- tek bir birim türü içeren değerler (ör."42px"
).CSSMathValue
- matematiksel ifade gibi birden fazla değer/birim içeren değerler (ör."calc(56em + 10%)"
).
Birim değerleri
Basit sayısal değerler ("50%"
) CSSUnitValue
nesneleriyle temsil edilir.
Bu nesneleri doğrudan oluşturabilirsiniz ancak (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 spesifikasyona bakın.
Matematik değerleri
CSSMathValue
nesneleri matematiksel ifadeleri temsil eder ve genellikle birden fazla değer/birim içerir. Yaygın bir örnek, CSS calc()
ifadesi oluşturmaktır, ancak tüm CSS işlevleri için kullanılabilen yöntemler vardır:
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)"
İç içe yerleştirilmiş ifadeler
Daha karmaşık değerler oluşturmak için matematik fonksiyonlarını kullanmak biraz kafa karıştırıcıdır. Aşağıda, başlamanıza yardımcı olacak birkaç örnek verilmiştir. Daha kolay okunması için fazladan 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 Türü OM'nin en kullanışlı özelliklerinden biri, CSSUnitValue
nesneleri üzerinde matematik işlemleri gerçekleştirebilmenizdir.
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 birim uzunluklarına 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üşüm değerleri
CSS dönüşümleri CSSTransformValue
ile oluşturulur ve bir dönüşüm değerleri dizisi iletir (ör. CSSRotate
, CSScale
, CSSSkew
, CSSSkewX
,
CSSSkewY
). Örnek olarak, şu CSS'yi yeniden oluşturmak istediğinizi varsayalım:
transform: rotateZ(45deg) scale(0.5) translate3d(10px,10px,10px);
Yazılan OM'ye çevrildi:
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))
]);
Ayrıntı düzeyinin yanı sıra (hahaha!), CSSTransformValue
birtakım etkileyici özelliklere sahip. 2D ve 3D dönüşümleri ayırt etmek için bir boole özelliği ve bir dönüşümün DOMMatrix
gösterimini döndürmek için .toMatrix()
yöntemi bulunur:
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üp animasyon
Dönüşümleri kullanmaya ilişkin pratik bir örneği inceleyelim. Bir küpü canlandırmak için JavaScript ve CSS dönüşümlerini 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.
})();
Şunlara dikkat edin:
- Sayısal değerler, açıyı doğrudan matematik kullanarak artırabileceğimiz anlamına gelir.
- Animasyon, DOM'a dokunmak veya her karede bir değeri (ör.
box.style.transform=`rotate(0,0,1,${newAngle}deg)`
yok) okumak yerine temelCSSTransformValue
veri nesnesinin güncellenip performansın artırılmasıyla yönetilir.
Demo
Tarayıcınız Yazılı OM'yi destekliyorsa aşağıda kırmızı bir küp görürsünüz. Fareyi üzerine getirdiğinizde küp dönmeye başlar. Animasyon, CSS Yazarak OM! 🤘
CSS özel özellik değerleri
CSS var()
, Yazılan OM'de CSSVariableReferenceValue
nesnesi haline geldi.
Herhangi bir türü (px, %, em, rgba() vb.) alabildiği için değerleri 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'
Bir özel özelliğin değerini almak istiyorsanız yapmanız gereken biraz iş 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ış x/y konumu alan CSS özellikleri CSSPositionValue
nesneleriyle temsil edilir.
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ğerler ayrıştırılıyor
Typed OM, web platformuna ayrıştırma yöntemleri sunuyor. Yani son olarak CSS değerlerini, kullanmayı denemeden önce programatik olarak ayrıştırabilirsiniz. Bu yeni özellik, erken hataları ve bozuk CSS'yi yakalamak için potansiyel bir hayat kurtarır.
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
olarak 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ç
Nihayet CSS için güncellenmiş bir nesne modeli oluşturmak güzel. Tellerle çalışmak bana hiç doğru gelmedi. CSS Typed OM API'si biraz ayrıntılı olsa da daha az hata ve daha yüksek performanslı kod sağlar.