3 poin oleh GN⁺ 2024-12-08 | 1 komentar | Bagikan ke WhatsApp
  • Elemen HTML `` membuat dialog modal dan non-modal native browser; dialog modal menjadikan area lain halaman berada dalam status inert sehingga interaksi diblokir
  • Cara dasar untuk membuka dan menutupnya adalah showModal(), show(), dan close(); kontrol deklaratif juga dimungkinkan dengan ``, Invoker Commands API, dan Popover API
  • Atribut closedby membagi jalur yang dapat digunakan pengguna untuk menutup dialog menjadi any, closerequest, dan none, dengan perilaku default yang berbeda sesuai cara dialog dibuka
  • Dalam aksesibilitas, fokus awal dan tombol tutup eksplisit penting; dialog yang dibuka dengan showModal() secara default menyediakan elemen pertama yang dapat difokuskan serta penutupan dengan tombol Esc
  • Untuk styling, gunakan :modal, :open, dan ::backdrop; untuk animasi diperlukan penanganan transisi diskret seperti display, overlay, @starting-style, dan transition-behavior: allow-discrete

Peran `` dan perilaku modal

  • Elemen HTML `` adalah elemen untuk membuat dialog modal dan non-modal
  • Dialog modal mencegah interaksi dengan elemen UI lain dan menjadikan bagian halaman lainnya berada dalam status inert
  • Dialog non-modal tetap memungkinkan interaksi dengan bagian halaman lainnya meskipun sedang terbuka

Status terbuka dan kebijakan penutupan

  • `` mencakup atribut global, tetapi atribut tabindex tidak boleh digunakan
    • `` sendiri bukan elemen interaktif dan tidak menerima fokus
    • Konten internal dan tombol tutup dapat menerima fokus dan berinteraksi
  • Atribut open menunjukkan bahwa dialog sedang aktif dan dapat berinteraksi
    • Jika atribut open tidak ada, dialog tidak terlihat oleh pengguna
    • Saat menampilkan dialog, penggunaan .show() atau .showModal() lebih disarankan daripada atribut open
    • `` yang dibuka dengan atribut open bersifat non-modal
    • Status buka/tutup dialog non-modal memang dapat diubah dengan toggle atribut open, tetapi tidak disarankan
  • Atribut closedby menentukan dengan cara apa pengguna dapat menutup dialog
    • any: dapat ditutup dengan ketiga cara berikut
      • light dismiss seperti klik atau tap di luar dialog
      • Tindakan khusus platform seperti tombol Esc, tombol kembali di perangkat mobile, atau gesture dismiss
      • Cara yang ditentukan developer seperti tombol yang memanggil HTMLDialogElement.close() atau pengiriman ``
    • closerequest: dapat ditutup dengan tindakan khusus platform dan cara yang ditentukan developer
    • none: hanya dapat ditutup dengan cara yang ditentukan developer
    • Jika tidak ada nilai closedby yang valid, dialog yang dibuka dengan showModal() berperilaku seperti closerequest, sedangkan selain itu berperilaku seperti none

JavaScript dan kontrol deklaratif

  • Anda dapat mengontrol langsung penampilan dan penutupan `` dengan JavaScript
    • showModal(): menampilkan dialog modal
    • show(): menampilkan dialog non-modal
    • close(): menutup dialog
    • Dialog juga dapat ditutup dengan mengirim di dalam menggunakan method="dialog"
    • Dialog modal juga dapat ditutup dengan tombol Esc
  • Invoker Commands API memungkinkan dialog modal dibuka dan ditutup hanya dengan atribut tombol
    • Tetapkan atribut commandfor dan command pada ``
    • Perintah yang dapat digunakan untuk dialog adalah "show-modal", "close", dan "request-close"
Open dialog

  This dialog was opened using an invoker command.

  Close

  • Popover API memungkinkan dialog non-modal dibuka, ditutup, dan di-toggle secara deklaratif
    • Tambahkan atribut popover pada `` untuk menjadikannya popover
    • Tetapkan popovertarget dan popovertargetaction pada tombol atau elemen input
    • Karena dialog popover bersifat non-modal, dialog dapat ditutup dengan mengklik bagian luar
    • Jika nilai popover tidak ditentukan, nilai default "auto" digunakan sehingga light dismiss aktif
    • popover="manual" menonaktifkan light dismiss dan memerlukan cara terpisah seperti tombol tutup
    • Jika popovertargetaction dihilangkan, nilai default toggle digunakan

Pengiriman form dan penanganan penutupan

  • Semua `` memerlukan mekanisme penutupan, dan mekanisme itu harus berfungsi juga pada perangkat tanpa keyboard fisik
  • Ada beberapa cara untuk menutup dialog
    • Mengatur method="dialog" pada di dalam lalu mengirimnya
    • Klik di luar dialog saat light dismiss aktif
    • Menekan tombol Esc pada dialog yang mengizinkannya
    • Memanggil HTMLDialogElement.close()
  • formmethod="dialog" pada `` atau tombol submit akan menutup dialog
    • Status kontrol form disimpan, tetapi tidak dikirim
    • returnValue diatur ke nilai tombol yang diaktifkan
  • Pada form dengan input wajib, user agent mencegah dialog ditutup lewat pengiriman biasa sampai nilai diberikan
    • Menggunakan formnovalidate pada tombol tutup dapat melewati validasi form
    • Dialog juga dapat ditutup dengan memanggil dialog.close() dari JavaScript

Fokus dan aksesibilitas

  • Saat `` dibuka dengan showModal(), fokus secara default diatur ke elemen bersarang pertama yang dapat difokuskan
  • Untuk menentukan posisi fokus awal yang paling tepat pada dialog tertentu, Anda dapat menggunakan atribut autofocus
    • Jika tidak ada elemen yang perlu segera diinteraksikan, disarankan menaruh autofocus pada tombol tutup
    • Jika posisi fokus awal tidak pasti, seperti pada dialog yang dirender secara dinamis, `` itu sendiri dapat menjadi posisi fokus awal yang tepat
  • Cara paling kuat adalah menyertakan tombol eksplisit seperti tombol konfirmasi, batal, atau tutup agar semua pengguna dapat menutup dialog
  • Dialog yang dipanggil dengan showModal() secara default ditutup dengan tombol Esc
    • Dialog non-modal secara default tidak ditutup dengan tombol Esc
    • Pengguna keyboard mengharapkan dialog modal dapat ditutup dengan tombol Esc
    • Jika beberapa dialog modal terbuka, tombol Esc hanya boleh menutup dialog yang terakhir ditampilkan; browser menyediakan perilaku ini saat menggunakan ``
  • `` native menyediakan fitur kegunaan dan aksesibilitas yang harus direplikasi sendiri pada dialog kustom yang dibuat dari elemen lain
  • Browser mengekspos `` secara mirip dengan dialog kustom yang menggunakan ARIA role="dialog"
    • `` yang dipanggil dengan showModal() secara implisit memiliki aria-modal="true"
    • `` yang ditampilkan dengan show(), atribut open, atau perubahan display default diekspos sebagai aria-modal="false"
    • Saat mengimplementasikan dialog modal, semua hal selain `` dan kontennya harus dirender sebagai inert; saat menggunakan showModal(), browser menyediakan perilaku ini

Styling CSS dan backdrop

  • `` dapat dipilih berdasarkan nama elemen seperti elemen biasa
  • Untuk styling berbasis status, pseudo-class :modal dan :open dapat digunakan
  • Latar belakang dialog modal dapat diberi styling dengan pseudo-element ::backdrop
    • Diterapkan pada backdrop yang muncul di belakang `` saat dialog ditampilkan dengan showModal()
    • Dapat memburamkan, menggelapkan, atau menutupi konten di belakang yang berada dalam status inert

Pola penggunaan lewat contoh

  • Contoh Invoker Commands API membuka dialog dengan tombol command="show-modal" dan menutupnya dengan tombol command="close"
    • Dapat dibuka dengan tombol “Open dialog”
    • Dapat ditutup dengan tombol “Close” atau tombol Esc
  • Contoh Popover API membuka dan menutup dialog non-modal dengan popover, popovertarget, dan popovertargetaction
    • Dapat ditutup dengan tombol “Close”, tombol Esc, atau memilih bagian luar dialog
    • Jika menggunakan popover="manual", light dismiss dinonaktifkan
  • Contoh atribut open membuat dialog non-modal HTML-only yang sudah terbuka saat halaman dimuat
    • Dapat ditutup dengan tombol “OK” pada ``
    • Tidak tersedia cara untuk membukanya lagi setelah ditutup
    • Untuk menampilkan dialog non-modal, penggunaan HTMLDialogElement.show() lebih disukai
  • Contoh dialog modal membukanya dengan .showModal() dan menutupnya dengan close()
    • Styling latar gradien dilakukan dengan ::backdrop
    • Saat dialog terbuka, bagian luar dialog menjadi inert sehingga tidak dapat berinteraksi dengan dokumen
  • Contoh returnValue menangani nilai kembalian dialog melalui form dan nilai tombol
    • returnValue default adalah string kosong, atau nilai tombol yang mengirim form di dalam dialog
    • Tombol “Confirm” meneruskan nilai pilihan ke close() untuk digunakan sebagai nilai kembalian
    • Jika ditutup dengan tombol Esc, returnValue tidak diperbarui dan event close juga tidak terjadi sehingga teks `` tidak diperbarui

Animasi ``

  • yang tersembunyi menjadi `display: none`, sedangkan yang ditampilkan menjadi display: block
  • Saat status tampilan berubah, elemen ditambahkan ke atau dihapus dari top layer dan accessibility tree
  • Untuk menganimasikan ``, properti display harus dapat dianimasikan
    • Browser yang mendukungnya menangani display sebagai animasi diskret
    • Saat berubah dari none ke block, perubahan ke block terjadi pada titik 0% sehingga elemen terlihat selama seluruh animasi
    • Saat berubah dari block ke none, perubahan ke none terjadi pada titik 100% sehingga elemen terlihat selama seluruh animasi
  • Animasi dengan CSS transition

    • Untuk menganimasikan `` dengan CSS transition, fitur berikut diperlukan
    • @starting-style: menyediakan nilai awal transisi setiap kali dialog dibuka
    • Transisi display: mempertahankan dialog pada nilai display yang terlihat selama transisi
    • Transisi overlay: menunda penghapusan dari top layer hingga transisi selesai
    • transition-behavior: allow-discrete: mengaktifkan transisi diskret untuk display dan overlay yang secara default tidak dianimasikan
    • Pada browser yang tidak mendukung pseudo-class :open, status terbuka dapat diberi styling dengan selector atribut dialog[open]
    • Karena `` berubah dari display: none ke display: block setiap kali ditampilkan, setiap transisi masuk beralih dari @starting-style ke gaya dialog:open
  • Animasi keyframe

    • Animasi CSS keyframe memiliki batasan yang berbeda dari transition
    • Tidak menyediakan @starting-style
    • Menyertakan nilai display di dalam keyframe
    • Tidak perlu mengaktifkan allow-discrete secara eksplisit
    • overlay juga tidak perlu diatur di keyframe
    • Fade-out backdrop tidak dapat dianimasikan karena backdrop langsung dihapus dari DOM saat dialog ditutup

Ringkasan teknis

  • Kategori konten: flow content, sectioning root
  • Konten yang diizinkan: flow content
  • Tag awal dan tag akhir sama-sama wajib, dan tidak ada penghilangan tag
  • Parent yang diizinkan: semua elemen yang menerima flow content
  • Peran ARIA implisit: dialog
  • Peran ARIA yang diizinkan: alertdialog
  • Antarmuka DOM: HTMLDialogElement
  • Spesifikasi: HTML # the-dialog-element

1 komentar

 
GN⁺ 2024-12-08
Komentar Hacker News
  • Elemen HTML yang dapat berinteraksi mencakup pemilih berkas, pemilih warna, pemilih tanggal/waktu, slider angka, opsi rekomendasi pada kolom teks, ringkasan/detail yang dapat dibuka, FAQ, dan pemutar media dengan kontrol.
    Elemen-elemen seperti ini bagus karena ringan dan semantik, dan <details> yang berbagi name yang sama juga dapat berperilaku menutup item sebelumnya saat salah satunya dibuka.

    • Belakangan ini, pada akhirnya perilakunya tidak cukup dapat diprediksi di tiap browser atau versi, sehingga hal-hal seperti ini sering digantikan oleh toolkit.
    • Pemutar media dengan kontrol tampil berbeda di setiap browser, dan tanpa JavaScript tidak bisa diberi styling.
      Semoga implementasinya bisa lebih baik.
  • Saya sudah memakai <dialog> sejak 2019, dan meskipun Firefox serta Safari belum mendukungnya selama beberapa tahun lagi, kualitas polyfill dari Google cukup baik sehingga saya menggunakannya tanpa masalah di produksi SaaS untuk bisnis.
    Hal yang paling mengecewakan adalah elemennya hampir tidak diberi styling. Hanya ada border piksel hitam tebal yang sangat dasar dan sudut-sudut tajam, berbeda dari harapan saya bahwa browser akan mengikuti konvensi penempatan dialog/jendela sistem operasi sehingga terlihat seperti dialog native macOS, iOS, atau Windows.
    Karena elemen yang terbuka berada di lapisan teratas yang terpisah, saya juga berharap ia bisa keluar hingga di luar viewport browser, tetapi harapan itu pun pupus. Dari sudut pandang pengguna, mereka tidak menginginkan popup atau dialog modal yang tidak bisa dipindahkan dan sepenuhnya menutupi isi dokumen.
    Para vendor browser tampaknya ingin kita tidak menggunakan alert(), prompt(), dan confirm() karena semuanya memblokir JavaScript/main thread, tetapi mereka belum menghadirkan API pengganti yang sama sederhananya, efektif, dan tidak menuntut kemampuan desain UI. Alih-alih mencoba meyakinkan bahwa <dialog> selalu lebih baik di semua situasi, akan lebih baik jika mereka menyediakan API alert/prompt/confirm non-pemblokir berbasis Promise.

    • Fitur yang bisa keluar dari viewport browser dan diberi styling seperti dialog sistem kemungkinan lebih diinginkan oleh developer daripada oleh pengguna atau pembuat browser.
      Bahwa fitur-fitur baru tidak lagi berperilaku seperti keluarga alert() lama bukan sekadar kekecewaan yang kebetulan, tetapi juga ada sisi baiknya.
      Namun gaya default <dialog> semestinya sedikit lebih diperhatikan; meskipun tidak 100% terlihat dan berperilaku seperti dialog sistem di luar DOM, sekadar memiliki gaya default yang cocok dengan gaya bawaan browser saja sudah akan jauh lebih baik.
      Untuk aplikasi web yang punya lebih banyak izin daripada halaman biasa, seperti PWA, arah seperti ini mungkin bisa lebih diterima, mirip fitur terpisah untuk styling jendela atau berinteraksi dengan sistem.
    • Jika vendor membuat <dialog> terlihat seperti jendela browser native, itu bisa mengaburkan line of death.
      Situs web berbahaya akan lebih mudah menampilkan popup “pembaruan browser” di tengah halaman, lalu mengalihkan pengguna ke halaman unduhan Chrome yang tampak meyakinkan untuk membuat mereka mengunduh executable yang sudah dimodifikasi.
      https://textslashplain.com/2017/01/14/the-line-of-death/
    • Modal yang memblokir fokus seluruh jendela browser bukan ide yang baik.
      Pengguna membuka banyak tab, dan informasi yang diperlukan untuk menyelesaikan dialog bisa saja berada di tab lain.
      Kita juga harus sangat berhati-hati soal seberapa besar kendali visual yang diberikan ke dialog sebenarnya. Terutama jika dibuat tampak seperti sistem operasi; bahkan sekarang pun banyak orang tertipu oleh peringatan virus browser palsu, dan dialog yang tampak asli serta wajib diinteraksikan dapat sangat meningkatkan tingkat keberhasilan serangan.
    • Fakta bahwa styling-nya hampir tidak ada tampaknya menjadi alasan utama yang menghambat penggunaan luas komponen bawaan browser.
      Sebagian besar perusahaan tempat saya bekerja memiliki bahasa desain yang cukup konsisten, dan apa pun kata orang yang menyukai HTML/CSS murni, memakai komponen bawaan browser apa adanya hampir selalu terlihat canggung dan amatiran.
      Jika masalah ini tidak diselesaikan, sulit bagi komponen seperti ini untuk dipakai luas.
    • Saya sedang menguji ide membuat pengganti alert(), prompt(), dan confirm() bekerja seperti await Prompts.alert("This is an alert message!");, await Prompts.confirm(...), dan await Prompts.prompt(...).
      Demo: https://tools.simonwillison.net/prompts-js
      Kodenya ditulis oleh o1: https://chatgpt.com/share/67539c28-4df0-8006-b021-4f468e011f...
  • Saya menulis artikel berjudul “The HTML dialog element API is a mess”: https://lapcatsoftware.com/articles/2024/2/1.html

    • Tidak banyak orang yang akan bilang standar web itu hebat, dirancang dengan baik, dan dikelola dengan baik
      Meski begitu, nilainya ada pada fakta bahwa ia adalah standar. Web itu hebat karena berjalan di mana saja, dan memang tidak mungkin tidak berantakan
      Dalam konteks ini, menurut saya <dialog> adalah keberhasilan, terutama untuk tool manajemen internal. Saya tidak peduli dengan tren frontend terbaru; saya hanya ingin menampilkan konten sebagai overlay modal di atas tampilan utama sambil menghemat ruang layar
    • Secara realistis, hampir tidak ada kasus membuka dialog yang sama dua kali sebagai modal
      Untuk membuka dialog diperlukan input pengguna, dan dialog modal memblokir input pengguna, jadi agar ini terjadi, input di dalam dialog harus membuka dialog itu lagi
      Jika membuka dialog lewat pekerjaan asinkron, Anda harus melacak mana yang sedang terbuka dan tertutup; hal serupa juga terjadi di lingkungan seperti Qt
    • Saya berharap <dialog> menjadi semacam versi lebih kuat dari await confirm/prompt yang dapat dikustomisasi dan tidak memunculkan peringatan berbeda-beda antar-browser meski dibuka beberapa kali
      Namun pada praktiknya, ia lebih mirip div yang dipercantik
    • Google Chrome juga membuat URLPattern, dan saya berharap Chrome dan Safari tidak mendukungnya
      Compression Streams API tidak buruk, tetapi itu API yang sangat kecil
      Melihat polanya, rasanya Google kurang baik dalam menangani pengalaman pengguna dan pengalaman developer
      Sebagai tambahan, setelah mencari posisi standar, ternyata keduanya mendukung URLPattern
  • Terlepas dari implementasinya, menurut saya ini adalah satu langkah ke arah yang benar
    Saat ini juga ada proposal yang tampaknya memperkuat <select>: https://open-ui.org/components/combobox.explainer/
    Untuk notifikasi toast, Popover API sudah masuk ke browser: https://mdn.github.io/dom-examples/popover-api/
    Ada juga proposal popover hint untuk tooltip: https://open-ui.org/components/popover-hint.research.explain...

  • Saya suka elemen <dialog>, terutama pertimbangan aksesibilitas yang sudah bawaan dan terstandardisasi
    Saya menantikan hari ketika bisa menggunakannya tanpa polyfill setelah Safari di bawah 15.4 keluar dari kriteria dukungan
    Namun keluhan terbesar saya adalah ketergantungannya pada JavaScript. Hampir semua pengunjung situs memang akan datang dengan JavaScript aktif, tetapi ada kepuasan tersendiri saat membuatnya tidak harus begitu
    Saya tidak tahu mengapa status terbuka dialog tidak bisa dikendalikan dengan CSS atau tombol target, dan saya ingin tahu jika saya keliru

    • Karena itu, saya biasanya memakai elemen kustom dengan atribut popover=""
      Elemen itu mudah dijadikan target oleh tombol, tidak membutuhkan JavaScript sisi klien, dan bisa ditutup dengan mengklik area luar. Ini perilaku yang tidak ada secara default pada <dialog>
      Saya tidak yakin soal aksesibilitasnya, tetapi rasanya tidak lebih buruk daripada cara lama memakai label/checkbox/elemen form tersembunyi, jauh lebih sederhana, dan tidak terasa terlalu seperti hack
    • Ini nantinya akan dimungkinkan lewat invokers
      https://css-tricks.com/invoker-commands-additional-ways-to-w...
  • Akan menyenangkan jika HTML mendukung konsep tag seperti <page> yang dapat mendefinisikan beberapa halaman dalam satu file dan menampilkannya satu per satu
    Tanpa terlihat seperti dialog, setiap PAGE bisa menarik bagian umum seperti header, sidebar, dan footer di halaman yang sama bergantung pada status terpilihnya
    Sekarang ini memang bisa dilakukan dengan menyembunyikan dan menampilkan div, tetapi dukungan pendekatan seperti ini lewat tag HTML tertentu bisa membantu

    • Sepertinya belakangan ada peningkatan semacam itu untuk stylesheet cetak, tetapi setahu saya belum ada untuk tampilan layar
  • Saat mencobanya hari ini, ada masalah yang tidak bisa saya atasi dengan workaround
    Jika ada form di dalam dialog, ketika menekan Enter saat fokus berada pada input atau menekan Space saat fokus berada pada tombol submit, dialog ikut tertutup bersamaan dengan submit form
    Saya tidak menemukan cara yang rapi untuk mencegahnya. Biasanya form memuat ulang halaman, jadi ini mungkin bukan masalah umum, tetapi di sini saya memakai htmx

    • Kalimat terakhir kemungkinan besar benar. Secara default, form mengirim permintaan jaringan
      Saya memakai editor yang memperbarui iframe lewat form di dalam <dialog>, dan iframe target saja yang dimuat ulang; dialognya tidak tertutup
      Saya juga tidak bisa mereproduksi kasus dialog tertutup saat menekan Enter. Itu seharusnya sama dengan submit, dan submit juga tidak menutup dialog
      Sebagai catatan, kemarin saya baru tahu bahwa tombol bisa menutup dialog
    • Bukankah cukup memakai preventDefault dan stopPropagation?
    • Mungkin sebaiknya melaporkan bug ke pihak HTMX
  • Saat mencari pengelola persetujuan cookie untuk build baru yang sudah dioptimalkan, saya tidak suka opsi open source yang ukurannya lebih dari 100KB, jadi saya membuat sendiri: https://github.com/replete/biscuitman
    Saya mengandalkan <dialog> agar bisa menulisnya sekecil mungkin, dan dengan hanya beberapa aturan CSS, ia tetap berjalan secara native bahkan tanpa styling
    Pada akhirnya saya juga sampai membuat tool build yang mengompilasi hingga IE11 dan versi browser yang benar-benar tua
    <dialog> umumnya bekerja dengan baik, dan di browser lama memang perlu sedikit trik CSS, tetapi tidak sulit ditangani. Ini tambahan yang bagus untuk platform web, tetapi setelah melakukan pekerjaan ini selama 20 tahun, saya tidak ingin harus membuat ulang kontrol multi-select kustom setiap beberapa tahun. Kontrol native itu bagus

  • Perilaku tutup yang umum di sebagian besar contoh tidak berfungsi di Android Firefox

    • Saat dipakai di proyek kemarin, atribut autofocus pada tombol tutup tidak berfungsi
      Akhirnya setiap kali memanggil show(), saya menambahkan baris seperti $('#thatModal *[autofocus]').focus()
      Menurut MDN, secara default seharusnya berfungsi sesuai yang dimaksud
    • Bisa jelaskan lebih lanjut apa yang dimaksud dengan “perilaku tutup yang umum di sebagian besar contoh”? Semua contoh yang saya lihat punya potongan JavaScript yang memasang event listener pada tombol tutup, dan itu berfungsi baik di Firefox for Android
    • Sama juga di Chrome pada Windows 10
      Sepertinya hanya listener yang ditambahkan lewat JavaScript yang berfungsi dengan benar