تعرَّف على كيفية استخدام @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
على أنّه جذر النطاق لقاعدة at-rule الخاصة بـ @scope
.
@scope (.card) {
img {
border-color: green;
}
}
لا يمكن لقاعدة الأسلوب على مستوى النطاق img { … }
اختيار عناصر <img>
إلا إذا كانت ضمن النطاق للعنصر .card
المطابق.
لمنع اختيار عناصر <img>
داخل منطقة محتوى البطاقة (.card__content
)، يمكنك جعل أداة اختيار img
أكثر تحديدًا. هناك طريقة أخرى لإجراء ذلك وهي استخدام حقيقة أنّ قاعدة at-rule @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.
@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 في التسلسل
ضمن تسلسل 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 على Unsplash)