पेश हैं command और commandfor

पब्लिश किया गया: 7 मार्च, 2025

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

Chrome 135 में, command और commandfor एट्रिब्यूट के साथ, एलान करने वाले व्यवहार की जानकारी देने के लिए नई सुविधाएं जोड़ी गई हैं. इनसे popovertargetaction और popovertarget एट्रिब्यूट को बेहतर बनाया गया है और उन्हें बदला गया है. इन नए एट्रिब्यूट को बटन में जोड़ा जा सकता है. इससे ब्राउज़र को आसानी और सुलभता से जुड़ी कुछ मुख्य समस्याओं को हल करने में मदद मिलती है. साथ ही, इनकी मदद से ब्राउज़र में पहले से मौजूद सामान्य फ़ंक्शन भी उपलब्ध कराए जा सकते हैं.

पारंपरिक पैटर्न

फ़्रेमवर्क के बिना बटन के व्यवहार को बनाने पर, प्रोडक्शन कोड के बदलने के साथ-साथ कुछ दिलचस्प चुनौतियां आ सकती हैं. एचटीएमएल, बटन के लिए onclick हैंडलर उपलब्ध कराता है. हालांकि, कॉन्टेंट की सुरक्षा से जुड़ी नीति (सीएसपी) के नियमों की वजह से, आम तौर पर इनका इस्तेमाल डेमो या ट्यूटोरियल के अलावा किसी और जगह पर नहीं किया जा सकता. ये इवेंट, बटन एलिमेंट पर डिस्पैच किए जाते हैं. हालांकि, आम तौर पर बटन को पेज पर अन्य एलिमेंट को कंट्रोल करने के लिए रखा जाता है. इसके लिए, एक साथ दो एलिमेंट को कंट्रोल करने के लिए कोड की ज़रूरत होती है. आपको यह भी पक्का करना होगा कि सहायक टेक्नोलॉजी का इस्तेमाल करने वाले लोग, इस इंटरैक्शन को ऐक्सेस कर पाएं. इससे अक्सर कोड ऐसा दिखता है:

<div class="menu-wrapper">
  <button class="menu-opener" aria-expanded="false">
    Open Menu
  </button>
  <div popover class="menu-content">
    <!-- ... -->
  </div>
</div>
<script type="module">
document.addEventListener('click', e => {  
  const button = e.target;
  if (button.matches('.menu-opener')) {
    const menu = button
      .closest('.menu-wrapper')
      .querySelector('.menu-content');
    if (menu) {
      button.setAttribute('aria-expanded', 'true');
      menu.showPopover();
      menu.addEventListener('toggle', e => {
        // reset back to aria-expanded=false on close
        if (e.newState == 'closed') {
          button.setAttribute('aria-expanded', 'false');
        }
      }, {once: true})
    }
  }
});
</script>

यह तरीका थोड़ा मुश्किल हो सकता है. फ़्रेमवर्क का मकसद, काम करने के तरीके को बेहतर बनाना है. React जैसे फ़्रेमवर्क के सामान्य पैटर्न में, क्लिक को किसी स्थिति में बदलाव करने के लिए मैप किया जा सकता है:

function MyMenu({ children }) {
  const [isOpen, setIsOpen] = useState(false);
  const open = useCallback(() => setIsOpen(true), []);
  const handleToggle = useCallback((e) => {
      // popovers have light dismiss which influences our state
     setIsOpen(e.newState === 'open')
  }, []);
  const popoverRef = useRef(null);
  useEffect(() => {
    if (popoverRef.current) {
      if (isOpen) {
        popoverRef.current.showPopover();
      } else {
        popoverRef.current.hidePopover();
      }
    }
  }, [popoverRef, isOpen]);
  return (
    <>
      <button onClick={open} aria-expanded={isOpen}>
        Open Menu
      </button>
      <div popover onToggle={handleToggle} ref={popoverRef}>
        {children}
      </div>
    </>
  );
}

कई अन्य फ़्रेमवर्क भी इसी तरह के एर्गोनॉमिक्स उपलब्ध कराने का लक्ष्य रखते हैं. उदाहरण के लिए, इसे AlpineJS में इस तरह लिखा जा सकता है:

<div x-data="{open: false}">
  <button @click="open = !open; $refs.popover.showPopover()" :aria-expanded="open">
    Open Menu
  </button>
  <div popover x-ref="popover" @toggle="open = $event.newState === 'open'">
    <!-- ... -->
  </div>
</div>

Svelte में इसे लिखते समय, यह कुछ ऐसा दिख सकता है:

<script>
  let popover;
  let open = false;
  function togglePopover() {
    open ? popover.hidePopover() : popover.showPopover();
    open = !open;
  }
</script>
<button on:click={togglePopover} aria-expanded={open}>
  Open Menu
</button>
<div bind:this={popover} popover>
  <!-- ... -->
</div>

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

import {MenuTrigger, MenuContent} from 'my-design-system';
function MyMenu({children}) {
  return (
    <MenuTrigger>
      <button>Open Menu</button>
    </MenuTrigger>
    <MenuContent>{children}</MenuContent>
  );
}

command और commandfor पैटर्न

command और commandfor एट्रिब्यूट की मदद से, बटन अब अन्य एलिमेंट पर कार्रवाई कर सकते हैं. इससे, फ़्रेमवर्क को आसानी से इस्तेमाल किया जा सकता है. commandfor बटन, for एट्रिब्यूट की तरह ही एक आईडी लेता है. वहीं, command पहले से मौजूद वैल्यू स्वीकार करता है. इससे, इसे आसानी से इस्तेमाल किया जा सकता है.

उदाहरण: command और commandfor के साथ मेन्यू बटन खोलना

यहां दिया गया एचटीएमएल, बटन और मेन्यू के बीच संबंध सेट अप करता है. इससे ब्राउज़र आपके लिए लॉजिक और ऐक्सेस करने की सुविधा को मैनेज कर पाता है. aria-expanded को मैनेज करने या कोई और JavaScript जोड़ने की ज़रूरत नहीं है.

<button commandfor="my-menu" command="show-popover">
Open Menu
</button>
<div popover id="my-menu">
  <!-- ... -->
</div>

command और commandfor की तुलना popovertargetaction और popovertarget से करना

अगर आपने पहले popover का इस्तेमाल किया है, तो आपको popovertarget और popovertargetaction एट्रिब्यूट के बारे में पता होगा. ये commandfor और command की तरह ही काम करते हैं. हालांकि, ये सिर्फ़ पॉपओवर के लिए होते हैं. command और commandfor एट्रिब्यूट, इन पुराने एट्रिब्यूट की जगह ले लेते हैं. नए एट्रिब्यूट, पुराने एट्रिब्यूट की तरह ही काम करते हैं. साथ ही, इनमें नई सुविधाएं भी जोड़ी गई हैं.

पहले से मौजूद निर्देश

command एट्रिब्यूट में, बिल्ट-इन व्यवहार का एक सेट होता है. यह इंटरैक्टिव एलिमेंट के लिए, अलग-अलग एपीआई से मैप होता है:

  • show-popover: el.showPopover() पर मैप करता है.
  • hide-popover: el.hidePopover() पर मैप करता है.
  • toggle-popover: el.togglePopover() पर मैप करता है.
  • show-modal: dialogEl.showModal() पर मैप करता है.
  • close: dialogEl.close() पर मैप करता है.

ये निर्देश, JavaScript के निर्देशों के साथ मैप होते हैं. साथ ही, इनसे ऐक्सेस को आसान बनाने (जैसे, aria-details और aria-expanded के बराबर संबंधों की जानकारी देना), फ़ोकस मैनेजमेंट वगैरह में भी मदद मिलती है.

उदाहरण: command और commandfor वाला पुष्टि करने वाला डायलॉग

<button commandfor="confirm-dialog" command="show-modal">
  Delete Record
</button>
<dialog id="confirm-dialog">
  <header>
    <h1>Delete Record?</h1>
    <button commandfor="confirm-dialog" command="close" aria-label="Close" value="close">
      <img role="none" src="/close-icon.svg">
    </button>
  </header>
  <p>Are you sure? This action cannot be undone</p>
  <footer>
    <button commandfor="confirm-dialog" command="close" value="cancel">
      Cancel
    </button>
    <button commandfor="confirm-dialog" command="close" value="delete">
      Delete
    </button>
  </footer>
</dialog>

रिकॉर्ड मिटाएं बटन पर क्लिक करने से, डायलॉग बॉक्स मोडल के तौर पर खुलेगा. वहीं, बंद करें, रद्द करें या मिटाएं बटन पर क्लिक करने से डायलॉग बॉक्स बंद हो जाएगा. साथ ही, डायलॉग बॉक्स पर "close" इवेंट भी डिस्पैच किया जाएगा. इसमें बटन की वैल्यू से मैच करने वाली returnValue प्रॉपर्टी होगी. इससे, डायलॉग बॉक्स में एक इवेंट Listener के अलावा, JavaScript की ज़रूरत कम हो जाती है, ताकि यह तय किया जा सके कि आगे क्या करना है:

dialog.addEventListener("close", (event) => {
  if (event.target.returnValue == "cancel") {
    console.log("cancel was clicked");
  } else if (event.target.returnValue == "close") {
    console.log("close was clicked");
  } else if (event.target.returnValue == "delete") {
    console.log("delete was clicked");
  }
});

पसंद के मुताबिक निर्देश

पहले से मौजूद निर्देशों के अलावा, -- प्रीफ़िक्स का इस्तेमाल करके कस्टम निर्देश तय किए जा सकते हैं. कस्टम निर्देश, टारगेट एलिमेंट पर "command" इवेंट भेजेंगे (बिल्ट-इन निर्देशों की तरह), लेकिन बिल्ट-इन वैल्यू की तरह कोई और लॉजिक कभी नहीं करेंगे. इससे, ऐसे कॉम्पोनेंट बनाने में मदद मिलती है जो बटन पर अलग-अलग तरह से प्रतिक्रिया दे सकते हैं. इसके लिए, उन्हें रैपर कॉम्पोनेंट देने, टारगेट एलिमेंट के लिए डीओएम को ट्रैवर्स करने या बटन क्लिक को स्टेटस में बदलावों से मैप करने की ज़रूरत नहीं होती. इससे, अपने कॉम्पोनेंट के लिए एचटीएमएल में एपीआई दिया जा सकता है:

<button commandfor="the-image" command="--rotate-landscape">
 Landscape
</button>
<button commandfor="the-image" command="--rotate-portrait">
 Portrait
</button>

<img id="the-image" src="photo.jpg">

<script type="module">
  const image = document.getElementById("the-image");
  image.addEventListener("command", (event) => {
   if ( event.command == "--rotate-landscape" ) {
    image.style.rotate = "-90deg"
   } else if ( event.command == "--rotate-portrait" ) {
    image.style.rotate = "0deg"
   }
  });
</script>

ShadowDOM में निर्देश

commandfor एट्रिब्यूट में आईडी डाला जाता है. इसलिए, शैडो डीओएम को क्रॉस करने पर पाबंदियां लागू होती हैं. इन मामलों में, .commandForElement प्रॉपर्टी को सेट करने के लिए JavaScript API का इस्तेमाल किया जा सकता है. यह प्रॉपर्टी, सभी शैडो रूट में कोई भी एलिमेंट सेट कर सकती है:

<my-element>
  <template shadowrootmode=open>
    <button command="show-popover">Show popover</button>
    <slot></slot>
  </template>
  <div popover><!-- ... --></div>
</my-element>
<script>
customElements.define("my-element", class extends HTMLElement {
  connectedCallback() {
    const popover = this.querySelector('[popover]');
    // The commandForElement can set cross-shadow root elements.
    this.shadowRoot.querySelector('button').commandForElement = popover;
  }
});
</script>

आने वाले समय में, शैडो सीमाओं के बीच रेफ़रंस शेयर करने के लिए, एलान करने वाला तरीका उपलब्ध कराया जा सकता है. जैसे, रेफ़रंस टारगेट का प्रस्ताव.

आगे क्या करना है?

हम वेबसाइटों पर इस्तेमाल होने वाली सामान्य सुविधाओं को कवर करने के लिए, पहले से मौजूद नए निर्देशों की संभावनाओं को एक्सप्लोर करते रहेंगे. सुझाए गए आइडिया, ओपन यूआई के लिए प्रस्ताव में शामिल हैं. पहले से इस्तेमाल किए जा रहे कुछ आइडिया:

  • <details> एलिमेंट खोलना और बंद करना.
  • <input> और <select> एलिमेंट के लिए "show-picker" कमांड, जो showPicker() पर मैप किया गया है.
  • <video> और <audio> एलिमेंट के लिए, वीडियो चलाने के निर्देश.
  • एलिमेंट से टेक्स्ट कॉन्टेंट कॉपी करना.

हम कम्यूनिटी के सुझावों का स्वागत करते हैं. अगर आपके पास सुझाव हैं, तो ओपन यूज़र इंटरफ़ेस (यूआई) की समस्या ट्रैकर पर जाकर, बेझिझक समस्या दर्ज करें.

ज़्यादा जानें

command और commandfor के बारे में ज़्यादा जानकारी पाने के लिए, स्पेसिफ़िकेशन और MDN पर जाएं.