الملخّص
أصبحت لغة CSS الآن واجهة برمجة تطبيقات مناسبة قائمة على الكائنات للعمل مع القيم في JavaScript.
el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'
لقد انتهت أيام سلاسل السلاسل والأخطاء الطفيفة!
مقدمة
CSSOM القديم
احتفظت CSS بنموذج عنصر (CSSOM) لسنوات عديدة. في الواقع، يمكنك استخدام هذا الرمز في أي وقت
تقرأ فيه/تضبط فيه .style
في JavaScript:
// 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;
تقرير تشغيلي جديد لصفحات الأنماط المتتالية (CSS)
إنّ نموذج كائن CSS Typed الجديد (Typed OM)، الذي يشكّل جزءًا من جهود Houdini، يوسِّع هذه النظرة العامة من خلال إضافة أنواع وطرق ونموذج مناسب للكائنات إلى قيم CSS. وبدلاً من السلاسل، يتم عرض القيم ككائنات JavaScript لتسهيل المعالجة الفعّالة (والمعقولة) لـ CSS.
بدلاً من استخدام 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/الإدخالات)، ما يجعلها مرنة عند استخدامها:
// 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'
)، ولكن يظهر رقم عند إعادة قراءة الخاصية لاحقًا.
المزايا
إذًا، ما هي المشاكل التي يحاول نظام إدارة المحتوى (OM) في كتابة CSS حلّها؟ وبالنظر إلى الأمثلة أعلاه (وباقي هذه المقالة)، يمكنك القول إن CSS Typed OM أكثر مدّة بكثير من نموذج الكائن القديم. أوافقك!
قبل شطب Typed OM، ضع في اعتبارك بعض الميزات الرئيسية التي يضيفها إلى الجدول:
أخطاء أقل: على سبيل المثال، يتم عرض قيم رقمية دائمًا كأرقام، وليس كسلاسل.
el.style.opacity += 0.1; el.style.opacity === '0.30.1' // dragons!
العمليات الحسابية وتحويل الوحدات. يتم التحويل بين وحدات الطول المطلق (مثل
px
->cm
) وإجراء العمليات الحسابية الأساسية.تقريب القيمة وتقريبها: قيم OM مكتوبة على شكل دائري و/أو مثبّت بحيث تكون ضمن النطاقات المقبولة لأحد المواقع.
تحسين الأداء: يتعين على المتصفح القيام بأعمال أقل في التسلسل وإلغاء تسلسل قيم السلسلة. أما الآن، فيستخدم المحرّك طريقة فهم مشابهة لقيم CSS على مستوى JavaScript وC++. وقد أظهرت علامة التبويب Akins بعض مقاييس الأداء الأولية التي جعلت إدارة العمليات المكتوبة بـ أسرع بحوالي% 30 في العمليات/العمليات عند مقارنتها باستخدام سلسلة CSSOM القديمة والسلاسل القديمة. ويمكن أن يكون ذلك مهمًا للصور المتحركة السريعة في CSS التي تستخدم
requestionAnimationFrame()
. يتتبّع crbug.com/808933 أعمال الأداء الإضافية في Blink.حدث الخطأ أثناء المعالجة. تضيف أساليب التحليل الجديدة ميزة التعامل مع الأخطاء إلى لغة CSS.
"هل يجب استخدام أسماء أو سلاسل CSS لحالة الجمل؟" ما من داعٍ للتخمين إذا كانت الأسماء مكتوبة على شكل جمل أو سلاسل (مثل
el.style.backgroundColor
مقابلel.style['background-color']
). تكون أسماء سمات CSS في Typed OM دائمًا سلاسل تطابق ما تكتبه فعليًا في CSS :)
دعم المتصفّح ورصد الميزات
وصلت عبارة OM المكتوبة إلى متصفّح Chrome 66 ويجري تنفيذها في Firefox. أظهر متصفّح Edge علامات تدلّ على توفّر ميزات متوافقة، ولكن لم تتم إضافته بعد إلى لوحة البيانات الخاصة به.
لاكتشاف الميزات، يمكنك التحقق من تحديد أحد المصانع الرقمية لـ CSS.*
:
if (window.CSS && CSS.number) {
// Supports CSS Typed OM.
}
أساسيات واجهة برمجة التطبيقات
الوصول إلى الأنماط
تكون القيم منفصلة عن الوحدات في 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()
:
أداة CSSOM القديمة
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.
القيم الرقمية لخدمة مقارنة الأسعار (CSS)
يتم تمثيل الأرقام من خلال نوعَين من كائنات CSSNumericValue
في Typed OM:
CSSUnitValue
- القيم التي تحتوي على نوع وحدة واحد (مثل"42px"
).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()
في CSS،
ولكن تتوفر طرق لكل دوال CSS:
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)
);
العمليات الحسابية
من أكثر الميزات المفيدة في CSS Typed OM أنّه يمكنك إجراء
عمليات حسابية على كائنات 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
قيم تحويل CSS
يتم إنشاء عمليات تحويل CSS باستخدام CSSTransformValue
وتمرير مصفوفة من قيم التحويل (مثل CSSRotate
وCSScale
وCSSSkew
وCSSSkewX
وCSSSkewY
). على سبيل المثال، لنفترض أنك تريد إعادة إنشاء CSS هذه:
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
على بعض
الميزات الرائعة. وتحتوي على خاصية منطقية للتفريق بين التحويلات الثنائية الأبعاد والثلاثية الأبعاد وطريقة .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 وتحولات CSS لتحريك مكعب.
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.
})();
لاحظ ما يلي:
- القيم العددية تعني أنه يمكننا زيادة الزاوية مباشرة باستخدام الرياضيات!
- بدلاً من لمس عنصر DOM أو قراءة قيمة في كل إطار (على سبيل المثال، بدون
box.style.transform=`rotate(0,0,1,${newAngle}deg)`
)، يتم تشغيل الصورة المتحركة من خلال تحديث كائن بياناتCSSTransformValue
الأساسي، ما يؤدي إلى تحسين الأداء.
تجريبي
سيظهر لك أدناه مكعّب أحمر إذا كان متصفّحك يتوافق مع Typed OM. يبدأ المكعب في التدوير عند تحريك الماوس فوقه. يتم تشغيل الصورة المتحركة بواسطة CSS Typed OM! 🤘
قيم خصائص CSS المخصّصة
تصبح CSS var()
كائن CSSVariableReferenceValue
في Typed OM.
يتم تحليل قيمها إلى 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>
قيم الموضع
يتم تمثيل خصائص CSS التي تأخذ موضع 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 Typed طرق التحليل إلى النظام الأساسي للويب! وهذا يعني أنّه يمكنك أخيرًا تحليل قيم CSS آليًا، قبل محاولة استخدامها. يمكن لهذه الإمكانية الجديدة إنقاذ حياة مُحتمَلة لاكتشاف الأخطاء المبكرة وإنشاء CSS بشكل غير صحيح.
تحليل نمط كامل:
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'
معالجة الأخطاء
مثال - تحقَّق مما إذا كان محلّل CSS متوافقًا مع قيمة transform
التالية:
try {
const css = CSSStyleValue.parse('transform', 'translate4d(bogus value)');
// use css
} catch (err) {
console.err(err);
}
الخلاصة
من الجيد أن يكون لديك أخيرًا نموذج كائن محدّث لـ CSS. لم يكن العمل على السلاسل مناسبًا لي. تتميز واجهة برمجة التطبيقات CSS Typed OM API بأنّها مطوَّلة بعض الشيء، ولكن نأمل أن ينتج عنها أخطاء أقل ورموز برمجية أفضل.