Kurzfassung
CSS verfügt jetzt über eine ordnungsgemäße objektbasierte API für die Arbeit mit Werten in JavaScript.
el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'
Die Zeiten, in denen Strings und kleine Fehler verkettet werden, sind vorbei!
Einführung
Altes CSSOM
Für Preisvergleichsportale gibt es seit vielen Jahren ein Objektmodell (CSSOM). Jedes Mal, wenn Sie .style
in JavaScript lesen/festlegen, verwenden Sie den Code:
// 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;
Neues CSS-Eingabe-OM
Das neue CSS Typed Object Model (Typed OM) ist Teil der Houdini-Initiative und erweitert diese Weltansicht um Typen, Methoden und ein geeignetes Objektmodell zu CSS-Werten. Anstelle von Strings werden Werte als JavaScript-Objekte zur Verfügung gestellt, um eine leistungsstarke (und sinnvolle) Manipulation von CSS zu ermöglichen.
Anstatt element.style
zu verwenden, greifen Sie auf Stile über eine neue .attributeStyleMap
-Eigenschaft für Elemente und über eine .styleMap
-Eigenschaft für Stylesheet-Regeln zu. Beide geben ein StylePropertyMap
-Objekt zurück.
// 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');
Da StylePropertyMap
s kartenähnliche Objekte sind, unterstützen sie alle üblichen Verdächtigen (get/set/keys/values/entries), sodass sie flexibel verwendet werden können:
// 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.
Beachten Sie, dass im zweiten Beispiel opacity
auf den String ('0.3'
) festgelegt ist, aber eine Zahl zurückgegeben wird, wenn das Attribut später gelesen wird.
Vorteile
Welche Probleme versucht das CSS Typed OM zu lösen? Wenn Sie sich die Beispiele oben und die übrigen Bestandteile dieses Artikels ansehen, könnten Sie der Meinung sein, dass das CSS Typed OM viel ausführlicher ist als das alte Objektmodell. Da stimme ich zu.
Bevor Sie Typed OM ausschreiben, sollten Sie einige der wichtigsten Funktionen berücksichtigen:
Weniger Fehler: Numerische Werte werden z. B. immer als Zahlen und nicht als Strings zurückgegeben.
el.style.opacity += 0.1; el.style.opacity === '0.30.1' // dragons!
Arithmetische Operationen und Einheitenumrechnung. Wandeln Sie zwischen absoluten Längeneinheiten (z.B.
px
->cm
) um und führen Sie einfache Berechnungen durch.Bindung und Rundung von Werten: Die eingegebenen OM-Werte runden und/oder klemmen, sodass sie innerhalb der zulässigen Bereiche für eine Eigenschaft liegen.
Bessere Leistung: Der Browser muss weniger Arbeit beim Serialisieren und Deserialisieren von Stringwerten erledigen. Jetzt verwendet die Engine ein ähnliches Verständnis von CSS-Werten in JS und C++. Tab Akins hat einige Early Performance-Benchmarks gezeigt, durch die die typisierte OM im Vergleich zur Verwendung des alten CSSOM und Strings etwa 30% schneller bei Vorgängen pro Sekunde liegt. Dies kann bei schnellen CSS-Animationen mit
requestionAnimationFrame()
von Bedeutung sein. Unter crbug.com/808933 können Sie zusätzliche Leistungsarbeiten in Blink verfolgen.Fehlerbehandlung: Neue Parsing-Methoden bieten eine Fehlerbehandlung in der Welt von CSS.
„Sollte ich CSS-Namen oder -Strings in der Camel-Case-Schreibweise verwenden?“ Sie müssen nicht mehr raten, ob Namen in Camel-Case-Schreibweise vorkommen, oder Strings (z. B.
el.style.backgroundColor
oderel.style['background-color']
). CSS-Eigenschaftsnamen im Typed OM sind immer Strings, die mit dem übereinstimmen, was Sie tatsächlich in CSS schreiben.
Browserunterstützung und Funktionserkennung
Typed OM wurde in Chrome 66 eingeführt und wird jetzt in Firefox implementiert. Edge hat Anzeichen für Unterstützung angezeigt, muss diese jedoch dem Plattform-Dashboard noch hinzufügen.
Für die Featureerkennung können Sie prüfen, ob eine der numerischen CSS.*
-Faktoren definiert ist:
if (window.CSS && CSS.number) {
// Supports CSS Typed OM.
}
API-Grundlagen
Zugreifen auf Stile
Die Werte sind getrennt von den Einheiten in CSS Typed OM. Wenn Sie einen Stil abrufen, wird ein CSSUnitValue
zurückgegeben, das value
und unit
enthält:
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
Berechnete Stile
Berechnete Stile wurden von einer API in window
in eine neue Methode in HTMLElement
, computedStyleMap()
, verschoben:
Altes CSSOM
el.style.opacity = 0.5;
window.getComputedStyle(el).opacity === "0.5" // Ugh, more strings!
Neu eingegebenes OM
el.attributeStyleMap.set('opacity', 0.5);
el.computedStyleMap().get('opacity').value // 0.5
Wertklemmen / Abrunden
Eines der praktischen Merkmale des neuen Objektmodells ist das automatische Aufteilen und/oder Runden von berechneten Stilwerten. Beispiel: Sie versuchen, opacity
auf einen Wert außerhalb des zulässigen Bereichs festzulegen [0, 1]. Das eingegebene OM setzt den Wert bei der Berechnung des Stils auf 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.
Wenn Sie z-index:15.4
festlegen, wird auf 15
gerundet, sodass der Wert eine Ganzzahl bleibt.
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.
Numerische CSS-Werte
Zahlen werden in Typed OM durch zwei Typen von CSSNumericValue
-Objekten dargestellt:
CSSUnitValue
: Werte, die einen einzelnen Einheitentyp enthalten (z.B."42px"
).CSSMathValue
: Werte, die mehr als einen Wert/eine Einheit enthalten, z. B. ein mathematischer Ausdruck (z. B."calc(56em + 10%)"
).
Einheitenwerte
Einfache numerische Werte ("50%"
) werden durch CSSUnitValue
-Objekte dargestellt.
Sie könnten diese Objekte zwar direkt erstellen (new CSSUnitValue(10, 'px')
), verwenden aber in den meisten Fällen die Factory-Methoden für 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'
Die vollständige Liste der CSS.*
-Methoden finden Sie in der Spezifikation.
Mathematische Werte
CSSMathValue
-Objekte stellen mathematische Ausdrücke dar und enthalten in der Regel mehr als einen Wert/eine Einheit. Das übliche Beispiel ist das Erstellen eines CSS-calc()
-Ausdrucks. Es gibt jedoch Methoden für alle CSS-Funktionen: 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)"
Verschachtelte Ausdrücke
Die Verwendung mathematischer Funktionen zum Erstellen komplexerer Werte kann etwas verwirrend sein. Im Folgenden finden Sie einige Beispiele für den Einstieg. habe ich eine zusätzliche Einrückung hinzugefügt, um sie besser lesbar zu machen.
calc(1px - 2 * 3em)
würde so erstellt:
new CSSMathSum(
CSS.px(1),
new CSSMathNegate(
new CSSMathProduct(2, CSS.em(3))
)
);
calc(1px + 2px + 3px)
würde so erstellt:
new CSSMathSum(CSS.px(1), CSS.px(2), CSS.px(3));
calc(calc(1px + 2px) + 3px)
würde so erstellt:
new CSSMathSum(
new CSSMathSum(CSS.px(1), CSS.px(2)),
CSS.px(3)
);
Arithmetische Operationen
Eines der nützlichsten Funktionen des CSS Typed OM ist, dass Sie mathematische Operationen an CSSUnitValue
-Objekten ausführen können.
Grundlegende Vorgänge
Grundlegende Vorgänge (add
/sub
/mul
/div
/min
/max
) werden unterstützt:
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))"
Conversion
Absolute Längeneinheiten können in andere Längeneinheiten umgewandelt werden:
// 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
Entsprechung
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-Transformationswerte
CSS-Transformationen werden mit einer CSSTransformValue
erstellt und übergeben ein Array von Transformationswerten (z.B. CSSRotate
, CSScale
, CSSSkew
, CSSSkewX
, CSSSkewY
). Angenommen, Sie möchten diesen CSS-Code neu erstellen:
transform: rotateZ(45deg) scale(0.5) translate3d(10px,10px,10px);
Übersetzt in folgendes geschriebenes 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))
]);
Abgesehen von der Ausführlichkeit (lolz!) CSSTransformValue
hat tolle Funktionen. Sie hat eine boolesche Eigenschaft, um 2D- und 3D-Transformationen zu unterscheiden, und eine .toMatrix()
-Methode, um die DOMMatrix
-Darstellung einer Transformation zurückzugeben:
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
Beispiel: Würfel animieren
Sehen wir uns ein praktisches Beispiel für die Verwendung von Transformationen an. Wir verwenden JavaScript- und CSS-Transformationen, um einen Würfel zu animieren.
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.
})();
Beachten Sie:
- Numerische Werte bedeuten, dass wir den Winkel direkt mit Mathematik erhöhen können.
- Anstatt das DOM zu berühren oder einen Wert für jeden Frame zurückzulesen (z.B. ohne
box.style.transform=`rotate(0,0,1,${newAngle}deg)`
), wird die Animation durch das Aktualisieren des zugrunde liegendenCSSTransformValue
-Datenobjekts gesteuert, um die Leistung zu verbessern.
Demo
Wenn Ihr Browser Typed OM unterstützt, sehen Sie unten einen roten Würfel. Der Kubus beginnt sich zu drehen, wenn Sie die Maus darüber bewegen. Die Animation wird von CSS Typed OM! 🤘
Werte benutzerdefinierter CSS-Eigenschaften
CSS-var()
wird zu einem CSSVariableReferenceValue
-Objekt im typisierten OM.
Ihre Werte werden in CSSUnparsedValue
geparst, da sie jeden Typ annehmen können (px, %, em, rgba() usw.).
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'
Wenn Sie den Wert einer benutzerdefinierten Eigenschaft abrufen möchten, sind einige Schritte erforderlich:
<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>
Positionswerte
CSS-Attribute, die eine durch Leerzeichen getrennte x/y-Position wie object-position
einnehmen, werden durch CSSPositionValue
-Objekte dargestellt.
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
Werte parsen
Das Typed OM führt Parsing-Methoden auf der Webplattform ein. Das bedeutet, dass Sie endlich CSS-Werte programmatisch parsen können, bevor Sie versuchen, sie zu verwenden. Mit dieser neuen Funktion lassen sich frühe Programmfehler und fehlerhaftes CSS leichter erkennen.
Vollständigen Stil parsen:
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)'
Werte in CSSUnitValue
parsen:
CSSNumericValue.parse('42.0px') // {value: 42, unit: 'px'}
// But it's easier to use the factory functions:
CSS.px(42.0) // '42px'
Fehlerbehandlung
Beispiel: Prüfen Sie, ob der CSS-Parser mit diesem transform
-Wert zufrieden ist:
try {
const css = CSSStyleValue.parse('transform', 'translate4d(bogus value)');
// use css
} catch (err) {
console.err(err);
}
Fazit
Wir freuen uns, dass wir nun ein aktualisiertes Objektmodell für CSS haben. Ich fand die Arbeit mit Saiten nie gut. Die CSS Typed OM API ist etwas ausführlicher, führt aber hoffentlich zu weniger Programmfehlern und einem leistungsfähigeren Code.