تعرَّف على كيفية استخدام @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)