يتم تحسين تداخل CSS باستخدام CSSNestedDeclarations | مقالات | web.dev
تم النشر: 8 أكتوبر 2024
لإصلاح بعض المراوغات الغريبة في تداخل CSS، قررت مجموعة عمل CSS إضافة CSSNestedDeclarations
واجهة لمواصفات تداخل CSS. مع هذه الإضافة، لم تعد الإعلانات التي تأتي بعد قواعد النمط تتغير، من بين بعض التحسينات الأخرى.
تتوفر هذه التغييرات في Chrome بدءًا من الإصدار 130 وهي جاهزة للاختبار في Firefox Nightly 132 وSafari Technology Preview 204.
مشكلة تداخل CSS بدون CSSNestedDeclarations
إحدى المشكلات المتعلقة بتداخل CSS هي أن المقتطف التالي لا يعمل كما قد تتوقع في البداية:
.foo {
width: fit-content;
@media screen {
background-color: red;
}
background-color: green;
}
بالنظر إلى الكود، ستفترض أن <div class=foo>
العنصر لديه green
background-color
لأن 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 بشكل صحيح، يجب أن يكون قادرًا على التمييز بين الخصائص التي تظهر في بداية محتويات قاعدة النمط وتلك التي تظهر متخللة مع قواعد أخرى.
أما بالنسبة للتصريحات داخل 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 بدءًا من Chrome 130.
يؤدي إدخال قاعدة الإعلانات المتداخلة إلى تغيير محلل CSS ليقوم تلقائيًا بتغليف الإعلانات المتتابعة المتداخلة مباشرةً في ملف CSSNestedDeclarations
مثال. عند إجراء تسلسل، هذا CSSNestedDeclarations
المثال ينتهي في cssRules
ملكية CSSStyleRule
.
أخذ ما يلي CSSStyleRule
كمثال مرة أخرى:
.foo {
width: fit-content;
@media screen {
background-color: red;
}
background-color: green;
}
عند إجراء تسلسل في Chrome 130 أو الأحدث، يبدو الأمر كما يلي:
↳ 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 أصبح أفضل كثيرًا اعتبارًا من Chrome 130. ولكن هذا يعني أيضًا أنه قد يتعين عليك مراجعة بعض التعليمات البرمجية الخاصة بك إذا كنت تقوم بتشذير الإعلانات العارية مع القواعد المتداخلة.
خذ المثال التالي الذي يستخدم الرائع @starting-style
/* This does not work in Chrome 130 */
#mypopover:popover-open {
@starting-style {
opacity: 0;
scale: 0.5;
}
opacity: 1;
scale: 1;
}
قبل Chrome 130، سيتم رفع هذه الإعلانات. سينتهي بك الأمر مع 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
}