: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) { … } ऐसे article चुनें जिनके वंशज img में alt टेक्स्ट न हो css article:has(img:not([alt])) { … } ऐसा documentElement चुनें जिसमें डीओएम में कोई स्टेटस मौजूद हो 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) { ... } पैराग्राफ़ में मौजूद हर उस a को चुनें जिसमें सीधा भाई-बहन hr एलिमेंट हो css p:has(+ hr) a:only-child { … } ऐसा article चुनें जिसमें कई शर्तें पूरी होती हों 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() का यह एक बड़ा फ़ायदा है कि इससे उपयोगकर्ता की गड़बड़ी को कम करने में मदद मिलती है. :has() की मदद से, सीएसएस, डीओएम में किए गए बदलावों के हिसाब से अपने-आप अडजस्ट हो जाती है. आपको JavaScript में क्लास के नामों को जॉग करने की ज़रूरत नहीं है. इससे, डेवलपर की गड़बड़ी की संभावना कम हो जाती है. हम सभी ने कभी न कभी क्लास के नाम में टाइपिंग की गड़बड़ी की है. ऐसे में, हमें Object लुकअप में उन्हें शामिल करना पड़ता है.

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

कुछ हटके सोचना

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

<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() से जुड़ी असल परफ़ॉर्मेंस मेट्रिक के लिए, यह ग्लिच देखें. लागू करने के बारे में इन अहम जानकारी और जानकारी शेयर करने के लिए, ब्यंगवू को क्रेडिट.

बस इतना ही!

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

सभी डेमो, इस CodePen कलेक्शन में उपलब्ध हैं.