सीएसएस @scope एट-रूल की मदद से, अपने सिलेक्टर की पहुंच सीमित करें

अपने DOM के सीमित सबट्री में मौजूद एलिमेंट चुनने के लिए, @scope का इस्तेमाल करने का तरीका जानें.

ब्राउज़र के इस्तेमाल से जुड़ी सहायता

  • Chrome: 118.
  • Edge: 118.
  • Firefox: फ़्लैग के पीछे.
  • Safari: 17.4.

सोर्स

सीएसएस सिलेक्टर लिखने का बेहतरीन तरीका

सिलेक्टर लिखते समय, आपको दो विकल्पों में से किसी एक को चुनना पड़ सकता है. एक तरफ़, आपको यह तय करना होता है कि आपको कौनसे एलिमेंट चुनने हैं. दूसरी ओर, आपको अपने सेलेक्टर को आसानी से बदलने की सुविधा चाहिए. साथ ही, यह ज़रूरी है कि वे डीओएम स्ट्रक्चर से ज़्यादा जुड़े न हों.

उदाहरण के लिए, अगर आपको “कार्ड कॉम्पोनेंट के कॉन्टेंट एरिया में हीरो इमेज” चुननी है, तो .card > .content > img.hero जैसा सिलेक्टर लिखने की ज़रूरत नहीं है. यह एक खास एलिमेंट है.

  • इस सिलेक्टर में (0,3,1) की खासता काफ़ी ज़्यादा है. इसलिए, कोड के बड़े होने पर इसे बदलना मुश्किल हो जाता है.
  • डायरेक्ट चाइल्ड कॉम्बिनेटर पर भरोसा करके, यह डीओएम स्ट्रक्चर से कसकर जुड़ा होता है. अगर मार्कअप कभी बदलता है, तो आपको अपनी सीएसएस भी बदलनी होगी.

हालांकि, आपको उस एलिमेंट के लिए सिलेक्टर के तौर पर सिर्फ़ img नहीं लिखना है, क्योंकि इससे आपके पेज पर मौजूद सभी इमेज एलिमेंट चुन लिए जाएंगे.

इसमें सही संतुलन बनाए रखना अक्सर मुश्किल होता है. पिछले कुछ सालों में, कुछ डेवलपर ने इस तरह की समस्याओं को हल करने के लिए कुछ तरीके और समाधान निकाले हैं. उदाहरण के लिए:

  • BEM जैसे तरीके बताते हैं कि आपको उस एलिमेंट को card__img card__img--hero की क्लास देनी चाहिए, ताकि आपने जो चुना है उसमें खास जानकारी शामिल न हो.
  • स्कोप वाली सीएसएस या स्टाइल वाले कॉम्पोनेंट जैसे JavaScript-आधारित समाधान, आपके सभी सिलेक्टर को फिर से लिखते हैं. इसके लिए, वे आपके सिलेक्टर में sc-596d7e0e-4 जैसी स्ट्रिंग जोड़ते हैं, जो अपने-आप जनरेट होती हैं. इससे, वे आपके पेज के दूसरी तरफ़ मौजूद एलिमेंट को टारगेट करने से बचते हैं.
  • कुछ लाइब्रेरी में सेलेक्टर की सुविधा पूरी तरह से बंद होती है. साथ ही, आपको स्टाइल ट्रिगर को सीधे मार्कअप में डालना पड़ता है.

लेकिन अगर आपको इनमें से किसी की भी ज़रूरत नहीं है, तो क्या होगा? क्या होगा, अगर सीएसएस आपको यह तय करने का विकल्प दे कि आपको कौनसे एलिमेंट चुनने हैं. इसके लिए, आपको ज़्यादा सटीक सिलेक्टर लिखने या ऐसे सिलेक्टर लिखने की ज़रूरत नहीं होगी जो आपके डीओएम से ज़्यादा जुड़े हों. ऐसे में, @scope का इस्तेमाल करके, सिर्फ़ अपने DOM के सबट्री में मौजूद एलिमेंट चुने जा सकते हैं.

पेश है @scope

@scope का इस्तेमाल करके, अपने सिलेक्टर की पहुंच को सीमित किया जा सकता है. ऐसा करने के लिए, स्कोपिंग रूट सेट करें. इससे, उस सबट्री की ऊपरी सीमा तय होती है जिसे आपको टारगेट करना है. स्कोप वाले रूट सेट में मौजूद स्टाइल नियम, सिर्फ़ DOM के उस सीमित सबट्री से चुने जा सकते हैं जिसे स्कोप वाले स्टाइल नियम कहा जाता है.

उदाहरण के लिए, .card कॉम्पोनेंट में सिर्फ़ <img> एलिमेंट को टारगेट करने के लिए, .card को @scope at-rule के स्कोपिंग रूट के तौर पर सेट किया जाता है.

@scope (.card) {
    img {
        border-color: green;
    }
}

स्कोप वाले स्टाइल नियम img { … } से, सिर्फ़ वे <img> एलिमेंट चुने जा सकते हैं जो मैच किए गए .card एलिमेंट के स्कोप में हों.

देखें

कार्ड के कॉन्टेंट एरिया (.card__content) में मौजूद <img> एलिमेंट के चुने जाने से रोकने के लिए, 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 */
    }
}

स्कोप वाले स्टाइल नियमों में मौजूद सिलेक्टर के आगे, :scope अपने-आप जुड़ जाता है. अगर आपको इस बारे में साफ़ तौर पर बताना है, तो :scope को पहले लगाएं. इसके अलावा, सीएसएस नेस्टिंग से & सिलेक्टर को पहले से जोड़ा जा सकता है.

@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

@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 रूट तक दो हॉप हैं. इसलिए, .light में मौजूद a सिलेक्टर जीतेगा.

देखें

आखिरी बात: स्टाइल आइसोलेशन के बजाय, सिलेक्टर आइसोलेशन

एक अहम बात यह है कि @scope, सिलेक्टर की पहुंच को सीमित करता है. यह स्टाइल अलग करने की सुविधा नहीं देता. जिन प्रॉपर्टी को चाइल्ड प्रॉपर्टी में इनहेरिट किया जाता है वे अब भी @scope की सबसे कम सीमा से ज़्यादा में इनहेरिट होंगी. ऐसी ही एक प्रॉपर्टी color है. अगर किसी डोनट स्कोप में एक एलिमेंट का एलान किया जाता है, तो color अब भी डोनट के होल में मौजूद चाइल्ड एलिमेंट को इनहेरिट करेगा.

@scope (.card) to (.card__content) {
  :scope {
    color: hotpink;
  }
}
देखें

ऊपर दिए गए उदाहरण में, .card__content एलिमेंट और उसके चाइल्ड एलिमेंट का रंग hotpink है, क्योंकि वे .card से वैल्यू इनहेरिट करते हैं.

(कवर फ़ोटो, rustam burkhanov की है. इसे Unsplash से लिया गया है)