Pop-up: Mereka muncul kembali!

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 mengelola z-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.

GIF dukungan lapisan atas DevTools sedang didemonstrasikan

  • 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 menjadi popovertoggletarget 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 dengan hidePopover.

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.

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 dengan showPopover 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 dengan autofocus.
  • 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 metode showPopover. 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