تاریخ انتشار: 8 اکتبر 2024
برای رفع برخی ایرادات عجیب و غریب در تودرتوی CSS، گروه کاری CSS تصمیم گرفت تا رابط CSSNestedDeclarations
را به CSS Nesting Specification اضافه کند. با این افزودن، اعلانهایی که پس از قوانین سبک ارائه میشوند، در میان برخی پیشرفتهای دیگر، دیگر تغییر نمیکنند.
این تغییرات در کروم از نسخه 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>
دارای background-color
green
است زیرا background-color: green;
اعلامیه آخر است اما در کروم قبل از نسخه 130 اینطور نیست. در آن نسخههایی که از CSSNestedDeclarations
پشتیبانی نمیکنند، background-color
عنصر red
است.
پس از تجزیه قانون واقعی کروم قبل از 130 استفاده به شرح زیر است:
.foo {
width: fit-content;
background-color: green;
@media screen {
& {
background-color: red;
}
}
}
CSS پس از تجزیه دو تغییر را تجربه کرد:
-
background-color: green;
برای پیوستن به دو اعلامیه دیگر به سمت بالا تغییر مکان داد. -
CSSMediaRule
تودرتو بازنویسی شد تا اعلانات خود را با استفاده از&
انتخابگر در یکCSSStyleRule
اضافی بپیچد.
یکی دیگر از تغییرات معمولی که در اینجا مشاهده می کنید، ویژگی های حذف تجزیه کننده است که از آن پشتیبانی نمی کند.
میتوانید با خواندن مجدد cssText
از CSSStyleRule
«CSS پس از تجزیه» را برای خودتان بررسی کنید.
خودتان آن را در این زمین بازی تعاملی امتحان کنید:
چرا این 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
به جای green
red
است.
رویکرد دیگری که گروه کاری 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;
}
هنگامی که در کروم 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
قرار میگیرد.
از Chrome 130 به بعد، اعلانها دیگر بالا نمیروند، و در نهایت با دو شیء تودرتوی 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
را شناسایی کنید، میتوانید از قطعه جاوا اسکریپت زیر استفاده کنید:
if (!("CSSNestedDeclarations" in self && "style" in CSSNestedDeclarations.prototype)) {
// CSSNestedDeclarations is not available
}