تاريخ النشر: 8 أكتوبر 2024
لحلّ بعض المشاكل الغريبة في تداخل CSS، قرّرت مجموعة عمل CSS إضافة واجهة CSSNestedDeclarations
إلى مواصفات تداخل CSS. ومن خلال هذه الإضافة، لم تعُد التعريفات التي تأتي بعد قواعد الأنماط تنتقل للأعلى، بالإضافة إلى بعض التحسينات الأخرى.
تتوفّر هذه التغييرات في Chrome اعتبارًا من الإصدار 130 وهي جاهزة للاختبار في الإصدار 132 من Firefox Nightly والإصدار 204 من Safari Technology Preview.
توافق المتصفّح
مشكلة تداخل CSS بدون CSSNestedDeclarations
من المشاكل المتعلقة بدمج CSS أنّ المقتطف التالي لا يعمل على النحو المتوقّع في البداية:
.foo {
width: fit-content;
@media screen {
background-color: red;
}
background-color: green;
}
بالنظر إلى الرمز، تفترض أن العنصر <div class=foo>
يحتوي على background-color
green
لأن تعريف background-color: green;
يأتي في النهاية. لكن هذا لا يحدث في Chrome الذي يسبق الإصدار 130. في هذه الإصدارات التي لا تتيح استخدام CSSNestedDeclarations
، يكون background-color
للعنصر هو red
.
بعد تحليل القاعدة الفعلية، فإنّ Chrome قبل الإصدار 130 يستخدم القاعدة التالية:
.foo {
width: fit-content;
background-color: green;
@media screen {
& {
background-color: red;
}
}
}
بعد التحليل، تم إجراء تغييرَين على ملف CSS:
- تم نقل
background-color: green;
للأعلى للانضمام إلى البيانَين الآخرين. - تمت إعادة كتابة
CSSMediaRule
المُدمَجة لتغليف بياناتها فيCSSStyleRule
إضافي باستخدام أداة الاختيار&
.
ومن التغييرات الشائعة الأخرى التي ستلاحظها هنا أنّه يتم تجاهل السمات التي لا يتوافق معها المُحلِّل.
يمكنك فحص "CSS بعد التحليل" بنفسك من خلال قراءة cssText
من CSSStyleRule
.
يمكنك تجربة ذلك بنفسك في هذه المساحة التفاعلية:
لماذا تمت إعادة كتابة ملف CSS هذا؟
لفهم سبب إجراء هذه إعادة الكتابة الداخلية، عليك فهم كيفية تمثيل هذا العنصر CSSStyleRule
في نموذج محتوى CSS (CSSOM).
في الإصدارات الأقدم من Chrome 130، يتم تسلسل مقتطف CSS الذي تمت مشاركته سابقًا على النحو التالي:
↳ CSSStyleRule
.type = STYLE_RULE
.selectorText = ".foo"
.resolvedSelectorText = ".foo"
.specificity = "(0,1,0)"
.style (CSSStyleDeclaration, 2) =
- width: fit-content
- background-color: green
.cssRules (CSSRuleList, 1) =
↳ CSSMediaRule
.type = MEDIA_RULE
.cssRules (CSSRuleList, 1) =
↳ CSSStyleRule
.type = STYLE_RULE
.selectorText = "&"
.resolvedSelectorText = ":is(.foo)"
.specificity = "(0,1,0)"
.style (CSSStyleDeclaration, 1) =
- background-color: red
من بين جميع السمات التي يتضمّنها CSSStyleRule
، تكون السمتان التاليتان ذا صلة في هذه الحالة:
- سمة
style
التي هي مثيلCSSStyleDeclaration
يمثّل البيانات - سمة
cssRules
التي هيCSSRuleList
تتضمّن جميع عناصرCSSRule
المدمجة.
يتم فقدان بعض المعلومات لأنّ جميع التعريفات من مقتطف CSS تنتهي في السمة style
في CSStyleRule
. عند الاطّلاع على السمة style
، لا يتضح أنّه تمّ الإعلان عن background-color: green
بعد CSSMediaRule
المُدمَجة.
↳ CSSStyleRule
.type = STYLE_RULE
.selectorText = ".foo"
.style (CSSStyleDeclaration, 2) =
- width: fit-content
- background-color: green
.cssRules (CSSRuleList, 1) =
↳ …
ويشكّل ذلك مشكلة، إذ يجب أن يتمكّن محرك CSS من التمييز بين الخصائص التي تظهر في بداية محتوى قاعدة النمط وتلك التي تظهر مختلطة مع قواعد أخرى، وذلك لكي يعمل محرك CSS بشكل سليم.
بالنسبة إلى التعريفات داخل CSSMediaRule
، يتم تضمينها فجأة في CSSStyleRule
، لأنّ CSSMediaRule
لم يتم تصميمه ليتضمن تعريفات.
بما أنّ CSSMediaRule
يمكن أن يحتوي على قواعد مُدمجة يمكن الوصول إليها من خلال سمة cssRules
، يتم تلقائيًا تضمين التعريفات في CSSStyleRule
.
↳ CSSMediaRule
.type = MEDIA_RULE
.cssRules (CSSRuleList, 1) =
↳ CSSStyleRule
.type = STYLE_RULE
.selectorText = "&"
.resolvedSelectorText = ":is(.foo)"
.specificity = "(0,1,0)"
.style (CSSStyleDeclaration, 1) =
- background-color: red
كيف يمكن حلّ هذه المشكلة؟
لقد راجعت مجموعة عمل CSS عدة خيارات لحلّ هذه المشكلة.
كان أحد الحلول المقترَحة هو إحاطة جميع التعريفات المجرّدة في CSSStyleRule
مدمَج باستخدام أداة الاختيار المتداخلة (&
). تم تجاهل هذه الفكرة لأسباب مختلفة، منها الآثار الجانبية التالية غير المرغوب فيها التالية لعملية إزالة لغة &
إلى :is(…)
:
- لها تأثير في النوعية. ويعود السبب في ذلك إلى أنّ
:is()
يتولى دقة الوسيطة الأكثر تحديدًا. - ولا تعمل هذه الميزة بشكل جيد مع العناصر الزائفة في المحدّد الخارجي الأصلي. ويعود سبب ذلك إلى أنّ
:is()
لا تقبل العناصر الزائفة في وسيطة قائمة الاختيار.
إليك المثال التالي:
#foo, .foo, .foo::before {
width: fit-content;
background-color: red;
@media screen {
background-color: green;
}
}
بعد تحليل هذا المقتطف، يصبح هذا المقتطف في Chrome قبل الإصدار 130:
#foo,
.foo,
.foo::before {
width: fit-content;
background-color: red;
@media screen {
& {
background-color: green;
}
}
}
هذه مشكلة لأنّ عنصر CSSRule
المدمج مع أداة الاختيار &
:
- تسطيح إلى
:is(#foo, .foo)
، مع إزالة.foo::before
من قائمة أداة الاختيار - لها درجة دقة تبلغ
(1,0,0)
، ما يجعل من الصعب استبدالها لاحقًا.
يمكنك التحقّق من ذلك من خلال فحص المحتوى الذي تُسلسله القاعدة:
↳ CSSStyleRule
.type = STYLE_RULE
.selectorText = "#foo, .foo, .foo::before"
.resolvedSelectorText = "#foo, .foo, .foo::before"
.specificity = (1,0,0),(0,1,0),(0,1,1)
.style (CSSStyleDeclaration, 2) =
- width: fit-content
- background-color: red
.cssRules (CSSRuleList, 1) =
↳ CSSMediaRule
.type = MEDIA_RULE
.cssRules (CSSRuleList, 1) =
↳ CSSStyleRule
.type = STYLE_RULE
.selectorText = "&"
.resolvedSelectorText = ":is(#foo, .foo, .foo::before)"
.specificity = (1,0,0)
.style (CSSStyleDeclaration, 1) =
- background-color: green
من الناحية المرئية، يعني ذلك أيضًا أنّ background-color
في .foo::before
هو red
بدلاً من green
.
من الأساليب الأخرى التي نظرت إليها مجموعة عمل CSS هي أن تطلب منك تضمين جميع التعريفات المُدمجة في قاعدة @nest
. تم رفض هذا الاقتراح بسبب التراجع في تجربة المطوّرين الذي سيؤدّي إليه.
لمحة عن واجهة CSSNestedDeclarations
الحلّ الذي استقرت عليه مجموعة عمل CSS هو إدخال قاعدة التعريفات المتداخلة.
يتم تنفيذ قاعدة البيانات المُدمجة هذه في Chrome بدءًا من الإصدار 130.
توافق المتصفّح
يؤدي إدخال قاعدة التعريفات المتداخلة إلى تغيير محلل CSS لتغليف التعريفات المتداخلة مباشرةً والمتتالية تلقائيًا في مثيل CSSNestedDeclarations
. عند التسلسل، ينتهي المطاف بهذا المثيل من CSSNestedDeclarations
في السمة cssRules
من CSSStyleRule
.
لنأخذ CSSStyleRule
التالي كمثال مرة أخرى:
.foo {
width: fit-content;
@media screen {
background-color: red;
}
background-color: green;
}
عند تسلسلها في الإصدار 130 من Chrome أو الإصدارات الأحدث، يظهر الرمز على النحو التالي:
↳ CSSStyleRule
.type = STYLE_RULE
.selectorText = ".foo"
.resolvedSelectorText = ".foo"
.specificity = (0,1,0)
.style (CSSStyleDeclaration, 1) =
- width: fit-content
.cssRules (CSSRuleList, 2) =
↳ CSSMediaRule
.type = MEDIA_RULE
.cssRules (CSSRuleList, 1) =
↳ CSSNestedDeclarations
.style (CSSStyleDeclaration, 1) =
- background-color: red
↳ CSSNestedDeclarations
.style (CSSStyleDeclaration, 1) =
- background-color: green
بما أنّ قاعدة CSSNestedDeclarations
تنتهي في CSSRuleList
، يتمكّن المُحلِّل من الاحتفاظ بموضع بيان background-color: green
: بعد بيان background-color: red
(الذي يُعدّ جزءًا من CSSMediaRule
).
علاوةً على ذلك، لا ينتج عن وجود مثيل CSSNestedDeclarations
أي آثار جانبية سيئة على الحلول المحتملة الأخرى التي تم تجاهلها والتي تسببت في الوقت الحالي: تتطابق قاعدة التعريفات المدمجة مع العناصر والعناصر الزائفة نفسها التي تتطابق تمامًا مع قاعدة النمط الأصلي الخاصة بها، مع سلوك الخصوصية نفسه.
يتمثل دليل ذلك في قراءة cssText
من CSSStyleRule
. وبفضل قاعدة الإعلانات المُدمجة، يكون هذا الأسلوب مطابقًا لأسلوب CSS المُدخل:
.foo {
width: fit-content;
@media screen {
background-color: red;
}
background-color: green;
}
معنى هذه السياسة
وهذا يعني أنّ عملية دمج CSS أصبحت أفضل بكثير اعتبارًا من الإصدار 130 من Chrome. ويعني ذلك أيضًا أنّه قد يكون عليك مراجعة بعض الرموز البرمجية إذا كنت تتخلّل بين التعريفات الأساسية القواعد المُدمجة.
راجِع المثال التالي الذي يستخدم الرمز الرائع @starting-style
.
/* This does not work in Chrome 130 */
#mypopover:popover-open {
@starting-style {
opacity: 0;
scale: 0.5;
}
opacity: 1;
scale: 1;
}
قبل الإصدار 130 من Chrome، كان يتم رفع هذه البيانات. سينتهي بك الأمر ببيانَي opacity: 1;
وscale: 1;
في CSSStyleRule.style
، متبوعَين بـ CSSStartingStyleRule
(يمثّل قاعدة @starting-style
) في CSSStyleRule.cssRules
.
اعتبارًا من الإصدار 130 من Chrome، لم تعُد يتمّ تصعيد البيانات، وينتهي بك الأمر بعنصرَين من النوع CSSRule
متداخلَين في CSSStyleRule.cssRules
. بالترتيب: واحدة CSSStartingStyleRule
(تمثل القاعدة @starting-style
) وCSSNestedDeclarations
واحدة تحتوي على تعريفات opacity: 1; scale: 1;
.
وبسبب هذا التغيير، يتم استبدال تعريفات @starting-style
بالتعريفات المضمَّنة في المثيل CSSNestedDeclarations
، ما يؤدي إلى إزالة حركة الإدخال.
لحلّ المشكلة في الرمز، تأكَّد من أنّ العنصر @starting-style
يأتي بعد التعريفات العادية. على النحو التالي:
/* This works in Chrome 130 */
#mypopover:popover-open {
opacity: 1;
scale: 1;
@starting-style {
opacity: 0;
scale: 0.5;
}
}
إذا أبقيت على التعريفات المُدمجة أعلى القواعد المُدمجة عند استخدام دمج CSS، سيعمل الرمز البرمجي في معظم الأحيان بشكل جيد مع جميع إصدارات جميع المتصفّحات التي تتيح دمج CSS.
أخيرًا، إذا كنت تريد إتاحة ميزة رصد مدى توفّر CSSNestedDeclarations
، يمكنك استخدام المقتطف التالي من JavaScript:
if (!("CSSNestedDeclarations" in self && "style" in CSSNestedDeclarations.prototype)) {
// CSSNestedDeclarations is not available
}