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

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

ब्राउज़र सहायता

  • 118
  • 118
  • x
  • x

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

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

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

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

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

ऐसे में, सही संतुलन बनाना काफ़ी मुश्किल होता है. पिछले कुछ सालों में, कुछ डेवलपर ने ऐसे मुश्किल हल निकालने के लिए कई तरीके हल किए हैं. उदाहरण के लिए:

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

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

पेश है @scope

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

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

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

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

कार्ड के कॉन्टेंट एरिया (.card__content) में <img> एलिमेंट को न चुनने के लिए, 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 को खुद ही पहले से लिखकर, इस बारे में साफ़ तौर पर जानकारी दी जा सकती है. इसके अलावा, सीएसएस 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 */
  }
  :root :root { /* ❌ 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 एक नया मानदंड भी जोड़ता है: स्कोपिंग प्रॉक्सिमिटी. चरण विशिष्टता के बाद आता है, लेकिन दिखने के क्रम से पहले.

CSS कैस्केड का विज़ुअलाइज़ेशन.

हर स्पेसिफ़िकेशन के मुताबिक:

अलग-अलग रेंज वाले रूट के साथ स्टाइल के नियमों में दिखने वाले एलान की तुलना करते समय, सबसे कम जनरेशन या सिबलिंग एलिमेंट वाली घोषणा, स्कोपिंग रूट और स्कोप वाले स्टाइल नियम के सब्जेक्ट के बीच बदलती है.

किसी कॉम्पोनेंट के कई वैरिएशन को नेस्ट करते समय, यह नया तरीका मददगार होता है. यह उदाहरण लें, जिसमें अभी @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 की वैल्यू लागू होती है.

(Unsplash पर रस्टम बुर्खानोव की कवर फ़ोटो)