Tujuan dari Inisiatif UI Terbuka adalah memudahkan developer dalam menciptakan pengalaman pengguna yang luar biasa. Untuk melakukannya, kami mencoba mengatasi pola bermasalah yang lebih banyak dihadapi developer. Kita bisa 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 "Popover".
{i>Popover<i} memiliki reputasi yang cukup polarisasi sejak lama. Hal ini sebagian disebabkan oleh cara mereka mem-build dan men-deploy. Membuat situs dengan baik bukanlah pola yang mudah, tetapi dapat menghasilkan banyak nilai dengan mengarahkan pengguna ke hal-hal tertentu, atau membuat mereka mengetahui konten di situs Anda—terutama saat digunakan dengan cara yang menarik.
Sering kali ada dua permasalahan utama saat membangun popover:
- Cara memastikan bahwa konten video ditempatkan di atas konten lainnya di tempat yang sesuai.
- Cara membuatnya mudah diakses (keyboard ramah, dapat difokuskan, dan sebagainya).
Popover API bawaan memiliki berbagai sasaran, semuanya dengan tujuan menyeluruh yang sama, yaitu memudahkan developer membuat pola ini. Sasaran tersebut antara lain:
- Memudahkan untuk menampilkan elemen dan turunannya di atas bagian dokumen lainnya.
- Buat agar mudah diakses.
- Tidak memerlukan JavaScript untuk perilaku paling umum (penutupan cahaya, 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 belakang tanda "Fitur platform web eksperimental" pada saat penulisan.
Untuk mengaktifkan tanda tersebut, buka Chrome Canary dan kunjungi chrome://flags
. Kemudian, aktifkan tanda "Experimental web platform features".
Tersedia juga Uji Coba Origin bagi developer yang ingin mengujinya di lingkungan produksi.
Terakhir, ada polyfill yang sedang dalam pengembangan 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 saat ini dapat Anda lakukan untuk mempromosikan konten Anda dibandingkan dengan konten lainnya? Jika didukung di browser Anda, Anda dapat menggunakan elemen Dialog HTML. Anda harus menggunakannya dalam bentuk "Modal". Dan ini memerlukan JavaScript untuk 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 {i>tool<i} ini cenderung bekerja dengan cara yang serupa.
- Tambahkan beberapa container ke isi untuk menampilkan popover.
- Tata gayanya agar berada di atas elemen lainnya.
- Buat elemen dan tambahkan ke penampung untuk menampilkan popover.
- Sembunyikan dengan menghapus elemen popover dari DOM.
Hal ini memerlukan dependensi tambahan dan lebih banyak keputusan bagi developer. Anda juga perlu riset untuk menemukan penawaran yang menyediakan semua yang Anda butuhkan. Popover API bertujuan untuk mengakomodasi banyak skenario termasuk tooltip. Tujuannya adalah untuk mencakup semua skenario umum tersebut, sehingga developer tidak perlu membuat keputusan lain sehingga mereka dapat fokus membangun pengalaman.
Pop-up pertama Anda
Ini yang Anda perlukan.
<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>
Tapi, apa yang terjadi di sini?
- Anda tidak perlu meletakkan elemen popover ke dalam container atau apa pun—elemen ini tersembunyi secara default.
- Anda tidak perlu menulis JavaScript untuk menampilkannya. Hal tersebut ditangani oleh atribut
popovertoggletarget
. - Saat muncul, lapisan tersebut dipromosikan ke lapisan teratas. Artinya, elemen ini dipromosikan di atas
document
di area pandang. Anda tidak perlu mengelolaz-index
atau khawatir tentang posisi popover di DOM. Fungsi ini dapat ditempatkan jauh di dalam DOM, dengan ancestor penyesuaian nilai. Anda juga bisa melihat elemen mana yang saat ini berada di lapisan atas melalui DevTools. Untuk informasi selengkapnya tentang lapisan atas, baca artikel ini.
- Anda akan melihat "Tutup Lampu" dari kotak. Artinya, Anda dapat menutup pop-up dengan sinyal tutup, seperti mengklik di luar popover, berpindah keyboard ke elemen lain, atau menekan tombol Esc. Buka lagi dan cobalah!
Apa lagi yang Anda dapatkan dari popover? Mari kita ambil contoh ini lebih jauh. Coba lihat demo ini dengan beberapa konten di halaman.
Tombol tindakan mengambang tersebut memiliki pemosisian tetap dengan z-index
yang tinggi.
.fab {
position: fixed;
z-index: 99999;
}
Konten popover disarangkan dalam DOM, namun ketika Anda membukanya, konten akan dipromosikan di atas elemen posisi tetap tersebut. Anda tidak perlu menetapkan gaya apa pun.
Anda mungkin juga melihat bahwa popover tersebut kini memiliki elemen pseudo ::backdrop
. Semua elemen yang ada di lapisan atas mendapatkan elemen pseudo ::backdrop
yang dapat ditata gayanya. Contoh ini menata gaya ::backdrop
dengan warna latar belakang alfa yang lebih sedikit dan filter tampilan latar, yang memburamkan konten pokok.
Menata gaya popover
Mari kita alihkan perhatian untuk menata gaya popover. Secara default, popover memiliki posisi tetap dan beberapa padding yang diterapkan. Halaman ini juga memiliki display: none
. Anda dapat menggantinya untuk menampilkan popover. Tapi, hal itu tidak akan mempromosikannya ke lapisan teratas.
[popover] { display: block; }
Apa pun cara Anda mempromosikan popover, setelah mempromosikan popover ke lapisan atas, Anda mungkin perlu menata atau mengatur posisi popover. Anda tidak dapat menargetkan lapisan atas dan melakukan hal seperti
:open {
display: grid;
place-items: center;
}
Secara default, popover akan diletakkan di tengah area pandang menggunakan margin: auto
. Namun, dalam beberapa kasus, Anda mungkin ingin menjelaskan pemosisian secara eksplisit. Contoh:
[popover] {
top: 50%;
left: 50%;
translate: -50%;
}
Jika Anda ingin menata konten di dalam popover menggunakan petak CSS atau flexbox, sebaiknya menggabungkannya ke dalam elemen. Jika tidak, Anda harus mendeklarasikan aturan terpisah yang mengubah display
setelah popover berada di lapisan atas. Jika disetel secara default, kolom ini akan ditampilkan secara default menggantikan display: none
.
[popover]:open {
display: flex;
}
Jika Anda mencoba demo tersebut, Anda akan melihat bahwa popover sekarang bertransisi masuk dan keluar. Anda dapat mentransisikan pop-up masuk dan keluar menggunakan pemilih pseudo :open
. Pemilih semu :open
cocok dengan popover yang ditampilkan (sehingga 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 di bawah kueri media untuk gerakan. Hal ini juga dapat membantu Anda menjaga pengaturan waktu. Hal ini karena Anda tidak dapat berbagi nilai antara popover
dan ::backdrop
melalui properti khusus.
@media(prefers-reduced-motion: no-preference) {
[popover] { transition: transform 0.2s; }
[popover]::backdrop { transition: opacity 0.2s; }
}
Sampai tahap ini, Anda telah melihat penggunaan popovertoggletarget
untuk menampilkan popover. 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 mengubah tombol beralih 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 tidak hanya mencakup gagasan historis kami tentang pop-up. Anda bisa membangun untuk semua jenis skenario seperti notifikasi, menu, tooltip, dll.
Beberapa skenario tersebut memerlukan pola interaksi yang berbeda. Interaksi seperti mengarahkan kursor. Penggunaan atribut popoverhovertarget
telah bereksperimen, 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 atas dan ke luar elemen yang direspons oleh popover. Perilaku default yang bereksperimen memiliki tampilan popover setelah 0.5s
eksplisit :hover
. Tindakan ini memerlukan tombol tutup ringan atau pembukaan pop-up lain untuk menutupnya (Info selengkapnya tentang hal ini akan segera dibahas). Hal ini disebabkan durasi penyembunyian 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);
});
});
Keuntungan menyetel sesuatu dengan jendela pengarahan kursor eksplisit adalah untuk memastikan tindakan pengguna disengaja (misalnya, pengguna meneruskan kursornya ke sebuah target). Kami tidak ingin menampilkan pop-up kecuali jika mereka menginginkannya.
Coba demo ini untuk mengarahkan kursor ke target dengan jendela ditetapkan ke 0.5s
.
Sebelum menjelajahi beberapa kasus dan contoh penggunaan umum, mari kita bahas beberapa hal.
Jenis popover
Kita telah membahas perilaku interaksi non-JavaScript. Tapi bagaimana dengan perilaku popover secara keseluruhan. Bagaimana jika Anda tidak ingin memilih "Tutup lampu"? Atau Anda ingin menerapkan pola singleton ke popover Anda?
Popover API memungkinkan Anda menentukan tiga jenis popover yang perilakunya berbeda.
[popover=auto]/[popover]
:
- Dukungan bertingkat. Ini tidak hanya berarti bersarang dalam DOM. Definisi popover ancestral adalah:
- yang terkait dengan posisi DOM (turunan).
- yang terkait dengan memicu atribut pada elemen turunan seperti
popovertoggletarget
,popovershowtarget
, dan seterusnya. - yang terkait dengan atribut
anchor
(Dalam pengembangan CSS Anchoring API).
- Tutup ringan.
- Membuka layar akan menutup pop-up lain yang bukan popover leluhur. Cobalah demo di bawah ini yang menyoroti cara kerja {i>nested<i} dengan popover leluhur. Lihat bagaimana mengubah beberapa instance
popoverhidetarget
/popovershowtarget
menjadipopovertoggletarget
akan mengubah banyak hal. - Menutup satu lampu akan menutup semua, tetapi menutup satu di tumpukan hanya akan menutup yang di atasnya dalam tumpukan.
[popover=manual]
:
- Tidak menutup jendela pop-up lainnya.
- Tidak ada penutupan lampu.
- Mewajibkan penutupan eksplisit melalui elemen pemicu atau JavaScript.
JavaScript API
Bila Anda membutuhkan kontrol lebih terhadap popover, Anda dapat melakukan berbagai hal dengan JavaScript. Anda akan mendapatkan metode showPopover
dan hidePopover
. Anda juga memiliki peristiwa popovershow
dan popoverhide
yang dapat diproses:
Menampilkan pop-up
js
popoverElement.showPopover()
Menyembunyikan popover:
popoverElement.hidePopover()
Dengarkan pop-up yang ditampilkan:
popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)
Dengarkan pop-up yang ditampilkan dan batalkan kemunculannya:
popoverElement.addEventListener('popovershow',event => {
event.preventDefault();
console.warn(‘We blocked a popover from being shown’);
})
Dengarkan pop-up 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')
Cara ini memberikan daya tambahan untuk beberapa skenario yang kurang umum. Misalnya, menampilkan popover setelah tidak aktif selama beberapa waktu.
Demo ini memiliki popover dengan pop-up yang dapat didengar, jadi kami memerlukan JavaScript untuk memutar audio. Saat diklik, kita akan menyembunyikan popover, memutar audio, lalu menampilkannya lagi.
Aksesibilitas
Aksesibilitas menjadi prioritas utama dengan Popover API. Pemetaan aksesibilitas mengaitkan popover dengan elemen pemicunya, sesuai kebutuhan. Ini berarti 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. Hal ini sama seperti Dialog, tetapi perbedaannya terletak saat mengembalikan fokus, yaitu karena penutupan ringan. Pada umumnya, menutup popover akan mengembalikan fokus ke elemen yang difokuskan sebelumnya. Namun, fokus dipindahkan ke elemen yang diklik saat penutupan ringan, jika bisa mendapatkan fokus. Lihat bagian tentang pengelolaan fokus di penjelasan.
Anda harus membuka "versi layar penuh" demo ini untuk melihat cara kerjanya.
Dalam demo ini, elemen yang difokuskan mendapatkan garis batas hijau. Coba gunakan tombol {i>tab<i} di sekitar antarmuka dengan keyboard Anda. Perhatikan tempat fokus dikembalikan saat popover ditutup. Anda mungkin juga memperhatikan bahwa jika Anda menekan tombol {i>tab<i}, {i>popover<i} akan tertutup. Itulah desain. Meskipun popover memiliki pengelolaan fokus, popover tidak menjebak fokus. Selain itu, navigasi keyboard mengidentifikasi sinyal tutup saat fokus bergerak keluar dari popover.
Anchoring (dalam pengembangan)
Berbicara tentang popover, pola rumit yang harus dipenuhi adalah menambatkan elemen ke pemicunya. Misalnya, jika tooltip ditetapkan untuk ditampilkan di atas pemicunya, tetapi dokumen akan di-scroll. Tooltip tersebut dapat terpotong oleh area pandang. Saat ini ada penawaran JavaScript untuk menangani hal ini, seperti "Floating UI". Alat tersebut akan memposisikan ulang tooltip agar Anda dapat menghentikan hal ini dan mengandalkan urutan posisi yang diinginkan.
Tapi, kami ingin Anda dapat mendefinisikan ini dengan gaya Anda. Ada API pendamping yang sedang dalam pengembangan bersama dengan Popover API untuk mengatasi hal ini. "CSS Anchor Positioning" API akan memungkinkan Anda melakukan tethering elemen ke elemen lain, dan akan melakukannya dengan cara yang memosisikan ulang elemen agar tidak terpotong oleh area pandang.
Demo ini menggunakan Anchoring API dalam status saat ini. Posisi perahu merespons posisi angkur di area pandang.
Berikut 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. Juga akan ada polyfill untuk API ini.
Contoh
Sekarang Anda telah mengetahui apa yang ditawarkan popover dan bagaimana caranya, mari kita pelajari beberapa contohnya.
Notifikasi
Demo ini menampilkan notifikasi "Salin ke papan klip".
- Menggunakan
[popover=manual]
. - Saat tindakan, tampilkan popover dengan
showPopover
. - Setelah waktu tunggu
2000ms
, sembunyikan denganhidePopover
.
Toast
Demo ini menggunakan lapisan atas untuk menampilkan notifikasi gaya toast.
- Satu pop-up dengan jenis
manual
bertindak sebagai penampung. - Notifikasi baru ditambahkan ke popover dan popover akan ditampilkan.
- Mereka dihapus dengan API animasi web saat diklik dan dihapus dari DOM.
- Jika tidak ada toast untuk 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 keyboard. - Ini adalah kandidat sempurna untuk CSS Anchoring API. Namun, untuk demo ini, Anda dapat menggunakan sejumlah kecil JavaScript 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));
Perlu diingat, karena demo ini menggunakan autofocus
, demo harus dibuka dalam "tampilan layar penuh" untuk navigasi keyboard.
Pop-up media
Demo ini menunjukkan cara memunculkan media.
- Menggunakan
[popover=auto]
untuk menutup ringan. - JavaScript akan memproses peristiwa
play
video dan memunculkan video. - Peristiwa
popoverhide
popover menjeda video.
Popover gaya Wiki
Demo ini menunjukkan cara membuat tooltip konten inline yang berisi media.
- Menggunakan
[popover=auto]
. Menampilkan satu akan menyembunyikan yang lain karena mereka bukan leluhur. - Ditampilkan pada
pointerenter
dengan JavaScript. - Kandidat sempurna lainnya untuk CSS Anchoring API.
Panel Navigasi
Demo ini membuat panel navigasi menggunakan popover.
- Menggunakan
[popover=auto]
untuk menutup ringan. - Menggunakan
autofocus
untuk memfokuskan item navigasi pertama.
Mengelola tampilan latar
Demo ini menunjukkan cara mengelola tampilan latar untuk beberapa popover di mana Anda hanya ingin satu ::backdrop
terlihat.
- Gunakan JavaScript untuk mengelola daftar pop-up 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.
- Promosikan
canvas
ke lapisan atas denganshowPopover
dan[popover=manual]
. - Saat popover lain dibuka, sembunyikan dan tampilkan popover
canvas
untuk memastikan popover berada di atas.
Popover Actionsheet
Demo ini menunjukkan cara menggunakan popover sebagai actionsheet.
- Menampilkan pop-up secara default, menggantikan
display
. - Actionsheet dibuka dengan pemicu popover.
- Saat ditampilkan, popover dipromosikan ke lapisan teratas dan diubah menjadi tampilan.
- Tutup ringan dapat digunakan untuk mengembalikannya.
Pop-up 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. - Tutup lampu menutup palet jika dropdown tidak ada.
- Kandidat lain untuk Anchoring API
Popover berjangka waktu
Demo ini menunjukkan popover tidak aktif setelah empat detik. Pola UI yang sering digunakan dalam aplikasi yang menyimpan informasi aman tentang pengguna untuk menampilkan modal logout.
- Gunakan JavaScript untuk menampilkan pop-up setelah tidak aktif selama jangka waktu tertentu.
- Pada acara pop-up, reset timer.
Screensaver
Mirip dengan demo sebelumnya, Anda dapat menambahkan sedikit sentuhan unik ke situs Anda dan menambahkan screensaver.
- Gunakan JavaScript untuk menampilkan pop-up setelah tidak aktif selama jangka waktu tertentu.
- Tutup lampu untuk menyembunyikan dan mereset timer.
Tanda sisipan
Demo ini menunjukkan cara membuat {i>popover<i} mengikuti tanda sisipan input.
- Menampilkan popover berdasarkan pilihan, peristiwa tombol, atau input karakter khusus.
- Gunakan JavaScript untuk memperbarui posisi popover dengan properti kustom cakupan.
- Pola ini akan memerlukan pemikiran yang cermat terhadap konten yang ditampilkan dan aksesibilitas.
- 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 mengimplementasikan menu tombol tindakan mengambang tanpa JavaScript.
- Promosikan popover jenis
manual
dengan metodeshowPopover
. Ini adalah tombol utama. - Menu adalah popover lain yang merupakan target dari tombol utama.
- Menu dibuka dengan
popovertoggletarget
. - Gunakan
autofocus
untuk memfokuskan item menu pertama yang ditampilkan. - Tombol tutup lampu menutup menu.
- Putar ikon menggunakan
:has()
. Anda dapat membaca selengkapnya tentang:has()
di artikel ini.
Selesai.
Jadi, itulah pengantar untuk popover, yang akan dimulai sebagai bagian dari inisiatif UI Terbuka. Digunakan dengan wajar, ini akan menjadi tambahan yang luar biasa untuk platform web.
Pastikan Anda melihat Open UI. Penjelasan popover selalu diperbarui seiring perkembangan API. Dan berikut adalah koleksi untuk semua demo.
Terima kasih telah “muncul”!
Foto oleh Madison Oren di Unsplash