ما الغرض من الفئة الزائفة للنطاق في CSS؟

يتم تعريف :scope في أدوات اختيار لغة CSS 4 على النحو التالي:

فئة زائفة تمثّل أي عنصر في مجموعة عناصر المرجع السياقي هذه مجموعة (قد تكون فارغة) محدّدة صراحةً من العناصر، مثل تلك المحدّدة من خلال querySelector() أو العنصر الرئيسي لعنصر <style scoped>، والتي تُستخدَم لتحديد "نطاق" المحدّد لكي يتطابق فقط ضمن شجرة فرعية.

في ما يلي مثال على استخدام هذه السمة في <style scoped> (مزيد من المعلومات):

<style>
    li {
    color: blue;
    }
</style>

<ul>
    <style scoped>
    li {
        color: red;
    }
    :scope {
        border: 1px solid red;
    }
    </style>
    <li>abc</li>
    <li>def</li>
    <li>efg</li>
</ul>

<ul>
    <li>hij</li>
    <li>klm</li>
    <li>nop</li>
</ul>

يؤدي ذلك إلى تلوين عناصر li باللون الأحمر في أول ul، ووضع حدود حول ul بموجب قاعدة :scope. ويعود السبب في ذلك إلى أنّ ul يتطابق مع :scope في سياق هذا <style scoped>. هذا هو السياق المحلي. إذا أضفنا قاعدة :scope في <style> الخارجي، ستتطابق مع المستند بأكمله. يعادل :root بشكل أساسي.

العناصر السياقية

من المرجّح أنّك على دراية بالإصدار Element من querySelector() وquerySelectorAll(). بدلاً من طلب البحث في المستند بأكمله، يمكنك حصر مجموعة النتائج بعنصر سياقي:

<ul>
    <li id="scope"><a>abc</a></li>
    <li>def</li>
    <li><a>efg</a></li>
</ul>
<script>
    document.querySelectorAll('ul a').length; // 2

    var scope = document.querySelector('#scope');
    scope.querySelectorAll('a').length; // 1
</script>

عند استدعاء هذه العناصر، يعرض المتصفّح NodeList تمّت فلترته لتضمين مجموعة العقد التي تطابق أ.) أداة الاختيار و ب.) تكون أيضًا من نسل عنصر السياق. في المثال الثاني، يعثر المتصفّح على جميع عناصر a، ثم يزيل العناصر التي لا تندرج ضمن عنصر scope. يعمل هذا الإجراء، ولكن يمكن أن يؤدي إلى بعض السلوكيات الغريبة إذا لم تكن حذرًا. تابع القراءة.

عند حدوث خطأ في querySelector

هناك نقطة مُهمّة حقًا في مواصفات المحدّدات غالبًا ما يتم تجاهلها. حتى عند استدعاء querySelector[All]() على عنصر، تظلّ المحدّدات تُقيّم في سياق المستند بأكمله. وهذا يعني أنّه يمكن أن تحدث أمور غير متوقّعة:

    scope.querySelectorAll('ul a').length); // 1
    scope.querySelectorAll('body ul a').length); // 1

WTF! في المثال الأول، ul هو العنصر الخاص بي، ومع ذلك لا يزال بإمكاني استخدامه ومطابقة العقد. في الحالة الثانية، لا يمثّل body سلفًا لعنصري، ولكنّ "body ul a" لا يزال مطابقًا. إنّ كلاهما أمران مربكان وغير متوقَّعَين.

من الجدير إجراء مقارنة بين jQuery هنا، الذي يتّبع النهج الصحيح وينفّذ ما تتوقّعه:

    $(scope).find('ul a').length // 0
    $(scope).find('body ul a').length // 0

…أدخِل :scope لحلّ هذه المشاكل النحوية.

إصلاح querySelector باستخدام :scope

أطلق WebKit مؤخرًا ميزة استخدام الفئة الزائفة :scope في querySelector[All](). يمكنك اختبارها في الإصدار 27 من Chrome Canary.

يمكنك استخدامها لحصر أدوات الاختيار بعنصر سياق. لنلقِ نظرة على مثال. في ما يلي، يتم استخدام :scope "لتحديد نطاق" أداة الاختيار على الشجرة الفرعية لعنصر النطاق. هذا صحيح، لقد قلت "نطاق" ثلاث مرات.

    scope.querySelectorAll(':scope ul a').length); // 0
    scope.querySelectorAll(':scope body ul a').length); // 0
    scope.querySelectorAll(':scope a').length); // 1

يؤدي استخدام :scope إلى جعل دلالات طرق querySelector() أكثر قابلية للتنبؤ وأكثر توافقًا مع ما يفعله الآخرون حاليًا، مثل jQuery.

هل حقّقت زيادة في الأداء؟

لا، ليس بعد :(

أريد معرفة ما إذا كان استخدام :scope في qS/qSA يؤدي إلى تحسين الأداء. وبما أنّني مهندس جيد، أجريتُ اختبارًا. السبب المنطقي: كلما قلّت مساحة العرض التي يبحث فيها المتصفّح عن المطابقة، كان البحث أسرع.

في تجربتي، يستغرق WebKit حاليًا وقتًا أطول بمقدار مرة ونصف إلى مرتين تقريبًا مقارنةً بعدم استخدام :scope. عارٍ عند إصلاح crbug.com/222028، من المفترض أن يؤدي استخدامها نظريًا إلى تحسين الأداء قليلاً مقارنةً بعدم استخدامها.