تعرَّف على كيفية استخدام @scope لتحديد العناصر فقط داخل شجرة فرعية محدودة في نموذج كائن المستند (DOM).
الفن الدقيق لكتابة أدوات اختيار لغة CSS
عند كتابة محدّدات، قد تجد نفسك منقسمًا بين عالمين. من ناحية، تريد أن تكون محددًا جدًا بشأن العناصر التي تحددها. من ناحية أخرى، تريد أن تظل أدوات الاختيار الخاصة بك سهلة التجاوز وألا تكون مرتبطة بإحكام ببنية DOM.
على سبيل المثال، عندما تريد تحديد "صورة الجزء الرئيسي في منطقة المحتوى لمكون البطاقة"، وهو اختيار عنصر محدّد، لا تريد على الأرجح كتابة أداة اختيار مثل .card > .content > img.hero
.
- تحتوي أداة الاختيار هذه على خصوصية عالية جدًا لـ
(0,3,1)
، ما يجعل من الصعب تجاوزها مع زيادة الرمز البرمجي. - ومن خلال الاعتماد على المُنشئ الثانوي المباشر، يقترن ببنية DOM بإحكام. وإذا تغير الترميز، فيجب تغيير CSS أيضًا.
وتذكَّر أيضًا أنّك لا تريد كتابة img
على أنّها أداة الاختيار لهذا العنصر فقط، لأنّ ذلك سيحدّد جميع عناصر الصورة على صفحتك.
وغالبًا ما يكون تحقيق التوازن الصحيح في هذه العملية تحديًا كبيرًا. على مرّ السنين، ابتكر بعض المطوّرين حلولاً وحلولاً بديلة لمساعدتك في مثل هذه المواقف. على سبيل المثال:
- وتنص منهجيات مثل BEM على منح هذا العنصر فئة
card__img card__img--hero
للحفاظ على مستوى الخصوصية منخفض مع السماح لك بالتركيز في الوقت نفسه على ما تختاره. - تعمل الحلول المستندة إلى JavaScript مثل CSS النطاق أو المكونات النمطية على إعادة كتابة جميع أدوات الاختيار من خلال إضافة سلاسل تم إنشاؤها عشوائيًا، مثل
sc-596d7e0e-4
، إلى أدوات الاختيار لمنعها من استهداف العناصر على الجانب الآخر من صفحتك. - تلغي بعض المكتبات أدوات الاختيار تمامًا وتطلب منك وضع مشغِّلات النمط مباشرةً في الترميز نفسه.
ولكن ماذا لو لم تكن بحاجة إلى أيٍّ منها؟ ماذا لو أعطاك CSS طريقة لتكون دقيقًا جدًا في ما يتعلق بالعناصر التي تحددها، بدون الحاجة إلى كتابة محددات عالية الدقة أو عناصر مرتبطة بإحكام بنموذج DOM الخاص بك؟ وهنا يأتي دور @scope
، ما يوفّر لك طريقة لاختيار العناصر فقط داخل شجرة فرعية في DOM.
نقدّم لك @scope
باستخدام @scope
، يمكنك الحدّ من مدى وصول أدوات الاختيار. ويمكنك إجراء ذلك من خلال ضبط جذر النطاق الذي يحدِّد الحدّ الأعلى للشجرة الفرعية التي تريد استهدافها. من خلال مجموعة جذر تحديد النطاق، لا يمكن لقواعد الأنماط المضمّنة، المسماة قواعد الأنماط ذات النطاق، الاختيار إلا من الشجرة الفرعية المحدودة في نموذج العناصر في المستند (DOM).
على سبيل المثال، لاستهداف عناصر <img>
فقط في المكوِّن .card
، يجب ضبط .card
كجذر تحديد نطاق قاعدة @scope
.
@scope (.card) {
img {
border-color: green;
}
}
يمكن لقاعدة النمط ذات النطاق img { … }
اختيار عناصر <img>
التي تقع في نطاق عنصر .card
المطابق بشكل فعّال.
لمنع اختيار عناصر <img>
داخل منطقة محتوى البطاقة (.card__content
)، يمكنك جعل أداة الاختيار img
أكثر تحديدًا. ويمكن إجراء ذلك أيضًا باستخدام حقيقة أنّ قاعدة @scope
تقبل أيضًا حد النطاق الذي يحدّد الحدّ الأدنى.
@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 */
}
}
تتم إضافة :scope
ضمنًا إلى أدوات الاختيار داخل قواعد الأنماط ذات النطاق الفرعي. يمكنك أن تكون واضحًا وصريحًا بشأن ذلك، من خلال إضافة :scope
بنفسك في بدايته. بدلاً من ذلك، يمكنك إضافة أداة اختيار &
في البداية، من CSS Nesting.
@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) { ... }
تجدر الإشارة إلى أنّ قواعد النمط ذات النطاق الفرعي نفسها لا يمكن أن تتخطّى الشجرة الفرعية. الاختيارات مثل :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
و&
داخل @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>
في المثال أعلاه، لا تستهدف القواعد المحدّدة النطاق سوى العناصر داخل div
باسم الفئة card__header
، لأنّ div
هو العنصر الرئيسي للعنصر <style>
.
@scope in the cascade
داخل تتالي CSS، تضيف @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>
عند عرض هذا الجزء الصغير من الترميز، سيكون الرابط الثالث هو white
بدلاً من black
، مع أنّه عنصر فرعي للعلامة 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
.
(صورة غلاف من قناة rustam burkhanov على Unspark)