Pop-up: Mereka muncul kembali!

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

GIF dukungan lapisan atas DevTools yang sedang ditunjukkan

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

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.

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