:has(): फ़ैमिली सिलेक्टर

सीएसएस के हिसाब से शुरू होने के बाद से, हमने अलग-अलग तरह से इस पर काम किया है. हमारी स्टाइल से "कैस्केडिंग स्टाइल शीट" बनती है. साथ ही, हमारे सिलेक्टर भी अच्छे लगते हैं. वे तिरछी हो सकती हैं. ज़्यादातर मामलों में, क्लिक की संख्या नीचे की ओर जाती है. लेकिन कभी भी ऊपर नहीं. कई सालों से, हम "माता-पिता चुनने के लिए" ऐप्लिकेशन के बारे में सोच रहे हैं. और अब यह आ गया है! :has() के स्यूडो सिलेक्टर के आकार में.

अगर पैरामीटर के तौर पर पास किया गया कोई भी सिलेक्टर, कम से कम एक एलिमेंट से मैच करता है, तो :has() सीएसएस स्यूडो-क्लास किसी एलिमेंट को दिखाता है.

हालांकि, यह "माता-पिता" के सिलेक्टर से कहीं ज़्यादा है. यह उसकी मार्केटिंग करने का एक अच्छा तरीका है. "शर्त के साथ एनवायरमेंट" चुनने का विकल्प, ऐसा हो सकता है जो आकर्षक न हो. हालांकि, उसमें कोई रिंग नहीं है. "फ़ैमिली" चुनने वाले टूल के बारे में क्या ख्याल है?

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

और आगे बढ़ने से पहले, ब्राउज़र की सुविधा के बारे में बताना ज़रूरी है. अभी तक ऐसा नहीं है. लेकिन, यह लक्ष्य करीब बढ़ता जा रहा है. अभी तक कोई Firefox समर्थन नहीं है, यह रोडमैप में है. हालांकि, यह पहले से ही Safari में है और इसे Chromium 105 में रिलीज़ किया जाना है. इस लेख में दिए गए सभी डेमो से आपको पता चलेगा कि अगर ये डेमो, इस्तेमाल किए गए ब्राउज़र में काम नहीं करते हैं.

देखें

:has को इस्तेमाल करने का तरीका

यह कैसा दिखेगा? everybody क्लास के साथ नीचे दिए गए एचटीएमएल को दो सिबलिंग एलिमेंट के साथ देखें. a-good-time क्लास के डिसेंडेंट को कैसे चुनें?

<div class="everybody">
  <div>
    <div class="a-good-time"></div>
  </div>
</div>

<div class="everybody"></div>

:has() के साथ, नीचे दी गई सीएसएस की मदद से ऐसा किया जा सकता है.

.everybody:has(.a-good-time) {
  animation: party 21600s forwards;
}

यह .everybody का पहला इंस्टेंस चुनता है और animation लागू करता है.

इस उदाहरण में, everybody क्लास वाला एलिमेंट टारगेट है. शर्त में a-good-time क्लास के साथ डिसेंडेंट है.

<target>:has(<condition>) { <styles> }

हालांकि, इसे और भी बेहतर बनाया जा सकता है, क्योंकि :has() से बेशुमार मौके मिलते हैं. वह भी शायद अभी तक नहीं खोजे गए. इनमें से कुछ के बारे में सोचें.

ऐसे figure एलिमेंट चुनें जिनमें डायरेक्ट figcaption शामिल हो. css figure:has(> figcaption) { ... } उन anchor को चुनें जिनमें सीधे तौर पर SVG डिसेंडेंट नहीं है css a:not(:has(> svg)) { ... } ऐसे label चुनें जिनका सीधा input सिबलिंग है. तिरछा हो रहा है! css label:has(+ input) { … } articles को चुनें, जहां डिसेंडेंट img में alt टेक्स्ट नहीं है css article:has(img:not([alt])) { … } वह documentElement चुनें जहां DOM में कुछ स्टेट मौजूद है css :root:has(.menu-toggle[aria-pressed=”true”]) { … } उस लेआउट कंटेनर को चुनें जिस पर बच्चों की विषम संख्या मौजूद है css .container:has(> .container__item:last-of-type:nth-of-type(odd)) { ... } ग्रिड के उन सभी आइटम को चुनें जिन पर कर्सर नहीं रखा गया है css .grid:has(.grid__item:hover) .grid__item:not(:hover) { ... } कस्टम एलिमेंट वाला कस्टम एलिमेंट चुनें <todo-list> css main:has(todo-list) { ... } ऐसे हर {11/मिक्स का नाम चुनें जो एक से ज़्यादा {11/मिक्स है. 2.1/सभी है.}articleahrcss p:has(+ hr) a:only-child { … }css article:has(>h1):has(>h2) { … } ऐसा article चुनें जिसमें टाइटल के बाद कैप्शन आता है css article:has(> h1 + h2) { … } इंटरैक्टिव स्टेट ट्रिगर होने पर :root को चुनें css :root:has(a:hover) { … } figure के बाद वाला पैराग्राफ़ चुनें, जिसमें figcaption न हो css figure:not(:has(figcaption)) + p { … }

:has() के इस्तेमाल के कौनसे दिलचस्प उदाहरण हैं? यहां की सबसे मज़ेदार बात यह है कि आपको अपने मानसिक मॉडल को तोड़ने के लिए बढ़ावा मिलता है. इससे आपको लगता है कि "क्या इन स्टाइल को किसी और तरीके से अपनाया जा सकता है?".

उदाहरण

आइए, कुछ उदाहरणों से समझते हैं कि इसका इस्तेमाल कैसे किया जा सकता है.

कार्ड

एक क्लासिक कार्ड डेमो लें. हम अपने कार्ड में कोई भी जानकारी दिखा सकते हैं. उदाहरण के लिए: कोई टाइटल, सबटाइटल या कुछ मीडिया. यह रहा बेसिक कार्ड.

<li class="card">
  <h2 class="card__title">
      <a href="#">Some Awesome Article</a>
  </h2>
  <p class="card__blurb">Here's a description for this awesome article.</p>
  <small class="card__author">Chrome DevRel</small>
</li>

क्या होगा जब आपको कुछ मीडिया का परिचय देना हो? इस डिज़ाइन के लिए कार्ड को दो कॉलम में बांटा जा सकता है. इस व्यवहार को दिखाने के लिए, आपको पहले एक नई क्लास बनानी पड़ सकती है, जैसे कि card--with-media या card--two-columns. इन क्लास के नामों को समझना मुश्किल होता है. साथ ही, इन्हें याद रखना और बनाए रखना भी मुश्किल हो जाता है.

:has() की मदद से, यह पता लगाया जा सकता है कि कार्ड में कोई मीडिया कॉन्टेंट मौजूद है और उसके हिसाब से सही काम किया जा सकता है. कार्रवाई बदलने वाली क्लास के नामों की ज़रूरत नहीं है.

<li class="card">
  <h2 class="card__title">
    <a href="/article.html">Some Awesome Article</a>
  </h2>
  <p class="card__blurb">Here's a description for this awesome article.</p>
  <small class="card__author">Chrome DevRel</small>
  <img
    class="card__media"
    alt=""
    width="400"
    height="400"
    src="./team-awesome.png"
  />
</li>

और आपको इसे वहीं छोड़ना नहीं है. आप इसके साथ क्रिएटिव हो सकते हैं. “चुनिंदा” कॉन्टेंट दिखाने वाला कार्ड, लेआउट के हिसाब से कैसे बदला जा सकता है? यह सीएसएस, चुनिंदा कार्ड को लेआउट की पूरी चौड़ाई के तौर पर सेट करेगी और इसे ग्रिड की शुरुआत में रखेगी.

.card:has(.card__banner) {
  grid-row: 1;
  grid-column: 1 / -1;
  max-inline-size: 100%;
  grid-template-columns: 1fr 1fr;
  border-left-width: var(--size-4);
}

क्या होगा अगर बैनर के साथ कोई चुनिंदा कार्ड ध्यान के लिए हिलता है?

<li class="card">
  <h2 class="card__title">
    <a href="#">Some Awesome Article</a>
  </h2>
  <p class="card__blurb">Here's a description for this awesome article.</p>
  <small class="card__author">Chrome DevRel</small>
  <img
    class="card__media"
    alt=""
    width="400"
    height="400"
    src="./team-awesome.png"
  />
  <div class="card__banner"></div>
</li>

.card:has(.card__banner) {
  --color: var(--green-3-hsl);
  animation: wiggle 6s infinite;
}

ढेर सारी संभावनाएं.

फ़ॉर्म

फ़ॉर्म कैसा रहेगा? उन्हें अपने स्टाइल में पेचीदा होने के लिए जाना जाता है. इनपुट और उनके लेबल को स्टाइल देना, इसका एक उदाहरण है. उदाहरण के लिए, हम कैसे बताते हैं कि फ़ील्ड मान्य है? :has() के साथ, यह काफ़ी आसान हो जाता है. आपके पास सही फ़ॉर्म को स्यूडो-क्लास में जोड़ने का विकल्प होता है, जैसे कि :valid और :invalid.

<div class="form-group">
  <label for="email" class="form-label">Email</label>
  <input
    required
    type="email"
    id="email"
    class="form-input"
    title="Enter valid email address"
    placeholder="Enter valid email address"
  />   
</div>
label {
  color: var(--color);
}
input {
  border: 4px solid var(--color);
}

.form-group:has(:invalid) {
  --color: var(--invalid);
}

.form-group:has(:focus) {
  --color: var(--focus);
}

.form-group:has(:valid) {
  --color: var(--valid);
}

.form-group:has(:placeholder-shown) {
  --color: var(--blur);
}

इसे इस उदाहरण में आज़माएं: मान्य और अमान्य वैल्यू डालकर, फ़ोकस को चालू और बंद करके देखें.

देखें

किसी फ़ील्ड के लिए गड़बड़ी का मैसेज दिखाने और छिपाने के लिए भी :has() का इस्तेमाल किया जा सकता है. हमारे “ईमेल” फ़ील्ड ग्रुप को चुनें और उसमें गड़बड़ी का मैसेज जोड़ें.

<div class="form-group">
  <label for="email" class="form-label">
    Email
  </label>
  <div class="form-group__input">
    <input
      required
      type="email"
      id="email"
      class="form-input"
      title="Enter valid email address"
      placeholder="Enter valid email address"
    />   
    <div class="form-group__error">Enter a valid email address</div>
  </div>
</div>

डिफ़ॉल्ट रूप से, गड़बड़ी का मैसेज छिपाया जाता है.

.form-group__error {
  display: none;
}

हालांकि, जब फ़ील्ड :invalid हो जाता है और फ़ोकस नहीं किया जाता है, तो अतिरिक्त क्लास के नामों के बिना भी मैसेज दिखाया जा सकता है.

.form-group:has(:invalid:not(:focus)) .form-group__error {
  display: block;
}

कोई भी वजह नहीं है कि आपके उपयोगकर्ता आपके फ़ॉर्म से इंटरैक्ट करने के लिए, मज़ेदार तरीके का इस्तेमाल कर सकें. यह उदाहरण देखें. माइक्रो-इंटरैक्शन के लिए कोई मान्य वैल्यू डाले जाने पर उन्हें देखें. :invalid वैल्यू सेट करने पर फ़ॉर्म ग्रुप हिल जाएगा. लेकिन, सिर्फ़ तभी, जब उपयोगकर्ता ने मोशन की कोई सेटिंग तय न की हो.

देखें

कॉन्टेंट

हमने कोड के उदाहरणों में इस बारे में बताया था. हालांकि, आपको अपने दस्तावेज़ में :has() का इस्तेमाल कैसे करना चाहिए? यह इस बारे में आइडिया देता है कि हम मीडिया के आस-पास टाइपोग्राफ़ी को किस तरह स्टाइल कर सकते हैं.

figure:not(:has(figcaption)) {
  float: left;
  margin: var(--size-fluid-2) var(--size-fluid-2) var(--size-fluid-2) 0;
}

figure:has(figcaption) {
  width: 100%;
  margin: var(--size-fluid-4) 0;
}

figure:has(figcaption) img {
  width: 100%;
}

इस उदाहरण में आंकड़े शामिल हैं. अगर उनके पास figcaption नहीं होता है, तो वे कॉन्टेंट में फ़्लोट करते हैं. जब figcaption मौजूद होता है, तो वे पूरी चौड़ाई लेते हैं और उन्हें ज़्यादा मार्जिन मिलता है.

देखें

राज्य पर प्रतिक्रिया देना

हमारे मार्कअप में अपनी स्टाइल को किसी स्थिति पर फिर से लागू करने के बारे में कैसे सोचना है. "क्लासिक" स्लाइडिंग नेविगेशन बार वाला एक उदाहरण देखें. अगर आपके पास कोई ऐसा बटन है जो नेविगेशन को खोलने के लिए टॉगल करता है, तो वह aria-expanded एट्रिब्यूट का इस्तेमाल कर सकता है. JavaScript का इस्तेमाल सही एट्रिब्यूट को अपडेट करने के लिए किया जा सकता है. जब aria-expanded, true हो, तो इसका पता लगाने और स्लाइड करने वाले नेविगेशन के लिए स्टाइल अपडेट करने के लिए, :has() का इस्तेमाल करें. JavaScript अपना काम करता है और सीएसएस उस जानकारी के साथ अपना काम कर सकता है. मार्कअप को शफ़ल करने या क्लास के ज़्यादा नाम वगैरह जोड़ने की ज़रूरत नहीं है. ध्यान दें: यह उदाहरण, प्रोडक्शन के लिए तैयार नहीं है.

:root:has([aria-expanded="true"]) {
    --open: 1;
}
body {
    transform: translateX(calc(var(--open, 0) * -200px));
}
देखें

क्या :उपयोगकर्ता की गड़बड़ी से बचने में मदद मिल सकती है?

इन सभी उदाहरणों में क्या समानता है? इस तथ्य के अलावा कि वे :has() को इस्तेमाल करने के तरीके दिखाते हैं, उनमें से किसी को भी क्लास के नामों में बदलाव करने की ज़रूरत नहीं है. उनमें से हर एक ने नया कॉन्टेंट डाला और एक एट्रिब्यूट अपडेट किया. यह :has() का सबसे बड़ा फ़ायदा है. इससे उपयोगकर्ता की गड़बड़ी को कम करने में मदद मिल सकती है. :has() की मदद से सीएसएस, डीओएम में किए गए बदलावों में बदलाव करने की ज़िम्मेदारी ले सकती है. आपको JavaScript में क्लास के नामों को एक साथ रखने की ज़रूरत नहीं है. इससे डेवलपर को मिलने वाली गड़बड़ी की संभावना कम हो जाती है. क्लास का नाम टाइप करने में हम सभी की मदद की गई है. हमें पता है कि इसे Object लुकअप में रखना ज़रूरी है.

यह एक दिलचस्प विचार है और क्या यह हमें साफ़ मार्कअप और कम कोड की ओर ले जाता है? हम JavaScript की संख्या कम कर रहे हैं, क्योंकि हम JavaScript में ज़्यादा बदलाव नहीं कर रहे हैं. कम एचटीएमएल, क्योंकि अब आपको card card--has-media वगैरह जैसी क्लास की ज़रूरत नहीं है.

कुछ अलग तरीके से सोचने के लिए

जैसा कि ऊपर बताया गया है, :has() आपको मानसिक मॉडल को तोड़ने के लिए बढ़ावा देता है. यह अलग-अलग चीज़ें आज़माने का मौका है. सीमा पार करने का एक तरीका यह है कि सिर्फ़ सीएसएस का इस्तेमाल करके, गेम के काम करने के तरीके तैयार करें. उदाहरण के लिए, Forms और सीएसएस की मदद से, सिलसिलेवार तरीके से काम करने की प्रोसेस तैयार की जा सकती है.

<div class="step">
  <label for="step--1">1</label>
  <input id="step--1" type="checkbox" />
</div>
<div class="step">
  <label for="step--2">2</label>
  <input id="step--2" type="checkbox" />
</div>
.step:has(:checked), .step:first-of-type:has(:checked) {
  --hue: 10;
  opacity: 0.2;
}


.step:has(:checked) + .step:not(.step:has(:checked)) {
  --hue: 210;
  opacity: 1;
}

और इससे दिलचस्प संभावनाएं मिलती हैं. आप इसका इस्तेमाल 'ट्रांसफ़ॉर्म' वाले किसी फ़ॉर्म को ट्रेस करने के लिए कर सकते हैं. ध्यान दें कि यह डेमो, किसी दूसरे ब्राउज़र टैब में सबसे अच्छा दिखता है.

और मज़े के लिए, क्लासिक बज़ वायर गेम कैसा रहेगा? :has() की मदद से, इसे आसानी से बनाया जा सकता है. अगर तार ऊपर घुमाया जाता है, तो गेम खत्म हो जाएगा. हां, हम सिबलिंग कॉम्बिनेटर (+ और ~) जैसी चीज़ों के साथ इनमें से कुछ गेम मैकेनिक्स बना सकते हैं. लेकिन, :has() दिलचस्प मार्कअप "ट्रिक" का इस्तेमाल किए बिना, इन ही नतीजे पाने का एक तरीका है. ध्यान दें कि यह डेमो, किसी दूसरे ब्राउज़र टैब में सबसे अच्छा दिखता है.

हालांकि, जल्द ही इन्हें प्रोडक्शन में शामिल नहीं किया जाएगा, लेकिन इनमें पुराने तरीकों को हाइलाइट किया गया है. जैसे, किसी :has() की चेन बनाना.

:root:has(#start:checked):has(.game__success:hover, .screen--win:hover)
.screen--win {
  --display-win: 1;
}

परफ़ॉर्मेंस और सीमाएं

जाने से पहले, आप :has() के साथ क्या नहीं कर सकती? :has() पर कुछ पाबंदियां लगी हैं. मुख्य मेट्रिक, परफ़ॉर्मेंस हिट की वजह से दिखती हैं.

  • आपके पास :has() को :has() करने का विकल्प नहीं है. हालांकि, :has() की चेन बनाई जा सकती है. css :has(.a:has(.b)) { … }
  • :has() में, सूडो एलिमेंट का इस्तेमाल नहीं किया गया है css :has(::after) { … } :has(::first-letter) { … }
  • सिर्फ़ कंपाउंड सिलेक्टर को स्वीकार करने वाले सूडो के अंदर, :has() के इस्तेमाल पर पाबंदी लगाएं css ::slotted(:has(.a)) { … } :host(:has(.a)) { … } :host-context(:has(.a)) { … } ::cue(:has(.a)) { … }
  • सूडो एलिमेंट के बाद, :has() के इस्तेमाल पर पाबंदी लगाएं css ::part(foo):has(:focus) { … }
  • :visited का इस्तेमाल हमेशा गलत होगा css :has(:visited) { … }

:has() से जुड़ी असल परफ़ॉर्मेंस मेट्रिक के लिए, यह Glitch देखें. लागू करने से जुड़ी अहम जानकारी और जानकारी शेयर करने के लिए, Byungwer को क्रेडिट दें.

बस इतना ही!

:has() के लिए तैयार हो जाएं. अपने दोस्तों को इसके बारे में बताएं और इस पोस्ट को शेयर करें. इससे, सीएसएस के साथ काम करने का हमारा तरीका बदल जाएगा.

इस CodePen Colllection में सभी डेमो उपलब्ध हैं.