Bildirimli İletişim Kutusu ve Popover Kontrolü

Özel JavaScript etkinlik işleyicilere gerek kalmadan <dialog> ve [popover] öğelerinin görünürlüğünü doğrudan HTML düğmelerinden değiştirmek için Invoker Commands API'yi kullanın.

commandfor (hedef kimliği) ve command (işlem) özelliklerini bir <button> öğesine uygulayarak tarayıcı; açma/kapatma durumu değişikliklerini, odak yönetimini ve erişilebilirlik bağlamalarını (ör. aria-expanded) otomatik olarak işler. Bu açıklayıcı yaklaşım, kırılgan ortak metin kodunu ortadan kaldırdığı, etkileşimlerin HTML ayrıştırmasından hemen sonra çalışmasını sağladığı ve sağlam, yerel olarak erişilebilir bir kullanıcı deneyimi sunduğu için önerilir.

Bildirimsel Popover'ları Uygulama

Pop-over'lar tek bir düğme kullanılarak açılıp kapatılabilir.

<!-- 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>

Açma ve kapatma işlemlerini ayrı düğmelerle kontrol etmeniz gerekiyorsa show-popover ve hide-popover komutlarını kullanabilirsiniz.

<!-- 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>

Bildirimli kalıcı iletişim kutularını uygulama

Pop-over'ların aksine, kalıcı iletişim kutuları genellikle açma ve kapatma için ayrı düğmeler kullanır. show-modal komutunu özellikle bir iletişim kutusunu kalıcı olarak açmanız gerektiğinde kullanın.

<!-- 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>

Yedek stratejiler

Çağırıcı komutları ve popover'lar henüz her yerde desteklenmediğinden, eski tarayıcılar için yedek olarak polyfill'leri KULLANMANIZ GEREKİR.

Çağırıcı komutlarını polyfill ile doldurma

ZORUNLU: HTMLButtonElement prototipinde commandForElement özelliğini kontrol ederek özellik algılama desteğini etkinleştirin. Pencere veya belge nesnesini KONTROL ETMEYİN. Polyfill'i yalnızca yerel özellik eksik olduğunda dinamik olarak içe aktarmanız GEREKİR. Polyfill'i koşulsuz olarak YÜKLEMEYİN. Zorunlu: Yerel "command" etkinliği kabarcıklanmadığından "command" etkinliğini doğrudan hedef öğede dinleyin.

1. seçenek: Paketleyici kullanma Polyfill'i npm (npm install invokers-polyfill) üzerinden yükleyin. Bu yaklaşım, paketleyici (ör. Vite veya Webpack) ya da içe aktarma eşlemeleri kullanan projeler içindir. Diğer tüm kurulumlar için aşağıdaki CDN seçeneğini kullanın.

// 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');
}

2. seçenek: CDN kullanma Paketleyici içermeyen projelerde polyfill'i doğrudan bir CDN'den <script type="module"> içine dinamik olarak içe aktarın.

<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 Sınırlamaları ZORUNLU: Bu polyfill, komut düğmesinin ARIA durumlarını (ör. aria-expanded) yerel tarayıcılar gibi işlemez. Sitenizin tamamen erişilebilir olmasını sağlamak için bu durumları kendiniz işlemeniz önemle tavsiye edilir.

Invoker Commands API desteklenmiyorsa command etkinliği tetiklenmez. Tüm modern tarayıcılarda tam destek için npm install veya CDN aracılığıyla https://github.com/keithamus/invokers-polyfill adresindeki invokers-polyfill'i kullanmanız önerilir.

Bu polyfill, özel işlemleri (-- ile başlayan) tam olarak destekler ve command etkinliğini tam olarak yerel API gibi gönderir.

Dinamik İçe Aktarma (Performans Optimizasyonu)

En iyi performans için polyfill'i yalnızca tarayıcı API'yi yerel olarak desteklemiyorsa yüklemeniz gerekir. Bu sayede, modern tarayıcıları kullanan kullanıcılar için bant genişliğinden tasarruf edilir ve komut dosyası yürütme süresi kısalır.

NOT: Bu polyfill, özel komutlar için ARIA durumlarını (aria-pressed veya aria-expanded gibi) yönetmez. Sitenizin erişilebilir olmasını sağlamak için etkinlik işleyicinizde bu durumları manuel olarak senkronize etmeniz gerekir.

// 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);
  }
});

Manuel yedek (geleneksel model)

Polyfill kullanmak istemiyorsanız etkinlikleri göndermek için etkinlik yetkilendirme ve işlemleri gerçekleştirmek için komut kayıt defteri kombinasyonunu kullanabilirsiniz. Bu, geleneksel JavaScript geliştirmede yaygın olarak kullanılan ve son derece verimli ve ölçeklenebilir bir mimari modeldir.

// 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 için yedekler ve tarayıcı desteği

Popover API çoğunlukla progresif geliştirme ile ilgilidir ancak tanımlayıcı davranışlarının (üst katman tanıtımı, hafif kapatma ve popovertarget çağrısı) yalnızca CSS'ye dayalı bir eşdeğeri yoktur. Eski tarayıcılar için polyfill veya polyfill kullanmak istemiyorsanız manuel yedek gerekir.

Polyfill: Eski tarayıcılarda popover özelliğini desteklemek için @oddbird/popover-polyfill öğesini koşullu olarak yükleyin. ZORUNLU: HTMLElement.prototype üzerinde popover özelliğini kontrol ederek özelliği tespit edin ve polyfill'i yalnızca yerel destek eksik olduğunda yükleyin. Koşulsuz olarak YÜKLEMEYİN.

Paketleyici veya içe aktarma eşlemesiyle:

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

Paketleyici olmadan, <script type="module"> içindeki bir CDN'den içe aktarın:

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

Stil uyarısı: Polyfill, gerçek :popover-open sözde sınıfını tanımlayamadığı için bunun yerine .\:popover-open sınıfını uygular. ZORUNLU: İkisini :is() veya :where() ile birleştirin. Aksi takdirde, :popover-open içermeyen tarayıcılar kuralın tamamını siler:

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

Alternatif olarak, polyfill içermeyen eski bir yedek için position: fixed kullanın ve koordinatları getBoundingClientRect() aracılığıyla manuel olarak hesaplayın veya kullanım alanına uygunsa inset: auto ile varsayılan konumlandırmayı kullanın.