डिक्लेरेटिव डायलॉग और पॉपओवर कंट्रोल

वेब की सुविधाएं: Invoker कमांड पॉपओवर <dialog>

Invoker Commands API का इस्तेमाल करके, एचटीएमएल बटन से सीधे <dialog> और [popover] एलिमेंट को टॉगल करें. इससे कस्टम JavaScript इवेंट लिसनर की ज़रूरत नहीं पड़ती.

<button> में commandfor (टारगेट आईडी) और command (कार्रवाई) एट्रिब्यूट लागू करने पर, ब्राउज़र अपने-आप ओपन/क्लोज़ स्टेटस में बदलाव, फ़ोकस मैनेजमेंट, और सुलभता बाइंडिंग (जैसे कि aria-expanded) को मैनेज करता है. हमारा सुझाव है कि आप इस डिक्लेरेटिव तरीके का इस्तेमाल करें, क्योंकि इससे छोटे-मोटे बदलाव वाले कोड हट जाते हैं. साथ ही, यह पक्का होता है कि एचटीएमएल पार्सिंग के तुरंत बाद इंटरैक्शन काम करें. इसके अलावा, यह एक बेहतर और नेटिव तौर पर सुलभ उपयोगकर्ता अनुभव की गारंटी देता है.

डिक्लेरेटिव पॉपओवर लागू करना

पॉपओवर को एक ही बटन का इस्तेमाल करके, टॉगल करके खोला और बंद किया जा सकता है.

<!-- MANDATORY: The commandfor attribute links the invoker to the ID of the target element so the browser knows what to control. -->
<!-- MANDATORY: The command attribute specifies the action to perform. Use 'toggle-popover' to handle both open and close states automatically. -->
<button commandfor="my-popover" command="toggle-popover">
  Toggle Popover
</button>

<!-- MANDATORY: The target element must have the popover attribute to be controlled as a popover. -->
<div id="my-popover" popover>
  <p>Popover content goes here.</p>
</div>

अगर आपको अलग-अलग बटन से खोलने और बंद करने की सुविधा को कंट्रोल करना है, तो show-popover और hide-popover निर्देशों का इस्तेमाल करें.

<!-- MANDATORY: Use 'show-popover' to explicitly open the popover. It will not close the popover if clicked again. -->
<button commandfor="my-explicit-popover" command="show-popover">
  Show Popover
</button>

<div id="my-explicit-popover" popover="manual">
  <p>This popover is explicitly opened and closed by separate buttons.</p>

  <!-- MANDATORY: Use 'hide-popover' to explicitly close the targeted popover. -->
  <button commandfor="my-explicit-popover" command="hide-popover">
    Hide Popover
  </button>
</div>

डिक्लेरेटिव मॉडल डायलॉग लागू करना

पॉपओवर के उलट, मॉडल डायलॉग आम तौर पर खोलने और बंद करने के लिए अलग-अलग बटन इस्तेमाल करते हैं. show-modal कमांड का इस्तेमाल खास तौर पर तब करें, जब आपको किसी डायलॉग को मॉडल के तौर पर खोलना हो.

<!-- MANDATORY: Use command="show-modal" to trigger the dialog as a modal, trapping focus and preventing interaction with the rest of the page. -->
<!-- MANDATORY: The commandfor attribute connects this button to the dialog ID. -->
<button commandfor="confirm-dialog" command="show-modal">
  Open Confirmation
</button>

<dialog id="confirm-dialog">
  <p>Are you sure you want to proceed?</p>

  <!-- MANDATORY: Use command="close" to dismiss the dialog safely. -->
  <button commandfor="confirm-dialog" command="close">
    Cancel
  </button>
</dialog>

फ़ॉलबैक रणनीतियां

इनवोकर कमांड और पॉपओवर की सुविधा, फ़िलहाल सभी ब्राउज़र पर काम नहीं करती. इसलिए, आपको पुराने ब्राउज़र के लिए फ़ॉलबैक के तौर पर पॉलीफ़िल का इस्तेमाल करना होगा.

इनवोकर कमांड को पॉलीफ़िल करना

ज़रूरी: HTMLButtonElement प्रोटोटाइप पर commandForElement प्रॉपर्टी की जांच करके, सुविधा की पहचान करने की सुविधा चालू करें. विंडो या दस्तावेज़ ऑब्जेक्ट की जांच न करें. आपको polyfill को डाइनैमिक तरीके से सिर्फ़ तब इंपोर्ट करना चाहिए, जब नेटिव सुविधा मौजूद न हो. पॉलीफ़िल को बिना किसी शर्त के लोड न करें. ज़रूरी: टारगेट एलिमेंट पर सीधे 'कमांड' इवेंट को सुनें, क्योंकि नेटिव 'कमांड' इवेंट बबल नहीं होता.

पहला विकल्प: बंडलर का इस्तेमाल करना npm (npm install invokers-polyfill) की मदद से polyfill इंस्टॉल करें. यह तरीका उन प्रोजेक्ट के लिए है जिनमें बंडलर (जैसे, Vite या Webpack) या इंपोर्ट मैप का इस्तेमाल किया जाता है. अन्य सभी सेटअप के लिए, नीचे दिए गए सीडीएन विकल्प का इस्तेमाल करें.

// MANDATORY: Feature detect 'commandForElement' on HTMLButtonElement.prototype.
// Conditionally load the invokers-polyfill only in browsers lacking native support.
if (!('commandForElement' in HTMLButtonElement.prototype)) {
  import('invokers-polyfill');
}

दूसरा विकल्प: सीडीएन का इस्तेमाल करना जिन प्रोजेक्ट में बंडलर नहीं है उनके लिए, <script type="module"> में सीधे सीडीएन से पॉलीफ़िल को डाइनैमिक तौर पर इंपोर्ट करें.

<script type="module">
  // MANDATORY: Feature detect 'commandForElement' on HTMLButtonElement.prototype.
  // Conditionally load the invokers-polyfill from a CDN only in browsers lacking native support.
  if (!('commandForElement' in HTMLButtonElement.prototype)) {
    import('https://esm.run/invokers-polyfill');
  }
</script>

Invokers Polyfill की सीमाएं ज़रूरी जानकारी: यह polyfill, कमांड बटन की ARIA स्थितियों (जैसे, aria-expanded) को नेटिव ब्राउज़र की तरह मैनेज नहीं करता. हमारा सुझाव है कि आप इन स्थितियों को खुद मैनेज करें, ताकि आपकी साइट को पूरी तरह से ऐक्सेस किया जा सके.

अगर Invoker Commands API काम नहीं करता है, तो command इवेंट ट्रिगर नहीं होगा. सभी मॉडर्न ब्राउज़र पर पूरी तरह से काम करने के लिए, हमारा सुझाव है कि आप npm install या सीडीएन के ज़रिए, https://github.com/keithamus/invokers-polyfill से invokers-polyfill का इस्तेमाल करें.

यह पॉलीफ़िल, कस्टम ऐक्शन (-- से शुरू होने वाले) के साथ पूरी तरह काम करता है. साथ ही, नेटिव एपीआई की तरह ही command इवेंट को डिस्पैच करता है.

डाइनैमिक इंपोर्ट (परफ़ॉर्मेंस ऑप्टिमाइज़ेशन)

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

ध्यान दें: यह पॉलीफ़िल, कस्टम कमांड के लिए एआरआईए स्टेटस (जैसे, aria-pressed या aria-expanded) को मैनेज नहीं करता. आपको अपने इवेंट लिसनर में इन स्थितियों को मैन्युअल तरीके से सिंक करना होगा, ताकि यह पक्का किया जा सके कि आपकी साइट को ऐक्सेस किया जा सकता है.

// 1. Conditionally load the polyfill
const hasNativeSupport = 'commandForElement' in HTMLButtonElement.prototype;

if (!hasNativeSupport) {
  // Wrap in an async IIFE to avoid top-level await issues in older browsers
  (async () => {
    try {
      await import('https://esm.run/invokers-polyfill');
    } catch (err) {
      console.error('Error loading fallback:', err);
    }
  })();
}

// 2. Manually manage ARIA states in your listener
document.getElementById('action-target').addEventListener('command', (event) => {
  const command = event.command;
  const target = event.target;
  const source = event.source; // The button that triggered the command

  if (command === '--spin') {
    const isSpun = target.classList.toggle('is-spun');
    
    // Polyfill tip: Manually update ARIA to match the new state
    source?.setAttribute('aria-pressed', isSpun);
  }
});

मैन्युअल फ़ॉलबैक (पारंपरिक पैटर्न)

अगर आपको पॉलीफ़िल का इस्तेमाल नहीं करना है, तो इवेंट डिस्पैच करने के लिए इवेंट डेलिगेशन और कार्रवाइयों को मैनेज करने के लिए कमांड रजिस्ट्री का इस्तेमाल किया जा सकता है. यह JavaScript डेवलपमेंट का एक सामान्य आर्किटेक्चरल पैटर्न है. यह अब भी काफ़ी असरदार है और इसे आसानी से बढ़ाया जा सकता है.

// 1. **Optional:** Define a registry of requested actions for cleaner logic
const commandRegistry = {
  '--spin': (target) => target.classList.toggle('is-spun'),
  '--grow': (target) => target.classList.toggle('is-grown'),
  '--reset': (target) => target.classList.remove('is-spun', 'is-grown'),
};

// 2. If CommandEvent doesn't exist, we assume no native support and provide the fallback
if (!globalThis.CommandEvent) {
  globalThis.CommandEvent = class CommandEvent extends Event {
    constructor(type, { source, command, ...options } = {}) {
      super(type, options);
      this.source = source;
      this.command = command;
    }
  }
}

// 3. The fallback: Dispatch events manually if native support is missing
  document.addEventListener('click', (event) => {
    const button = event.composedPath().find((el) => el.matches?.("button[commandfor]"));
    if (!button) return;

    const target = document.getElementById(button.getAttribute('commandfor'));
    const command = button.getAttribute('command');

    if (target && command) {
      target.dispatchEvent(new CommandEvent('command', { 
        command, 
        source: button,
      }));
    }
  });

// 4. **Mandatory:** Register the unified listener directly on the target element
document.getElementById('action-target').addEventListener('command', (event) => {
  const command = event.command;
  const target = event.target;
  const action = commandRegistry[command];

  if (action) {
    action(target);
  }
 });

पॉपओवर के लिए फ़ॉलबैक और ब्राउज़र की सुविधा

Popover API ज़्यादातर प्रोग्रेसिव एन्हांसमेंट है. हालांकि, इसके मुख्य व्यवहार — टॉप-लेयर प्रमोशन, लाइट-डिसमिस, और popovertarget को शुरू करना — सिर्फ़ सीएसएस के बराबर नहीं हैं. पुराने ब्राउज़र के लिए, polyfill की ज़रूरत होती है. अगर आपको polyfill का इस्तेमाल नहीं करना है, तो मैन्युअल फ़ॉलबैक की ज़रूरत होती है.

पॉलीफ़िल: पुराने ब्राउज़र में popover एट्रिब्यूट का इस्तेमाल करने के लिए, शर्त के मुताबिक @oddbird/popover-polyfill लोड करें. ज़रूरी: HTMLElement.prototype पर popover प्रॉपर्टी की जांच करके, सुविधा का पता लगाएं. साथ ही, पॉलीफ़िल को सिर्फ़ तब लोड करें, जब नेटिव सपोर्ट मौजूद न हो. इसे बिना किसी शर्त के लोड न करें.

बंडलर या इंपोर्ट मैप के साथ:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

बंडलर के बिना, <script type="module"> में सीडीएन से इंपोर्ट करें:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

स्टाइलिंग से जुड़ी चेतावनी: polyfill, असल :popover-open स्यूडो-क्लास को तय नहीं कर सकता. इसलिए, यह इसके बजाय .\:popover-open क्लास लागू करता है. ज़रूरी: दोनों को :is() या :where() के साथ जोड़ें. ऐसा न करने पर, जिन ब्राउज़र में :popover-open नहीं है वे पूरे नियम को खारिज कर देते हैं:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

इसके अलावा, बिना polyfill के लेगसी फ़ॉलबैक के लिए, position: fixed का इस्तेमाल करें और getBoundingClientRect() की मदद से मैन्युअल तरीके से निर्देशांकों का हिसाब लगाएं. इसके अलावा, अगर इस्तेमाल के उदाहरण के लिए यह स्वीकार्य है, तो inset: auto के साथ डिफ़ॉल्ट पोज़िशनिंग का इस्तेमाल करें.