Sasaran inisiatif UI Terbuka adalah memudahkan developer dalam memberikan pengalaman pengguna yang luar biasa. Untuk melakukannya, kami mencoba mengatasi pola yang lebih bermasalah yang dihadapi developer. Kami dapat melakukannya dengan menyediakan API dan komponen bawaan platform yang lebih baik.
Salah satu area masalah tersebut adalah pop-up, yang dijelaskan di UI Terbuka sebagai "Pop-up".
Pop-up memiliki reputasi yang agak terpolarisasi sejak lama. Salah satu alasannya adalah karena cara project tersebut dibangun dan di-deploy. Mereka bukanlah pola yang mudah untuk dibuat dengan baik, tetapi mereka bisa menghasilkan banyak nilai dengan mengarahkan pengguna ke hal-hal tertentu, atau membuat mereka menyadari konten di situs Anda—terutama jika digunakan dengan cara yang menarik.
Sering kali ada dua masalah utama saat membuat popover:
- Cara memastikannya ditempatkan di atas konten lainnya di tempat yang sesuai.
- Cara membuatnya mudah diakses (ramah keyboard, dapat difokuskan, dan sebagainya).
Popover API bawaan memiliki berbagai sasaran, semuanya dengan sasaran menyeluruh yang sama, yaitu memudahkan developer membuat pola ini. Sasaran yang penting adalah:
- Permudah untuk menampilkan elemen dan turunannya di atas bagian lain dokumen.
- Buat mudah diakses.
- Tidak memerlukan JavaScript untuk sebagian besar perilaku umum (menutup ringan, singleton, penumpukan, dan sebagainya).
Anda dapat melihat spesifikasi lengkap untuk pop-up di situs OpenUI.
Kompatibilitas browser
Di mana Anda dapat menggunakan Popover API bawaan sekarang? Fitur ini didukung di Chrome Canary di balik flag "Fitur platform web eksperimental" pada saat penulisan.
Untuk mengaktifkan tanda tersebut, buka Chrome Canary dan buka chrome://flags
. Kemudian, aktifkan tanda "Fitur platform web eksperimental".
Ada juga Uji Coba Origin untuk developer yang ingin mengujinya di lingkungan produksi.
Terakhir, ada polyfill yang sedang dikembangkan untuk API. Pastikan untuk melihat repo di github.com/oddbird/popup-polyfill.
Anda dapat memeriksa dukungan pop-up dengan:
const supported = HTMLElement.prototype.hasOwnProperty("popover");
Solusi saat ini
Apa yang dapat Anda lakukan saat ini untuk mempromosikan konten Anda di atas segalanya? Jika didukung di browser, Anda dapat menggunakan elemen Dialog HTML. Anda harus menggunakannya dalam bentuk "Modal". Dan tindakan ini memerlukan JavaScript agar dapat digunakan.
Dialog.showModal();
Ada beberapa pertimbangan aksesibilitas. Sebaiknya gunakan a11y-dialog, misalnya jika melayani pengguna Safari di bawah versi 15.4.
Anda juga dapat menggunakan salah satu dari banyak library berbasis popover, pemberitahuan, atau tooltip yang ada. Banyak dari aplikasi ini cenderung berfungsi dengan cara yang sama.
- Tambahkan beberapa penampung ke isi untuk menampilkan popover.
- Tata gayanya agar berada di atas yang lainnya.
- Buat elemen dan tambahkan ke penampung untuk menampilkan popover.
- Sembunyikan dengan menghapus elemen pop-up dari DOM.
Hal ini memerlukan dependensi tambahan dan lebih banyak keputusan bagi developer. Anda juga perlu meneliti untuk menemukan penawaran yang menyediakan semua yang Anda butuhkan. Popover API bertujuan untuk memenuhi banyak skenario, termasuk tooltip. Tujuannya adalah untuk mencakup semua skenario umum tersebut, sehingga developer tidak perlu membuat keputusan lain sehingga mereka dapat berfokus untuk membangun pengalaman mereka.
Pop-up pertama Anda
Hanya itu yang Anda perlukan.
<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>
Namun, apa yang terjadi di sini?
- Anda tidak perlu menempatkan elemen popover ke dalam penampung atau apa pun—elemen ini disembunyikan secara default.
- Anda tidak perlu menulis JavaScript apa pun untuk membuatnya muncul. Hal ini ditangani oleh atribut
popovertoggletarget
. - Saat muncul, item akan dipromosikan ke lapisan atas. Artinya, akan dipromosikan di atas
document
di area pandang. Anda tidak perlu mengelolaz-index
atau khawatir tentang posisi popover di DOM. Elemen ini dapat disusun bertingkat dalam DOM, dengan ancestor pemotongan. Anda juga dapat melihat elemen yang saat ini berada di lapisan atas melalui DevTools. Untuk mengetahui informasi selengkapnya tentang lapisan atas, baca artikel ini.
- Anda akan mendapatkan "Light Dismiss" dari kotak. Artinya, Anda dapat menutup popover dengan sinyal tutup, seperti mengklik di luar popover, menavigasi keyboard ke elemen lain, atau menekan tombol Esc. Buka lagi dan coba!
Apa lagi yang Anda dapatkan dengan popover? Mari kita bahas contoh ini lebih lanjut. Pertimbangkan demo ini dengan beberapa konten di halaman.
Tombol tindakan mengambang tersebut memiliki posisi tetap dengan z-index
yang tinggi.
.fab {
position: fixed;
z-index: 99999;
}
Konten pop-up disusun bertingkat di DOM, tetapi saat Anda membuka pop-up, konten tersebut akan dipromosikan di atas elemen posisi tetap tersebut. Anda tidak perlu menetapkan gaya apa pun.
Anda mungkin juga melihat bahwa popover kini memiliki elemen pseudo ::backdrop
. Semua elemen yang berada di lapisan atas mendapatkan elemen pseudo ::backdrop
yang dapat ditata gayanya. Contoh ini menata gaya ::backdrop
dengan warna latar belakang alfa yang dikurangi dan filter latar belakang, yang memburamkan konten yang mendasarinya.
Menata gaya popover
Mari kita alihkan perhatian ke gaya popover. Secara default, popover memiliki posisi tetap dan beberapa padding yang diterapkan. Ini juga memiliki display: none
. Anda dapat menggantinya untuk menampilkan popover. Namun, tindakan tersebut tidak akan mempromosikannya ke lapisan atas.
[popover] { display: block; }
Terlepas dari cara Anda mempromosikan popover, setelah mempromosikan popover ke lapisan atas, Anda mungkin perlu menata atau memosisikan popover. Anda tidak dapat menargetkan lapisan atas dan melakukan hal seperti
:open {
display: grid;
place-items: center;
}
Secara default, popover akan ditata di tengah area pandang menggunakan margin: auto
. Namun, dalam beberapa kasus, Anda mungkin ingin menentukan posisi secara eksplisit. Contoh:
[popover] {
top: 50%;
left: 50%;
translate: -50%;
}
Jika Anda ingin menata letak konten di dalam popover menggunakan petak CSS atau flexbox, sebaiknya gabungkan konten ini dalam elemen. Jika tidak, Anda harus mendeklarasikan aturan terpisah yang mengubah display
setelah popover berada di lapisan atas. Menetapkan setelan ini secara default akan membuatnya ditampilkan secara default, yang akan mengganti display: none
.
[popover]:open {
display: flex;
}
Jika telah mencoba demo tersebut, Anda akan melihat bahwa popover kini bertransisi masuk dan keluar. Anda dapat melakukan transisi popover masuk dan keluar menggunakan pemilih pseudo :open
. Pemilih pseudo :open
cocok dengan popover yang ditampilkan (dan karenanya berada di lapisan atas).
Contoh ini menggunakan properti kustom untuk mendorong transisi. Anda juga dapat menerapkan transisi ke ::backdrop
popover.
[popover] {
--hide: 1;
transition: transform 0.2s;
transform: translateY(calc(var(--hide) * -100vh))
scale(calc(1 - var(--hide)));
}
[popover]::backdrop {
transition: opacity 0.2s;
opacity: calc(1 - var(--hide, 1));
}
[popover]:open::backdrop {
--hide: 0;
}
Tips di sini adalah untuk mengelompokkan transisi dan animasi pada kueri media untuk {i>motion.<i} Hal ini juga dapat membantu menjaga pengaturan waktu Anda. Hal ini karena Anda tidak dapat berbagi nilai antara popover
dan ::backdrop
melalui properti kustom.
@media(prefers-reduced-motion: no-preference) {
[popover] { transition: transform 0.2s; }
[popover]::backdrop { transition: opacity 0.2s; }
}
Hingga saat ini, Anda telah melihat penggunaan popovertoggletarget
untuk menampilkan pop-up. Untuk menutupnya, kita menggunakan "Tutup ringan". Namun, Anda juga mendapatkan atribut popovershowtarget
dan popoverhidetarget
yang dapat digunakan. Mari kita tambahkan tombol ke popover yang menyembunyikannya dan ubah tombol untuk menggunakan popovershowtarget
.
<div id="code-popover" popover>
<button popoverhidetarget="code-popover">Hide Code</button>
</div>
<button popovershowtarget="code-popover">Reveal Code</button>
Seperti yang disebutkan sebelumnya, Popover API mencakup lebih dari sekadar konsep pop-up historis kami. Anda dapat mem-build untuk semua jenis skenario seperti notifikasi, menu, tooltip, dll.
Beberapa skenario tersebut memerlukan pola interaksi yang berbeda. Interaksi seperti mengarahkan kursor. Penggunaan atribut popoverhovertarget
pernah diuji coba, tetapi saat ini tidak diterapkan.
<div popoverhovertarget="hover-popover">Hover for Code</div>
Idenya adalah Anda mengarahkan kursor ke elemen untuk menampilkan target. Perilaku ini dapat dikonfigurasi melalui properti CSS. Properti CSS ini akan menentukan jangka waktu untuk mengarahkan kursor ke dan dari elemen yang bereaksi terhadap popover. Perilaku default yang diuji coba memiliki popover yang ditampilkan setelah 0.5s
eksplisit dari :hover
. Kemudian, popover tersebut akan memerlukan penutupan ringan atau pembukaan popover lain untuk ditutup (Selengkapnya akan dibahas nanti). Hal ini disebabkan karena durasi tersembunyi popover disetel ke Infinity
.
Sementara itu, Anda dapat menggunakan JavaScript untuk mem-polyfill fungsi tersebut.
let hoverTimer;
const HOVER_TRIGGERS = document.querySelectorAll("[popoverhovertarget]");
const tearDown = () => {
if (hoverTimer) clearTimeout(hoverTimer);
};
HOVER_TRIGGERS.forEach((trigger) => {
const popover = document.querySelector(
`#${trigger.getAttribute("popoverhovertarget")}`
);
trigger.addEventListener("pointerenter", () => {
hoverTimer = setTimeout(() => {
if (!popover.matches(":open")) popover.showPopOver();
}, 500);
trigger.addEventListener("pointerleave", tearDown);
});
});
Manfaat menetapkan sesuatu sebagai periode pengarahan kursor yang eksplisit adalah memastikan tindakan pengguna disengaja (misalnya, pengguna mengarahkan kursor ke target). Kita tidak ingin menampilkan pop-up kecuali jika itu adalah niat mereka.
Coba demo ini dan arahkan Anda ke target dengan jendela yang disetel ke 0.5s
.
Sebelum menjelajahi beberapa kasus penggunaan dan contoh umum, mari kita bahas beberapa hal.
Jenis popover
Kita telah membahas perilaku interaksi non-JavaScript. Namun, bagaimana dengan perilaku popover secara keseluruhan. Bagaimana jika Anda tidak ingin "Tutup ringan"? Atau Anda ingin menerapkan pola singleton ke popover Anda?
Popover API memungkinkan Anda menentukan tiga jenis popover yang berbeda perilakunya.
[popover=auto]/[popover]
:
- Dukungan bertingkat. Ini juga tidak hanya berarti bertingkat dalam DOM. Definisi popover ancestral adalah:
- terkait berdasarkan posisi DOM (turunan).
- terkait dengan memicu atribut pada elemen turunan seperti
popovertoggletarget
,popovershowtarget
, dan seterusnya. - terkait dengan atribut
anchor
(CSS Anchoring API sedang dalam pengembangan).
- Menutup dengan ringan.
- Membuka akan menutup popover lain yang bukan popover leluhur. Coba demo di bawah yang menyoroti cara kerja pembuatan bertingkat dengan popover leluhur. Lihat bagaimana perubahan beberapa instance
popoverhidetarget
/popovershowtarget
menjadipopovertoggletarget
akan mengubah hal-hal. - Menghapus satu dengan ringan akan menghapus semuanya, tetapi menghapus satu di kelompok hanya akan menghapus yang berada di atasnya dalam kelompok.
[popover=manual]
:
- Tidak menutup popover lainnya.
- Tidak ada lampu yang dimatikan.
- Memerlukan penutupan eksplisit melalui elemen pemicu atau JavaScript.
JavaScript API
Jika memerlukan lebih banyak kontrol atas popover, Anda dapat menggunakan JavaScript. Anda mendapatkan metode showPopover
dan hidePopover
. Anda juga memiliki peristiwa popovershow
dan popoverhide
yang akan diproses:
Menampilkan pop-up
js
popoverElement.showPopover()
Menyembunyikan pop-up:
popoverElement.hidePopover()
Memproses popover yang ditampilkan:
popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)
Memproses popover yang ditampilkan dan membatalkan tampilannya:
popoverElement.addEventListener('popovershow',event => {
event.preventDefault();
console.warn(‘We blocked a popover from being shown’);
})
Dengarkan popover yang disembunyikan:
popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)
Anda tidak dapat membatalkan popover yang disembunyikan:
popoverElement.addEventListener('popoverhide',event => {
event.preventDefault();
console.warn("You aren't allowed to cancel the hiding of a popover");
})
Periksa apakah popover berada di lapisan atas:
popoverElement.matches(':open')
Hal ini memberikan daya ekstra untuk beberapa skenario yang kurang umum. Misalnya, tampilkan popover setelah periode tidak ada aktivitas.
Demo ini memiliki pop-up dengan suara pop yang terdengar, jadi kita memerlukan JavaScript untuk memutar audio. Saat diklik, kita akan menyembunyikan popover, memutar audio, lalu menampilkannya lagi.
Aksesibilitas
Aksesibilitas adalah hal yang paling penting dalam pemikiran dengan Popover API. Pemetaan aksesibilitas mengaitkan popover dengan elemen pemicunya, sesuai kebutuhan. Artinya, Anda tidak perlu mendeklarasikan atribut aria-*
seperti aria-haspopup
, dengan asumsi Anda menggunakan salah satu atribut pemicu seperti popovertoggletarget
.
Untuk pengelolaan fokus, Anda dapat menggunakan atribut fokus otomatis untuk memindahkan fokus ke elemen di dalam popover. Sama halnya dengan Dialog, tetapi perbedaannya muncul saat menampilkan fokus, dan hal ini terjadi karena penyingkatan cahaya. Pada umumnya, menutup popover akan mengembalikan fokus ke elemen yang sebelumnya difokuskan. Namun, fokus akan dipindahkan ke elemen yang diklik saat menutup ringan, jika elemen tersebut dapat mendapatkan fokus. Lihat bagian tentang pengelolaan fokus dalam penjelasan.
Anda perlu membuka "versi layar penuh" demo ini agar dapat melihatnya.
Dalam demo ini, elemen yang difokuskan mendapatkan garis luar berwarna hijau. Coba gunakan tombol tab di antarmuka dengan keyboard. Perhatikan tempat fokus ditampilkan saat popover ditutup. Anda mungkin juga melihat bahwa jika Anda beralih tab, popover akan tertutup. Hal ini sesuai dengan desain. Meskipun memiliki pengelolaan fokus, popover tidak menjebak fokus. Selain itu, navigasi keyboard mengidentifikasi sinyal tutup saat fokus berpindah dari popover.
Penjangkaran (sedang dalam pengembangan)
Untuk popover, pola yang sulit untuk dipenuhi adalah mengaitkan elemen ke pemicunya. Misalnya, jika tooltip disetel untuk ditampilkan di atas pemicunya, tetapi dokumen di-scroll. Tooltip tersebut dapat terpotong oleh area pandang. Saat ini ada penawaran JavaScript untuk mengatasi hal ini seperti "Floating UI". Alat ini akan memosisikan ulang tooltip untuk menghentikan hal ini dan mengandalkan urutan posisi yang diinginkan.
Namun, kami ingin Anda dapat menentukannya dengan gaya Anda. Ada API pendamping yang sedang dikembangkan bersama Popover API untuk mengatasi hal ini. API "CSS Anchor Positioning" akan memungkinkan Anda melakukan tethering elemen ke elemen lain, dan akan melakukannya dengan cara mengubah posisi elemen agar tidak terpotong oleh area tampilan.
Demo ini menggunakan Anchoring API dalam statusnya saat ini. Posisi perahu merespons posisi anchor di area pandang.
Berikut adalah cuplikan CSS yang membuat demo ini berfungsi. JavaScript tidak diperlukan.
.anchor {
--anchor-name: --anchor;
}
.anchored {
position: absolute;
position-fallback: --compass;
}
@position-fallback --compass {
@try {
bottom: anchor(--anchor top);
left: anchor(--anchor right);
}
@try {
top: anchor(--anchor bottom);
left: anchor(--anchor right);
}
}
Anda dapat melihat spesifikasinya di sini. Akan ada juga polyfill untuk API ini.
Contoh
Setelah Anda memahami apa yang ditawarkan popover dan caranya, mari kita pelajari beberapa contohnya.
Notifikasi
Demo ini menampilkan notifikasi "Salin ke papan klip".
- Menggunakan
[popover=manual]
. - Pada tindakan, tampilkan pop-up dengan
showPopover
. - Setelah waktu tunggu
2000ms
habis, sembunyikan denganhidePopover
.
Toast
Demo ini menggunakan lapisan atas untuk menampilkan notifikasi gaya toast.
- Satu popover dengan jenis
manual
bertindak sebagai penampung. - Notifikasi baru ditambahkan ke popover dan popover ditampilkan.
- Elemen ini dihapus dengan API animasi web saat diklik dan dihapus dari DOM.
- Jika tidak ada toast yang akan ditampilkan, popover akan disembunyikan.
Menu bertingkat
Demo ini menunjukkan cara kerja menu navigasi bertingkat.
- Gunakan
[popover=auto]
karena memungkinkan popover bertingkat. - Gunakan
autofocus
pada link pertama setiap dropdown untuk menavigasi dengan keyboard. - Ini adalah kandidat yang sempurna untuk CSS Anchoring API. Namun, untuk demo ini, Anda dapat menggunakan JavaScript dalam jumlah kecil untuk memperbarui posisi menggunakan properti kustom.
const ANCHOR = (anchor, anchored) => () => {
const { top, bottom, left, right } = anchor.getBoundingClientRect();
anchored.style.setProperty("--top", top);
anchored.style.setProperty("--right", right);
anchored.style.setProperty("--bottom", bottom);
anchored.style.setProperty("--left", left);
};
PRODUCTS_MENU.addEventListener("popovershow", ANCHOR(PRODUCT_TARGET, PRODUCTS_MENU));
Ingat, karena demo ini menggunakan autofocus
, demo ini harus dibuka dalam "tampilan layar penuh" untuk navigasi keyboard.
Pop-up media
Demo ini menunjukkan cara menampilkan media.
- Menggunakan
[popover=auto]
untuk menutup ringan. - JavaScript memproses peristiwa
play
video dan memunculkan video. - Peristiwa
popoverhide
pop-up menjeda video.
Popover gaya Wiki
Demo ini menunjukkan cara membuat tooltip konten inline yang berisi media.
- Menggunakan
[popover=auto]
. Menampilkan salah satunya akan menyembunyikan yang lain karena tidak bersifat leluhur. - Ditampilkan di
pointerenter
dengan JavaScript. - Kandidat lain yang sempurna untuk CSS Anchoring API.
Panel Samping Navigasi
Demo ini membuat panel navigasi menggunakan popover.
- Menggunakan
[popover=auto]
untuk menutup ringan. - Menggunakan
autofocus
untuk memfokuskan item navigasi pertama.
Mengelola latar belakang
Demo ini menunjukkan cara mengelola latar belakang untuk beberapa popover yang hanya ingin menampilkan satu ::backdrop
.
- Gunakan JavaScript untuk mempertahankan daftar popover yang terlihat.
- Terapkan nama class ke popover terendah di lapisan atas.
Pop-up kursor kustom
Demo ini menunjukkan cara menggunakan popover
untuk mempromosikan canvas
ke lapisan atas dan menggunakannya untuk menampilkan kursor kustom.
- Mempromosikan
canvas
ke lapisan atas denganshowPopover
dan[popover=manual]
. - Saat pop-up lain dibuka, sembunyikan dan tampilkan pop-up
canvas
untuk memastikannya berada di atas.
Popover sheet tindakan
Demo ini menunjukkan cara menggunakan popover sebagai actionsheet.
- Menampilkan popover secara default yang menggantikan
display
. - Actionsheet dibuka dengan pemicu popover.
- Saat ditampilkan, popover akan dipromosikan ke lapisan atas dan diterjemahkan ke dalam tampilan.
- Penutupan ringan dapat digunakan untuk menampilkannya kembali.
Popover yang diaktifkan keyboard
Demo ini menunjukkan cara menggunakan popover untuk UI gaya palet perintah.
- Gunakan cmd + j untuk menampilkan popover.
input
difokuskan denganautofocus
.- Kotak kombinasi adalah
popover
kedua yang diposisikan di bawah input utama. - Menutup ringan akan menutup palet jika dropdown tidak ada.
- Kandidat lain untuk Anchoring API
Popover berjangka waktu
Demo ini menampilkan popover tidak ada aktivitas setelah empat detik. Pola UI yang sering digunakan di aplikasi yang menyimpan informasi aman tentang pengguna untuk menampilkan modal logout.
- Gunakan JavaScript untuk menampilkan popover setelah periode tidak ada aktivitas.
- Saat popover ditampilkan, reset timer.
Screensaver
Serupa dengan demo sebelumnya, Anda dapat menambahkan sedikit keceriaan ke situs dan menambahkan screensaver.
- Gunakan JavaScript untuk menampilkan popover setelah periode tidak ada aktivitas.
- Tutup lampu untuk menyembunyikan dan mereset timer.
Tanda sisipan mengikuti
Demo ini menunjukkan bagaimana Anda bisa membuat popover mengikuti tanda sisipan input.
- Menampilkan popover berdasarkan pilihan, peristiwa tombol, atau input karakter khusus.
- Gunakan JavaScript untuk memperbarui posisi pop-up dengan properti kustom cakupan.
- Pola ini akan memerlukan pemikiran yang cermat terhadap konten yang ditampilkan dan aksesibilitas.
- Ikon ini sering terlihat di UI pengeditan teks dan aplikasi tempat Anda dapat memberi tag.
Menu tombol tindakan mengambang
Demo ini menunjukkan cara menggunakan popover untuk menerapkan menu tombol tindakan mengambang tanpa JavaScript.
- Promosikan pop-up jenis
manual
dengan metodeshowPopover
. Ini adalah tombol utama. - Menu adalah popover lain yang merupakan target tombol utama.
- Menu dibuka dengan
popovertoggletarget
. - Gunakan
autofocus
untuk memfokuskan item menu pertama saat ditampilkan. - Menutup ringan akan menutup menu.
- Putaran ikon menggunakan
:has()
. Anda dapat membaca selengkapnya tentang:has()
di artikel ini.
Selesai.
Jadi, ini adalah pengantar untuk popover, yang akan menjadi bagian dari inisiatif Open UI. Jika digunakan dengan bijak, ini akan menjadi tambahan yang luar biasa untuk platform web.
Pastikan untuk melihat UI Terbuka. Penjelasan pop-up selalu diperbarui seiring perkembangan API. Berikut adalah koleksi untuk semua demo.
Terima kasih telah mampir.
Foto oleh Madison Oren di Unsplash