یاد بگیرید که چگونه از @scope برای انتخاب عناصر فقط در یک زیردرخت محدود از DOM خود استفاده کنید.
هنر ظریف نوشتن انتخابگرهای CSS
هنگام نوشتن انتخابگرها ممکن است بین دو دنیا سرگردان شوید. از یک طرف شما می خواهید در مورد عناصری که انتخاب می کنید کاملاً مشخص باشید. از سوی دیگر، شما می خواهید انتخابگرهای شما به راحتی قابل بازگردانی باقی بمانند و محکم به ساختار DOM متصل نشوند.
برای مثال، وقتی میخواهید «تصویر قهرمان در قسمت محتوای مؤلفه کارت» را انتخاب کنید – که یک انتخاب عنصر نسبتاً خاص است – به احتمال زیاد نمیخواهید انتخابکنندهای مانند .card > .content > img.hero
.
- این انتخابگر دارای ویژگی بسیار بالایی از
(0,3,1)
است که باعث میشود با بزرگتر شدن کد، نادیده گرفتن آن سخت شود. - با تکیه بر ترکیب کننده مستقیم فرزند، آن را محکم به ساختار DOM متصل می کند. اگر نشانه گذاری تغییر کند، باید CSS خود را نیز تغییر دهید.
اما، شما همچنین نمی خواهید فقط img
به عنوان انتخابگر آن عنصر بنویسید، زیرا با این کار تمام عناصر تصویر در صفحه شما انتخاب می شود.
یافتن تعادل مناسب در این مورد اغلب چالش برانگیز است. در طول سالها، برخی از توسعهدهندگان راهحلها و راهحلهایی برای کمک به شما در موقعیتهایی مانند این ارائه کردهاند. به عنوان مثال:
- روشهایی مانند BEM حکم میکند که به آن عنصر یک کلاس از
card__img card__img--hero
بدهید تا ویژگی را پایین نگه دارید و در عین حال به شما امکان میدهد در آنچه انتخاب میکنید خاص باشید. - راهحلهای مبتنی بر جاوا اسکریپت مانند Scoped CSS یا Styled Components ، همه انتخابگرهای شما را با افزودن رشتههایی که بهطور تصادفی تولید میشوند – مانند
sc-596d7e0e-4
– به انتخابگرهای شما بازنویسی میکنند تا از هدف قرار دادن عناصر در سمت دیگر صفحه شما جلوگیری کنند. - برخی از کتابخانهها حتی انتخابگرها را به کلی لغو میکنند و از شما میخواهند که محرکهای استایل را مستقیماً در خود نشانهگذاری قرار دهید.
اما اگر به هیچکدام از آن ها نیاز نداشتید چه؟ اگر CSS راهی را به شما می داد تا در مورد عناصری که انتخاب می کنید کاملاً مشخص باشید، بدون اینکه از شما بخواهد انتخابگرهایی با ویژگی بالا یا مواردی که کاملاً با DOM شما مرتبط هستند بنویسید؟ خب، اینجاست که @scope
وارد عمل میشود، و راهی را به شما پیشنهاد میکند که فقط در زیر درختی از DOM، عناصر را انتخاب کنید.
معرفی @scope
با @scope
می توانید دسترسی انتخابگرهای خود را محدود کنید. شما این کار را با تنظیم ریشه محدوده که مرز بالایی زیردرختی را که می خواهید هدف قرار دهید تعیین می کند، انجام می دهید. با یک مجموعه ریشه محدوده، قوانین سبک موجود - به نام قوانین سبک scoped - فقط می توانند از آن زیر درخت محدود DOM انتخاب کنند.
برای مثال، برای هدف قرار دادن فقط عناصر <img>
در مؤلفه .card
، .card
به عنوان ریشه دامنه @scope
at-rule تنظیم می کنید.
@scope (.card) {
img {
border-color: green;
}
}
قانون scoped style img { … }
به طور موثر فقط می تواند عناصر <img>
را انتخاب کند که در محدوده عنصر .card
همسان هستند.
برای جلوگیری از انتخاب عناصر <img>
در قسمت محتوای کارت ( .card__content
) می توانید انتخابگر img
را خاص تر کنید. راه دیگر برای انجام این کار استفاده از این واقعیت است که @scope
at-rule نیز یک محدودیت محدوده را می پذیرد که مرز پایین را تعیین می کند.
@scope (.card) to (.card__content) {
img {
border-color: green;
}
}
این قانون سبک محدوده فقط عناصر <img>
را هدف قرار می دهد که بین عناصر .card
و .card__content
در درخت اجداد قرار می گیرند. این نوع محدودهبندی – با مرز بالا و پایین – اغلب به عنوان محدوده دونات شناخته میشود.
انتخابگر :scope
بهطور پیشفرض، همه قوانین استایل محدودهبندی شده نسبت به ریشه دامنه هستند. همچنین میتوان خود عنصر ریشه دامنه را هدف قرار داد. برای این کار از انتخابگر :scope
استفاده کنید.
@scope (.card) {
:scope {
/* Selects the matched .card itself */
}
img {
/* Selects img elements that are a child of .card */
}
}
انتخابگرها در داخل قوانین سبک scoped به طور ضمنی :scope
prepended می شوند. اگر بخواهید، میتوانید با پیشفرض کردن :scope
خودتان در مورد آن صریح باشید. از طرف دیگر، میتوانید از CSS Nesting ، &
Selector را به حالت قبل اضافه کنید.
@scope (.card) {
img {
/* Selects img elements that are a child of .card */
}
:scope img {
/* Also selects img elements that are a child of .card */
}
& img {
/* Also selects img elements that are a child of .card */
}
}
یک محدودیت محدوده می تواند از کلاس شبه :scope
برای نیاز به یک رابطه خاص با ریشه دامنه استفاده کند:
/* .content is only a limit when it is a direct child of the :scope */
@scope (.media-object) to (:scope > .content) { ... }
یک محدودیت محدوده همچنین می تواند عناصر خارج از ریشه محدوده خود را با استفاده از :scope
ارجاع دهد. به عنوان مثال:
/* .content is only a limit when the :scope is inside .sidebar */
@scope (.media-object) to (.sidebar :scope .content) { ... }
توجه داشته باشید که قوانین استایل scoped خود نمی توانند از زیر درخت فرار کنند. انتخاب هایی مانند :scope + p
نامعتبر هستند زیرا سعی می کند عناصری را انتخاب کند که در محدوده نیستند.
@scope
و ویژگی
انتخابگرهایی که در مقدمه برای @scope
استفاده می کنید، بر ویژگی انتخابگرهای موجود تأثیر نمی گذارد. در مثال زیر، ویژگی انتخابگر img
همچنان (0,0,1)
است.
@scope (#sidebar) {
img { /* Specificity = (0,0,1) */
…
}
}
ویژگی :scope
مربوط به یک شبه کلاس معمولی است، یعنی (0,1,0)
.
@scope (#sidebar) {
:scope img { /* Specificity = (0,1,0) + (0,0,1) = (0,1,1) */
…
}
}
در مثال زیر، به صورت داخلی، &
در انتخابگری که برای ریشه دامنه استفاده میشود، در یک انتخابگر :is()
بازنویسی میشود. در پایان، مرورگر از :is(#sidebar, .card) img
به عنوان انتخابگر برای انجام تطبیق استفاده می کند. این فرآیند به نام شیرین سازی شناخته می شود.
@scope (#sidebar, .card) {
& img { /* desugars to `:is(#sidebar, .card) img` */
…
}
}
از آنجایی که &
با استفاده از :is()
قند زدایی می شود، ویژگی &
طبق قوانین اختصاصی بودن :is()
محاسبه می شود: ویژگی &
خاص ترین استدلال آن است.
در این مثال، ویژگی :is(#sidebar, .card)
خاص ترین آرگومان آن، یعنی #sidebar
است و بنابراین تبدیل به (1,0,0)
می شود. آن را با ویژگی img
ترکیب کنید - که (0,0,1)
است - و در نهایت به (1,0,1)
به عنوان ویژگی برای کل انتخابگر پیچیده خواهید رسید.
@scope (#sidebar, .card) {
& img { /* Specificity = (1,0,0) + (0,0,1) = (1,0,1) */
…
}
}
تفاوت بین :scope
و &
inside @scope
علاوه بر تفاوت در نحوه محاسبه ویژگی، تفاوت دیگر بین :scope
و &
این است که :scope
نشان دهنده ریشه محدوده منطبق است، در حالی که &
نشان دهنده انتخابگر مورد استفاده برای مطابقت با ریشه محدوده است.
به همین دلیل، امکان استفاده &
چندین بار وجود دارد. این بر خلاف :scope
است که فقط یک بار می توانید از آن استفاده کنید، زیرا نمی توانید یک ریشه محدوده را در داخل یک ریشه محدوده قرار دهید.
@scope (.card) {
& & { /* Selects a `.card` in the matched root .card */
}
:scope :scope { /* ❌ Does not work */
…
}
}
دامنه بدون پیش درآمد
هنگام نوشتن استایلهای درون خطی با عنصر <style>
، میتوانید قوانین سبک را به عنصر والد دربرگیرنده عنصر <style>
با مشخص نکردن هیچ ریشه محدودهای محدود کنید. شما این کار را با حذف پیش درآمد @scope
انجام می دهید.
<div class="card">
<div class="card__header">
<style>
@scope {
img {
border-color: green;
}
}
</style>
<h1>Card Title</h1>
<img src="…" height="32" class="hero">
</div>
<div class="card__content">
<p><img src="…" height="32"></p>
</div>
</div>
در مثال بالا، قوانین scoped فقط عناصر داخل div
را با نام کلاس card__header
هدف قرار می دهند، زیرا آن div
عنصر والد عنصر <style>
است.
@scope در آبشار
در داخل CSS Cascade ، @scope
یک معیار جدید نیز اضافه میکند: نزدیکی محدوده . مرحله بعد از مشخص بودن اما قبل از ترتیب ظاهر می آید.
هنگام مقایسه اعلانهایی که در قوانین سبک با ریشههای محدودهبندی متفاوت ظاهر میشوند، آنگاه اعلانی با کمترین جهش نسلی یا عنصری خواهر و برادر بین ریشه محدوده و موضوع قانون سبک محدوده برنده میشود.
این مرحله جدید هنگام تودرتو کردن چندین گونه از یک جزء مفید است. این مثال را در نظر بگیرید، که هنوز از @scope
استفاده نمی کند:
<style>
.light { background: #ccc; }
.dark { background: #333; }
.light a { color: black; }
.dark a { color: white; }
</style>
<div class="light">
<p><a href="#">What color am I?</a></p>
<div class="dark">
<p><a href="#">What about me?</a></p>
<div class="light">
<p><a href="#">Am I the same as the first?</a></p>
</div>
</div>
</div>
هنگام مشاهده آن مقدار کوچک نشانه گذاری، پیوند سوم به جای black
، white
خواهد بود، حتی اگر فرزند یک div
است که کلاس .light
روی آن اعمال شده است. این به دلیل معیار ترتیب ظاهر است که آبشار در اینجا برای تعیین برنده استفاده می کند. می بیند که .dark a
آخرین بار اعلام شده است، بنابراین از قانون .light a
برنده خواهد شد
با معیار مجاورت محدوده، اکنون حل شده است:
@scope (.light) {
:scope { background: #ccc; }
a { color: black;}
}
@scope (.dark) {
:scope { background: #333; }
a { color: white; }
}
از آنجایی که هر دو انتخابگر محدوده a
ویژگی یکسان دارند، معیار مجاورت محدوده عمل میکند. هر دو انتخابگر را با نزدیکی به ریشه محدوده آنها وزن می کند. برای آن a
سوم، فقط یک پرش به ریشه محدوده .light
دو تا به .dark
است. بنابراین، انتخابگر a
در .light
برنده خواهد شد.
نکته پایانی: جداسازی انتخابگر، نه جداسازی سبک
یکی از نکات مهمی که باید به آن توجه کنید این است که @scope
دسترسی انتخابگرها را محدود میکند، و جداسازی سبک را ارائه نمیدهد. ویژگیهایی که به فرزندان ارث میبرند، فراتر از محدوده پایین @scope
، همچنان به ارث میرسند. یکی از این ویژگی ها color
یکی است. هنگامی که آن را در داخل محدوده دونات اعلام می کنید، color
همچنان به کودکان داخل سوراخ دونات به ارث می رسد.
@scope (.card) to (.card__content) {
:scope {
color: hotpink;
}
}
در مثال بالا، عنصر .card__content
و فرزندان آن دارای رنگ hotpink
هستند زیرا مقدار را از .card
به ارث می برند.
(عکس روی جلد رستم بورخانوف در Unsplash )