HTML <dialog> öğesi, sayfanın üzerinde açılan bir iletişim kutusu ya da kapatılabilir bir uyarı, inceleme paneli veya alt pencere gibi etkileşimli bir bileşen tanımlar. Kısa cevap şudur: modal bir pencere için <dialog> öğesini yazar, bir düğmeye commandfor ve command="show-modal" verirsiniz ya da JavaScript ile showModal() yöntemini çağırırsınız. Pencereyi kapatmak için içindeki bir formda method="dialog" kullanmak, command="close" vermek veya close() çağırmak yeterlidir.

Bu yazıda modal ile modal olmayan pencerenin farkını, open ve closedby özniteliklerini, returnValue ile kullanıcının hangi düğmeye bastığını öğrenmeyi, request-close ile kapanmayı durdurmayı ve odak yönetimiyle erişilebilirlik kurallarını MDN belgelerine dayanarak anlatıyoruz. Açılır içerik için popover özniteliği, düğmenin diğer öznitelikleri için button etiketi yazılarına bakabilirsiniz.

open özniteliği: sayfa yüklenirken açık pencere

open mantıksal (Boolean) bir özniteliktir ve pencerenin etkin olduğunu gösterir. Öznitelik yoksa pencere kullanıcıya görünmez. Sayfa açıldığında hazır görünen, yalnızca HTML ile yazılmış modal olmayan bir kutu için şu yapı yeterlidir:

html
<dialog open>
  <p>Siparişiniz alındı. Teşekkür ederiz!</p>
  <form method="dialog">
    <button>Tamam</button>
  </form>
</dialog>

Burada "Tamam" düğmesine basıldığında form dialog yöntemiyle gönderilir ve pencere kapanır; bunun için JavaScript gerekmez. Yalnız bir sınırlama vardır: kapanan pencereyi yeniden açacak bir HTML yolu bu örnekte yoktur. MDN, open özniteliğini ekleyip kaldırarak pencereyi açıp kapatmanın mümkün olduğunu ama önerilmediğini belirtir. Modal olmayan pencereler için tercih edilen yol show() yöntemidir.

JavaScript olmadan açmak: commandfor ve command

Invoker Commands API adı verilen yapı, <button> öğesine iki öznitelik ekler. commandfor denetlenecek öğenin id değerini alır; command ise düğmeye basıldığında hangi komutun gönderileceğini söyler. Pencereler için kullanılabilen komutlar show-modal, close ve request-close değerleridir. Tarayıcı desteği için MDN uyumluluk tablosuna bakın.

html
<button commandfor="silme-penceresi" command="show-modal">Kaydı sil</button>

<dialog id="silme-penceresi">
  <h2>Kayıt silinsin mi?</h2>
  <p>Bu işlem geri alınamaz.</p>
  <button commandfor="silme-penceresi" command="close" value="vazgec">Vazgeç</button>
  <button commandfor="silme-penceresi" command="close" value="sil">Sil</button>
</dialog>

show-modal komutu pencereyi modal açar; pencere zaten modal açıksa hiçbir şey yapmaz. close komutu pencereyi kapatır; zaten kapalıysa yine bir şey olmaz. Düğmede value özniteliği varsa bu değer pencerenin returnValue özelliğine yazılır. Yukarıdaki örnekte kullanıcı "Sil" düğmesine basarsa returnValue değeri sil olur; sayfadaki bir betik pencerenin close olayını dinleyerek bu değere göre işlem yapabilir.

commandfor, MDN tanımıyla popovertarget özniteliğinin daha genel bir sürümüdür. Komut değeri iki tire ile başlayan özel bir ad da olabilir (örneğin --yenile); bu durumda tarayıcı denetlenen öğeye bir CommandEvent gönderir ve ne olacağını sizin betiğiniz belirler.

JavaScript ile açma ve kapatma

Komut öznitelikleri her senaryoyu karşılamayabilir; örneğin pencereyi bir ağ isteği bittikten sonra açmak isteyebilirsiniz. Bu durumda HTMLDialogElement arayüzünün yöntemleri kullanılır: showModal() modal açar, show() modal olmayan biçimde açar, close() kapatır, requestClose() ise kapatma isteği gönderir. close() yöntemine verdiğiniz metin returnValue olarak saklanır. JavaScript tarafının ayrıntıları için JavaScript öğrenme rehberine bakabilirsiniz.

javascript
const pencere = document.getElementById("silme-penceresi");

document.querySelector("#ac").addEventListener("click", () => {
  pencere.showModal();
});

pencere.addEventListener("close", () => {
  console.log(pencere.returnValue); // "sil" veya "vazgec"
});

Buradaki konsol yorumu, örnekteki düğme değerlerinden hangisinin returnValue olacağını gösterir; Esc ile kapatılan pencerede değer bir düğmeden gelmez; MDN örneği bu durumu ayırt etmek için cancel olayında returnValue değerini kendisi atar. MDN, returnValue değerinin varsayılan olarak boş metin ya da formu gönderen düğmenin değeri olduğunu söyler.

Pencereyi kapatmanın yolları ve closedby

Pencereyi kapatmanın yolları ve closedby: Pencere içindeki bir formu method="dialog" ile göndermek., "Light dismiss" açıksa pencerenin dışına tıklamak ya da dokunmak., Etkin...

Her pencerede mutlaka bir kapatma yolu bulunmalıdır ve bu yol fiziksel klavyesi olmayan cihazlarda da çalışmalıdır. MDN kapatma yollarını şöyle sıralar:

  • Pencere içindeki bir formu method="dialog" ile göndermek.
  • "Light dismiss" açıksa pencerenin dışına tıklamak ya da dokunmak.
  • Etkin olduğu pencerelerde Esc tuşuna basmak; mobilde bunun yerine geri ya da kapat hareketi olabilir.
  • close() yöntemini çağırmak veya command="close" taşıyan bir düğmeye basmak.

closedby özniteliği bu yollardan hangilerinin geçerli olacağını belirler. any değeri üç yolu da açar: dışarı tıklama, platforma özgü kapatma (masaüstünde Esc) ve geliştiricinin sağladığı düğme. closerequest dışarı tıklamayı kapatır ama Esc ve düğmeyi bırakır. none yalnızca geliştiricinin sağladığı yolu, yani düğmeyi ya da formu kabul eder. Öznitelik yoksa veya geçersiz bir değer taşıyorsa showModal() ile açılan pencere closerequest, diğerleri none gibi davranır. Tarayıcı desteği için MDN uyumluluk tablosuna bakın.

html
<dialog id="bilgi" closedby="any">
  <p>Dışarı tıklayarak, Esc ile veya düğmeyle kapatabilirsiniz.</p>
  <button commandfor="bilgi" command="close">Kapat</button>
</dialog>

<dialog id="onay" closedby="none">
  <p>Bu pencere yalnızca aşağıdaki düğmeyle kapanır.</p>
  <button commandfor="onay" command="close">Anladım</button>
</dialog>

<button commandfor="bilgi" command="show-modal">Bilgi penceresi</button>
<button commandfor="onay" command="show-modal">Onay penceresi</button>

closedby="none" seçeneğini, kullanıcının bir karar vermeden geçmemesi gereken durumlarla sınırlı tutun. Erişilebilirlik bölümünde göreceğiniz gibi klavye kullanıcıları modal pencerenin Esc ile kapanmasını bekler.

form method="dialog" ve returnValue

form öğesinin method özniteliği get ve post dışında dialog değerini de alır. Form bir pencerenin içindeyse bu yöntem pencereyi kapatır ve bir submit olayı tetikler; ancak veriyi sunucuya göndermez ve formu temizlemez. Form denetimlerinin durumu korunur, returnValue ise basılan düğmenin değeri olur. method="dialog" ayarlıyken formun action özniteliği yok sayılır.

Aynı etkiyi tek bir düğmeye vermek de mümkündür: gönderme düğmesindeki formmethod="dialog" özniteliği formun yöntemini yalnızca o düğme için geçersiz kılar. Bu sayede bir "Vazgeç" düğmesi pencereyi kapatırken "Kaydet" düğmesi formu normal biçimde sunucuya gönderebilir.

html
<dialog id="adres">
  <form method="post" action="/adres-kaydet">
    <p>
      <label>Şehir
        <input type="text" name="sehir" required>
      </label>
    </p>
    <button type="submit">Kaydet</button>
    <button type="submit" value="vazgec" formmethod="dialog" formnovalidate>Vazgeç</button>
  </form>
</dialog>

Bu örnekte iki önemli ayrıntı var. Birincisi, form içinde zorunlu bir alan varsa tarayıcı, değer girilmeden formun gönderilmesine izin vermediği için pencereyi de kapatmaz. MDN bunu aşmak için iki yol gösterir: kapatma düğmesine formnovalidate eklemek ya da düğmeye basıldığında close() çağırmak. İkincisi, action tanımlanmamış bir formu varsayılan GET yöntemiyle göndermek sayfanın yeniden yüklenmesine yol açar; pencere düğmelerinde yöntemi bilinçli seçmek bu yüzden önemlidir. Doğrulama kuralları için form doğrulama yazısına bakın.

request-close ve cancel olayı: kapanmayı durdurmak

close komutu pencereyi doğrudan kapatır. request-close komutu ve ona karşılık gelen requestClose() yöntemi ise önce pencereye bir cancel olayı, ardından close olayı gönderir. Fark şudur: cancel olayında preventDefault() çağrılırsa pencere kapanmaz. Esc tuşuyla yapılan kapatma da önce cancel olayını tetikler.

html
<dialog id="taslak">
  <p>Kaydedilmemiş değişiklikleriniz var.</p>
  <button commandfor="taslak" command="request-close" value="kapat">Kapat</button>
</dialog>
javascript
const taslak = document.getElementById("taslak");
let kaydedildi = false;

taslak.addEventListener("cancel", (olay) => {
  if (!olay.cancelable) return;
  if (!kaydedildi) olay.preventDefault(); // pencere açık kalır
});

MDN örneği önce cancelable değerini kontrol eder; olay iptal edilebilir değilse dinleyici hiçbir şey yapmaz. Bu kontrolü atlamamak iyi bir alışkanlıktır. Düğmedeki value, request-close ile birlikte kullanıldığında da returnValue özelliğine aktarılır.

dialog öğesini popover olarak kullanmak

Bir <dialog> öğesine popover özniteliği eklenirse pencere bir popover olur ve popovertarget ile popovertargetaction taşıyan düğmelerle açılıp kapatılabilir. Bu durumda pencere modal değildir; popover değeri verilmediği için auto kabul edilir ve pencere dışına tıklamak ya da Esc tuşu onu kapatır. Ayrıntılar popover yazısında anlatılıyor.

html
<button popovertarget="ipucu-penceresi">Kısayolları göster</button>

<dialog id="ipucu-penceresi" popover>
  <p>Arama için / tuşuna basabilirsiniz.</p>
  <button popovertarget="ipucu-penceresi" popovertargetaction="hide">Kapat</button>
</dialog>

Odak, autofocus ve erişilebilirlik

showModal() ile açılan pencerede odak, içerideki ilk odaklanabilir öğeye gider. Kullanıcının hemen etkileşime gireceği öğeye autofocus vermek, ilk odağın nereye düşeceğini açıkça belirler. Daha uygun bir öğe yoksa MDN, kapatma düğmesine ya da kullanıcının pencereyi etkinleştirerek kapatması bekleniyorsa pencerenin kendisine autofocus eklenmesini önerir. İçeriği açılırken dinamik olarak oluşturulan pencerelerde başlangıç odağı için en iyi yer pencerenin kendisi olabilir.

<dialog> öğesine tabindex eklenmemelidir; öğe etkileşimli değildir ve kendisi odak almaz. İçindeki düğmeler ve alanlar odaklanabilir. Birden çok modal pencere açıksa Esc yalnızca en son gösterileni kapatmalıdır ve <dialog> kullanıldığında bu davranışı tarayıcı sağlar. Kapatmanın en sağlam yolu ise içerikte açıkça görünen bir "Kapat", "Vazgeç" ya da "Tamam" düğmesi bulundurmaktır.

Tarayıcılar <dialog> öğesini role="dialog" taşıyan özel bir pencere gibi sunar. showModal() ile açılan pencere örtük olarak aria-modal="true" değerine sahiptir; show(), open özniteliği ya da CSS ile görünür yapılan pencere ise aria-modal="false" olarak sunulur. Öğenin örtük rolü dialog, izin verilen tek ek rolü alertdialog değeridir. ARIA dialog rolü sayfası, pencereye bir erişilebilir ad verilmesini, görünür başlık varsa bunu aria-labelledby ile bağlamayı önerir. Bu konuların genel çerçevesi için erişilebilir ad ve ARIA yazılarına bakın.

html
<dialog id="hesap" aria-labelledby="hesap-baslik">
  <h2 id="hesap-baslik">Bilgileriniz güncellendi</h2>
  <p>Hesap ayarlarından istediğiniz zaman değiştirebilirsiniz.</p>
  <button commandfor="hesap" command="close" autofocus>Tamam</button>
</dialog>

Teknik özet: dialog nereye yazılır?

MDN teknik özetine göre <dialog> akış içeriği (flow content) kategorisindedir ve bir bölümleme köküdür (sectioning root). İçine akış içeriği alır; yani paragraflar, başlıklar, formlar, listeler yazılabilir. Akış içeriği kabul eden her öğenin içinde yer alabilir. Başlangıç ve bitiş etiketlerinin ikisi de zorunludur. Kategorilerin ne anlama geldiğini MDN: içerik kategorileri sayfasında ve iç içe kullanım hataları yazısında bulabilirsiniz.

Sık yapılan hatalar

Sık yapılan hatalar: Pencereyi open özniteliğiyle açıp modal davranış beklemek. open her zaman modal..., <dialog> öğesine tabindex vermek. MDN bunu açıkça yasaklar; odağı...
  • Pencereyi open özniteliğiyle açıp modal davranış beklemek. open her zaman modal olmayan pencere üretir.
  • <dialog> öğesine tabindex vermek. MDN bunu açıkça yasaklar; odağı autofocus ile içerideki öğeye yönlendirin.
  • Kapatma düğmesi koymamak ve yalnızca Esc tuşuna güvenmek. Dokunmatik cihazlarda klavye olmayabilir.
  • Zorunlu alan içeren bir formda "Vazgeç" düğmesine formnovalidate eklemeyi unutmak; pencere kapanmaz.
  • Pencere içindeki düğmelerin varsayılan type değerinin submit olduğunu unutmak. Form içindeki türü belirtilmemiş her düğme formu gönderir.
  • commandfor değerini pencerenin id değeriyle eşleştirmemek. Değer bir seçici değil, düz bir kimlik değeridir; başına # konmaz.

Sık Sorulan Sorular

dialog öğesini JavaScript olmadan modal açabilir miyim?

Evet. commandfor ile pencerenin kimliğini, command="show-modal" ile komutu veren bir <button> yeterlidir. Tarayıcı desteği için MDN uyumluluk tablosuna bakın. Desteklenmeyen ortamlar için showModal() çağıran küçük bir betik yedek olarak kullanılabilir.

Kullanıcının hangi düğmeye bastığını nasıl öğrenirim?

Düğmelere value verin. method="dialog" ile gönderilen formda, command="close" ya da request-close komutunda bu değer pencerenin returnValue özelliğine yazılır. Pencerenin close olayında bu özelliği okuyabilirsiniz.

Pencere açıkken arka plan neden tıklanamıyor?

Pencere showModal() ya da show-modal komutuyla açıldığı için modaldır ve tarayıcı pencere dışındaki her şeyi inert yapar. Arka planla etkileşim istiyorsanız pencereyi show() ile açın.

closedby ile popover arasındaki ilişki nedir?

closedby="any" değerindeki dışarı tıklayarak kapatma, MDN tanımına göre auto durumundaki popoverların "light dismiss" davranışıyla aynıdır. Fark, closedby ayarının modal bir pencerede de çalışmasıdır.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: HTML öğrenme rehberi · popover özniteliği · button etiketi · form etiketi · tabindex ve klavye erişimi · hidden ve inert · erişilebilir ad