पहले, सीएसएस सिलेक्टर सिर्फ़ नीचे की ओर ट्रैवर्स कर सकते थे. इसका मतलब है कि पैरंट के आधार पर चाइल्ड को स्टाइल किया जा सकता था, लेकिन चाइल्ड के आधार पर पैरंट को स्टाइल नहीं किया जा सकता था. :has() स्यूडो-क्लास इसे बदल देती है. इससे आपको किसी कंटेनर एलिमेंट को उसके डिसेंडेंट की मौजूदगी या स्थिति के आधार पर, शर्त के साथ स्टाइल करने की सुविधा मिलती है.
:has() को स्टेट स्यूडो-क्लास (जैसे, :checked, :focus, :valid, :invalid या :not()) के साथ जोड़कर, पूरी तरह से सीएसएस में जटिल और इंटरैक्टिव यूआई कॉम्पोनेंट बनाए जा सकते हैं. इसके लिए, पैरंट एलिमेंट पर "मॉडिफ़ायर" क्लास (जैसे, .is-active या .has-error) को टॉगल करने के लिए JavaScript की ज़रूरत नहीं होती.
यह उन कॉम्पोनेंट के लिए खास तौर पर मददगार है जिन्हें इंटरनल इंटरैक्शन का जवाब देना होता है. जैसे, चेकबॉक्स (:checked) पर प्रतिक्रिया देने वाला स्थानीय थीम टॉगल, गड़बड़ी को हाइलाइट करने वाला फ़ॉर्म ग्रुप (:invalid) या चाइल्ड लिंक पर फ़ोकस करने पर ऊपर उठने वाला कार्ड (:focus-within).
स्टेट के हिसाब से कंटेनर की स्टाइलिंग लागू करना
ज़रूरी: आपको कंटेनर एलिमेंट पर :has() सिलेक्टर का इस्तेमाल करना होगा, ताकि उसके इंटरैक्टिव चाइल्ड एलिमेंट की खास स्थिति (जैसे, :checked, :focus, :invalid) का पता लगाया जा सके.
ऐसा कॉम्पोनेंट बनाने के लिए जो चाइल्ड कॉम्पोनेंट की स्थिति के आधार पर अपनी स्टाइल बदलता है:
- डिफ़ॉल्ट स्टाइल तय करना: कंटेनर की बेस स्टेट तय करने के लिए, उस पर सीएसएस वैरिएबल सेट करें.
- स्टेट के आधार पर बदलाव लागू करना: कंटेनर को
:has([child-selector]:[state])से टारगेट करें. साथ ही, ऐक्टिव या वैकल्पिक स्टेट के लिए, सीएसएस वैरिएबल को फिर से तय करें.
उदाहरण: ऐसा कॉम्पोनेंट जो चाइल्ड टॉगल के आधार पर थीम बदलता है.
/* 1. Define the default state on the component container */
.theme-card {
/* Using custom properties makes state-switching cleaner */
--card-bg: #ffffff;
--card-text: #333333;
--card-border: #cccccc;
background-color: var(--card-bg);
color: var(--card-text);
border: 1px solid var(--card-border);
/* Use a transition for smooth state changes */
transition: background-color 0.3s, color 0.3s;
}
/* 2. Apply styles when the child enters the specific state */
/* MANDATORY: Target the container and use :has() to check the descendant's state */
.theme-card:has(.theme-toggle:checked) {
/* Override the properties for the active or alternate state */
--card-bg: #222222;
--card-text: #f0f0f0;
--card-border: #555555;
}
/* You can also combine :has() and :not() to target specific negative states */
/* This selects a card that DOES NOT have a toggle in the checked state */
.theme-card:not(:has(.theme-toggle:checked)) {
/* Optional: Explicit styles for the unchecked state if needed */
}
<!-- The container element that receives the styling -->
<div class="theme-card">
<!-- The child element whose state controls the parent -->
<label>
<input type="checkbox" class="theme-toggle">
Enable Dark Mode
</label>
<h2>Card Title</h2>
<p>The style of this entire card is controlled by the checkbox above.</p>
</div>
परफ़ॉर्मेंस से जुड़ी सलाह: :has() का इस्तेमाल करते समय, सिलेक्टर को ज़्यादा से ज़्यादा खास कंटेनर (जैसे, .theme-card) तक सीमित रखें. अगर स्टाइल में किए गए बदलाव स्थानीय हैं, तो इसे body:has(...) जैसे बहुत हाई-लेवल एलिमेंट पर ऐंकर करने से बचें. ऐसा इसलिए, क्योंकि ब्रॉड :has() क्वेरी, लेआउट को फिर से कैलकुलेट करने की प्रोसेस को ट्रिगर कर सकती हैं.
फ़ॉलबैक रणनीतियां
अगर उपयोगकर्ता अनुभव या पेज लेआउट के लिए, स्टेट-बेस्ड स्टाइलिंग ज़रूरी है, तो आपको उन ब्राउज़र के लिए फ़ॉलबैक उपलब्ध कराना होगा जो :has() सिलेक्टर के साथ काम नहीं करते. सिर्फ़ सजावटी इफ़ेक्ट के लिए, :has() का इस्तेमाल फ़ॉलबैक के बिना, प्रोग्रेसिव एन्हांसमेंट के तौर पर किया जा सकता है.
ज़रूरी: ज़रूरी सुविधाओं के लिए फ़ॉलबैक लागू करते समय, आपको अपनी सीएसएस में @supports not selector(:has(*)) का इस्तेमाल करना होगा. इससे क्लास पर आधारित ट्रेडिशनल फ़ॉलबैक को तय किया जा सकेगा. अगर ज़रूरी स्थिति में बदलाव, उपयोगकर्ता के इंटरैक्शन पर निर्भर करता है, तो आपको CSS.supports() के साथ एक छोटी इनलाइन स्क्रिप्ट का भी इस्तेमाल करना होगा. इससे, स्थिति में बदलाव को दिखाने वाले JavaScript इवेंट (जैसे, change, focus, blur) के आधार पर उस क्लास को टॉगल किया जा सकेगा.
/* Fallback CSS for older browsers */
/* We check if the browser DOES NOT support the :has() selector */
@supports not selector(:has(*)) {
/* Define a traditional modifier class that applies the exact same overrides */
.theme-card.is-active {
--card-bg: #222222;
--card-text: #f0f0f0;
--card-border: #555555;
}
}
/* Fallback JavaScript for older browsers */
/* Check for support before running the script to avoid unnecessary work in modern browsers */
if (!CSS.supports('selector(:has(*))')) {
const toggle = document.querySelector('.theme-toggle');
const card = document.querySelector('.theme-card');
if (toggle && card) {
// Manually toggle the fallback class when the input state changes
toggle.addEventListener('change', (e) => {
card.classList.toggle('is-active', e.target.checked);
});
}
}