Menerapkan Objek yang Dapat Diseret
(redblobgames.com)- Diagram interaktif Red Blob Games mengharuskan layar merespons seketika saat pembaca menggerakkan objek secara langsung, sehingga penanganan input disusun berpusat pada Pointer Events agar mouse dan sentuhan bisa ditangani bersama
- Pendekatan lama
mousedown/mouseup/mousemovedapat membuat status drag menjadi kacau saat pointer keluar dari objek atau tombol dilepas di luar area, sehingga perlu pelengkap seperti handler global - Input sentuh memiliki cara pengiriman event yang berbeda dari mouse, dan drag satu jari juga memicu scroll halaman, jadi
preventDefault()dipanggil padatouchstartuntuk mengurangi bentrokan - Resep akhirnya menggabungkan
pointerdown,pointermove,pointerup,pointercancel, dansetPointerCapture(), serta memeriksa tombol kiri dan offset awal agar objek tidak tiba-tiba melompat - Objek yang berisi teks·gambar, multi-touch, drag bertingkat,
<canvas>, kombinasi hover dan drag, serta pengecualian spesifik platform sepertilostpointercapturememerlukan penanganan terpisah di UI nyata
Tujuan handler input drag
- Banyak halaman interaktif memiliki objek yang dapat diseret langsung oleh pembaca, dan diagram harus merespons perubahan posisi objek
- Tujuannya adalah membuat handler event input yang bekerja baik untuk mouse maupun input sentuh
- Alur dasarnya adalah memulai drag di
pointerdown, memperbarui posisi dipointermove, lalu mengakhiri drag dipointerupatau event pembatalan - Dalam implementasi nyata, hal ini menjadi lebih rumit daripada model sederhana karena tombol mouse, sentuhan banyak jari, target pengiriman event, dan menu konteks klik kanan
- Resep dasarnya menggabungkan elemen-elemen berikut
- Di
pointerdown, kembalikan jikaevent.button !== 0agar hanya menangani tombol utama - Ubah koordinat event menjadi koordinat diagram
- Simpan
dx,dy, yaitu selisih antara posisi objek dan posisi pointer saat mulai - Lakukan pointer capture dengan
el.setPointerCapture(event.pointerId) - Di
pointermove, perbaruistate.posdengan menambahkan offset yang disimpan - Di
pointerup,pointercancel, setstate.dragging = null - Panggil
preventDefault()ditouchstart
- Di
- Resep ini adalah handler event input; pemrosesan output sebenarnya harus dihubungkan ke handler status terpisah
- Kode lengkap tersedia di examples page, termasuk contoh drag kiri-kanan pada angka alih-alih memindahkan objek, melukis di kanvas, dan drag terbatas
- Cakupan pengujian meliputi Mac, Windows, Linux, Android, iPhone, dan iPad pada Gecko/Firefox, Blink/Chrome, dan WebKit/Safari
- Stylus yang mendukung hover, perangkat hibrida touch+mouse, dan input suara belum diuji
- Pendekatan ini bukan HTML Drag and Drop API untuk meletakkan elemen di atas elemen lain; pada diagram, event mouse·sentuh harus dibaca langsung
Batasan saat hanya memakai event mouse
- Implementasi awal menggunakan
mousedown,mouseup, danmousemoveyang dipasang langsung pada elemen drag - Sekilas tampak berfungsi, tetapi muncul dua masalah
- Jika pointer digerakkan cepat, pointer tidak lagi berada di atas lingkaran sehingga event tidak diterima
- Jika tombol dilepas di luar lingkaran, status
draggingbisa terjebak
- Perbaikannya adalah menerima
mousedowndi lingkaran, lalu selama drag berlangsung memasangmousemovedanmouseupsementara padadocument - Dengan menghapus handler
mousemovedanmouseupyang dipasang padadocumentsaatmouseup, drag mouse dapat bekerja lebih stabil - Pendekatan ini hanya menangani mouse dan tidak menangani event sentuh
Perbedaan event sentuh
- Input sentuh menggunakan
touchstart,touchend, dantouchmovealih-alihmousedown,mouseup, danmousemove - Setelah
touchstart, event sentuh otomatis ditangkap sehingga semuatouchmovedikirim ke elemen asal - Karena sifat ini, tidak perlu memasang sementara handler event global pada
documentseperti pada event mouse - Saat browser perlu membatalkan rangkaian sentuhan, browser mengirim
touchcancel - Implementasi drag sentuh memanggil
preventDefault()ditouchstart, lalu memperbarui posisi ditouchmovedengan koordinatevent.changedTouches[0] - Pendekatan ini hanya menangani sentuhan dan tidak menangani event mouse
Menyatukan mouse dan sentuhan dengan Pointer Events
- Untuk menangani mouse dan sentuhan sekaligus, dulu dibutuhkan banyak handler event, dan sebelum 2021 pendekatan seperti itu memang digunakan
- Pada 2011~2014, proyek d3 menggunakan d3-drag, dan pada proyek yang tidak memakai d3 dibuat kode mouse+touch terpisah
- Pointer Events menyatukan dan menyederhanakan penanganan mouse dan sentuhan
- MS IE menambahkan dukungan pada 2012
- Chrome menambahkan dukungan pada 2017, Firefox pada 2018, dan Safari pada 2020
- Perilaku browser berubah seiring waktu, dan pada 2017 Chrome mengubah sebagian event ke passive mode secara default, sehingga saat mencoba menyeret objek halaman ikut melakukan scroll
- Safari dan Firefox juga menerapkan perubahan yang sama pada 2018
- Dengan pointer capture, logika dapat dibuat lebih sederhana tanpa perlu menambah dan menghapus handler global
documentseperti pada event mouse
Menjaga pelacakan pointer dengan Pointer Capture
- Pointer capture memungkinkan pelacakan terus berjalan meski pointer keluar dari lingkaran, diagram, atau jendela browser
- Pada event mouse, efek yang sama mengharuskan pemasangan handler event pada
document, tetapi di Pointer EventssetPointerCapture(event.pointerId)jauh lebih sederhana - Tanpa pointer capture, drag bisa berhenti saat diseret dengan cepat atau saat keluar dari diagram lalu masuk lagi
- Jika tombol dilepas di luar diagram atau pengguna berpindah ke jendela lain dengan Alt+Tab, penanganan akhir drag juga berbeda tergantung ada tidaknya capture
- Pada perangkat sentuh, pointer capture adalah perilaku bawaan, tetapi pada mouse harus diatur secara eksplisit
Scroll sentuh dan preventDefault()
- Pada perangkat sentuh, drag satu jari secara default akan memicu scroll halaman
- Jika drag satu jari yang sama juga menggerakkan lingkaran, scroll dan drag objek akan terjadi bersamaan
- Solusi paling sederhana adalah menerapkan CSS
touch-action: nonepada diagram, tetapi dalam kasus ini pengguna tidak bisa melakukan scroll di bagian mana pun pada diagram - Perilaku yang diinginkan adalah mencegah scroll hanya saat menyeret lingkaran, dan tetap mengizinkan scroll saat menyeret diagram itu sendiri
- Hasil perbandingannya sebagai berikut
touch-action: nonepada seluruh diagram: drag lingkaran dan drag diagram sama-sama menghentikan scroll- Nilai default: keduanya tidak dapat mencegah scroll
touch-action: nonehanya pada lingkaran: tidak bekerja seperti yang diharapkanpreventDefault()ditouchstart: hanya mencegah scroll saat drag lingkaran, sambil tetap mengizinkan scroll diagram
preventDefault()dipasang padatouchstart, bukanpointerstart- Pendekatan ini bekerja dalam sebagian besar situasi, tetapi tidak bekerja pada “mobile emulation mode” di Firefox desktop, sementara pada Firefox mobile di Android pendekatan ini berfungsi
Menyimpan offset saat mulai drag
- Saat menyeret dengan memegang tepi objek, pengalaman penggunaan akan buruk jika pusat objek langsung melompat ke posisi pointer
- Solusinya adalah menyimpan posisi relatif antara pusat objek dan posisi pointer saat drag dimulai
- Setelah itu, saat bergerak, posisi objek dihitung dengan menambahkan
dx,dyyang disimpan ke koordinat pointer saat ini - Cara ini mengubah
state.draggingdari sekadartrue/falsemenjadi informasi posisi relatif seperti{dx, dy} - Pada mouse, perbedaan lompatan terlihat jelas saat memegang dari tepi, sedangkan pada sentuhan mungkin kurang terlihat karena tertutup jari
- Detail kecil terkait juga dirangkum di halaman little details
Menu konteks dan tombol mouse
- Cara mengaktifkan menu konteks berbeda di setiap platform
- Windows: klik kanan down+up, Shift+F10
- Linux: tombol kanan down, Shift+F10
- Mac: tombol kanan down, Ctrl+klik kiri
- iOS: long press hanya pada teks
- Android: long press di mana saja
- Masalahnya,
pointerdownterlihat tetapipointeruptidak selalu terlihat dalam beberapa kasus - Tombol sebenarnya mungkin sudah dilepas, tetapi kode masih bisa menganggapnya dalam keadaan ditekan
- Cara menanganinya adalah mengabaikan tombol kanan dan hanya menetapkan status
draggingpada tombol mouse kiri - Pada properti
buttondi spesifikasi Pointer Events,button = 0berarti primary button, dan ini dipakai untuk mengecualikan tombol tengah dan kanan - Di Chrome dan Safari pada Mac, Ctrl+klik bisa masuk seperti
pointerdowntombol kiri sehinggaevent.ctrlKeyjuga perlu diperiksa - Pilihannya adalah sebagai berikut
- Di
contextmenu, menu bisa diblok denganpreventDefault(), tetapi ini dapat berbenturan dengan perilaku default klik tengah atau drag - Jika drag hanya dilakukan dengan tombol kiri, sebagian besar perbedaan klik kanan bisa dihindari
- Jika ingin menghindari juga Ctrl+klik di Mac, maka
ctrlKeyjuga harus dikecualikan
- Di
- Meskipun perubahan tombol tidak dikirim melalui
pointerdownataupointerup, sebagian statusnya bisa diperiksa lewat status tombol padapointermove
Objek yang berisi teks dan gambar
- Jika ada teks atau gambar di dalam elemen yang bisa di-drag, perlu penanganan tambahan
- Saat menyeret lingkaran, teks di dalamnya bisa ikut terseleksi
- Solusinya adalah menerapkan CSS
user-select: none- Jika selalu diterapkan, teks di dalamnya tidak akan pernah bisa dipilih
- Jika hanya diterapkan saat drag berlangsung, pemilihan teks tetap bisa dilakukan dalam penggunaan biasa
- Per awal 2023, Safari masih belum mendukung
user-selecttanpa prefiks sehinggawebkitUserSelectjuga perlu diatur - Windows, Linux, dan Mac mendukung drag and drop antaraplikasi untuk teks dan gambar, dan ini bertabrakan dengan drag objek
- Dengan memanggil
preventDefault()padadragstart, drag objek bisa diprioritaskan dibanding drag teks atau gambar yang dipilih - Perilakunya sedikit berbeda tergantung browser dan sistem operasi
Banyak pointer dan drag simultan
- Apakah beberapa objek bisa di-drag secara bersamaan dengan banyak jari atau banyak mouse adalah edge case
- Pada touch, kode yang ditunjukkan sudah bekerja untuk menyeret beberapa objek sekaligus
- Jika dua jari menyeret objek yang sama, diperlukan penanganan terpisah
- Solusinya adalah menyimpan
event.pointerIdkestate.draggingatau state saatpointerdown, lalu mengabaikannya dipointermovejikapointerId-nya berbeda - Contoh implementasi ini belum diterapkan sepenuhnya di isi utama, tetapi bisa dilihat di canvas dragging test
- Untuk mouse, spesifikasi Pointer Events menyatakan bahwa sistem operasi umum dan user agent biasanya tidak memiliki konsep banyak input mouse
- Meski ada trackpad dan mouse eksternal sekaligus, biasanya tetap diperlakukan sebagai satu pointer mouse, sehingga dinilai tidak ada cara untuk menyeret objek berbeda dengan mouse yang berbeda
Saat beberapa tombol mouse ditekan bersamaan
- Jika beberapa tombol mouse ditekan sekaligus, perilaku Pointer Events dan Mouse Events berbeda
- Mouse Events mengirim
mousedownuntuk setiap penekanan tombol, danmouseupuntuk setiap pelepasan tombol - Menurut spesifikasi Pointer Events,
pointerdownterjadi pada tombol pertama yang ditekan, danpointerupterjadi pada tombol terakhir yang dilepas - Karena itu,
pointerdowndanpointerupbisa terjadi pada status tombol yang berbeda - Misalnya, jika tombol kiri ditekan, lalu tombol kanan ditekan, lalu tombol kiri dilepas, objek bisa tetap berada dalam status drag
- Solusinya adalah memeriksa status tombol kiri dengan
event.buttons & 1dipointermove, lalu memanggilend(event)jika tombol itu tidak lagi ditekan - Pointer capture bisa terus berlangsung sampai semua tombol dilepas, dan dapat tetap berlanjut kecuali capture dilepas secara eksplisit
- Implementasi ini tidak menangani seluruh masalah ini dan semua edge case lain
Elemen drag bertingkat
- Jika ada elemen yang bisa di-drag di dalam elemen lain yang juga bisa di-drag, elemen dalam dan luar sama-sama akan menangani drag
- Solusinya adalah memanggil
event.stopPropagation()pada elemen drag bagian dalam agar event tidak naik ke elemen drag luar - Cara yang dipakai adalah menghentikan propagasi pada
pointerdowndanpointermove - Demonya tidak ada di isi utama, tetapi pada contoh Vue playground, Anda bisa melihat kasus elemen drag merah sebagai anak dari elemen drag kuning
Drag di Canvas
- Biasanya menggunakan SVG, tetapi di
<canvas>Anda tidak bisa menetapkan event handler atau bentuk pointer mouse pada elemen yang dapat diseret itu sendiri - Pada 2D Canvas atau WebGL, event handler harus dipasang ke
<canvas>, lalu Anda harus memeriksa sendiri apakah koordinat berada di atas objek yang dapat diseret - Metode penanganannya sebagai berikut
pointerdown,touchstart,dragstart: jika tidak berada di atas objek yang dapat diseret, segera kembalikanpointermove: atur kursor berdasarkan apakah pointer berada di atas objek yang dapat diseret
- Demonya ada di examples page
Kombinasi Hover mouse dan Drag sentuh
- Beberapa diagram merespons hover tanpa menekan tombol pada mouse, dan pada perangkat sentuh harus merespons dengan drag karena tidak ada hover
- Resep dasarnya mengasumsikan drag untuk mouse maupun sentuh
- Untuk menggunakan hover mouse dan drag sentuh bersama, hapus kondisi
if (!state.dragging)dipointermove - Di Responsive Design page, layout berubah saat baris di-hover dengan mouse, dan pada sentuh layout berubah dengan drag
- Di Hexagons Guide juga banyak diagram yang menggunakan hover mouse dan drag sentuh secara bersamaan
- Saat di sentuh menyeret dari item A ke B, jika capture tetap dipertahankan maka event move akan dikirim ke A, jadi untuk menyorot B mungkin perlu melepas capture
Toggle Paint dan perluasan status drag
- Di Rounded Cell Painter,
pointerdownmenangkap warna cat awal - Setelah itu, semua
pointermovehinggapointerupmenggunakan warna cat yang sama - Untuk itu,
state.draggingharus mencakup bukan hanyax,yawal tetapi juga warna cat awal - Demonya ada di examples page
Kasus tepi lostpointercapture
lostpointercapturedapat digunakan untuk mendeteksi apakah pointer capture telah hilang- Dalam situasi apa ini terjadi dan apa yang harus dilakukan masih belum sepenuhnya dirapikan
- Skenario yang dibahas di komentar adalah sebagai berikut
- Mulai menyeret lingkaran dengan tombol kiri
- Pindah ke luar area drag sambil tetap menahan tombol kiri
- Tekan tombol kanan lalu lepaskan tombol kiri
- Klik di mana saja di luar area drag
- standard menyatakan bahwa setelah pointer capture dilepas, user agent wajib memicu event
lostpointercapture - Perilaku browser nyata sangat beragam; pada beberapa sistem
lostpointercaptureterjadi, tetapi pada sistem lain tidak terjadi sampai mengklik tempat lain
1 komentar
Komentar Hacker News
Ini tulisan tentang drag, dan saya mengalami hampir semua jebakan yang disebut Amit lalu sampai pada solusi yang sama. Tulisan yang bagus
Dari sisi pengguna, mengubah urutan dengan drag terlihat sangat natural, tetapi begitu masuk ke item dengan ukuran berbeda-beda, membuat placeholder di antara item, deteksi tepi, sampai lubang kelinci algoritma penyesuaian kotak, ini termasuk cukup sulit untuk diimplementasikan sendiri
Setiap kali mouse bergerak, pilih posisi yang paling dekat dengan posisi objek yang sedang di-drag saat ini, lalu render layout yang dipilih. Ini bekerja dengan rasa yang baik bahkan pada layout atau reflow yang kompleks
Pada akhirnya, ketika sebuah kartu diangkat, saya menghitung layout seolah-olah kartu itu dihapus dari board, dan itu membuatnya lebih mudah. Tinggal mendorong item-item yang berada di posisi yang diarahkan mouse
Meski begitu, penanganan kasus khususnya sangat banyak, terutama ketika mulai mengedit kartu baru yang belum “di-commit” ke model data. Saya juga harus menambahkan opsi untuk mendorong kartu yang ada agar membuat tempat untuk kartu hantu
Mengakui bahwa ada banyak kasus khusus yang harus ditangani secara manual itu membantu. Jika mendekatinya seolah ada solusi yang lebih umum dan seragam, desainnya akan berputar-putar. Suatu hari saya mungkin harus merangkum berbagai kasus khusus itu dalam tulisan blog
Saya juga menemukan cara layout yang lebih baik, yang mengurangi perhitungan yang tidak perlu dan membuat layout engine lebih fleksibel serta lebih ramah pengguna. Mengejutkan betapa besar perbedaan yang dibuat oleh pilihan representasi model data terhadap solusinya
[1] https://www.ussherpress.com/cardbuddy/
Sepertinya tulisan itu tidak membahas penanganan pembatalan. Misalnya di Windows ada konvensi bahwa menekan Escape membatalkan drag
Kadang kita juga ingin membatalkan drag jika mouse dilepas di luar area yang ditentukan. Pembatalan menjadi cara undo yang lebih cepat bagi pengguna, atau jalan keluar seperti “ah, ternyata saya tidak bermaksud melakukan drag”
Jadi meski tidak ada fitur undo terpisah, kita harus menyimpan status awal pada saat drag dimulai, lalu memulihkannya jika dibatalkan
Untuk pembatalan drag di luar area, ada juga membatalkan pembatalan. Saat pointer berada di luar area, secara visual tampilkan seolah sudah kembali ke keadaan semula, lalu ketika pointer masuk lagi ke dalam area, lanjutkan drag. Pembatalan yang sebenarnya terjadi saat mouse dilepas, tetapi selama drag pun pengguna sudah diberi petunjuk visual bahwa aksi itu akan dibatalkan
Sulit dipercaya Microsoft masih menganggap ini desain UI yang baik. Ini benar-benar membingungkan pengguna; Anda bisa melihat orang takut melepas tombol kiri mouse karena tidak tahu apa yang terjadi, atau mengulanginya seperti yo-yo
Saya setuju bahwa pembatalan adalah masalah besar yang sering terlewatkan, dan tidak sederhana. Pengguna umum juga tidak tahu bahwa ESC bisa membantu
Ada detail tambahan yang saya pelajari selama 20 tahun menyeret berbagai macam objek di GUI Ardour [0]: semua event tombol ditekan dan dilepas harus diperlakukan sebagai event drag tanpa gerakan
Artinya, penekanan selalu memulai drag dan pelepasan mengakhirinya, sementara kode yang menangani pelepasan melakukan perlakuan khusus untuk kondisi tanpa gerakan
[0] https://ardour.org/
Saya punya handler
mousedowndanmouseupbiasa, lalu memakai timeout yang dapat dikonfigurasi sekitar 150ms untuk menentukan apakah itu klik, awal drag, atau tindakan lain. Jikamouseupterjadi atau mouse keluar dari layar dan ada drag yang sedang berlangsung, saya menjalankanstopdrag. Bagi saya, drag hanyalah salah satu dari beberapa kasus khususDahulu sekali di Minneapolis ada sebuah geometry center yang didukung NSF. Ada konferensi teori grup komputasional, dan mereka mengundang beberapa orang dari bidang sekitar sebagai semacam maskot
Saya diundang karena menulis sistem geometri aljabar, lalu bangun sangat pagi dalam cuaca -20°F untuk mendapatkan workstation Silicon Graphics yang akan dipakai hari itu, kemudian mengodekan sebuah gim untuk lebih memahami generator dan relasi grup
Gim itu memakai drag. Hasilnya, percakapan yang berlanjut sampai pukul 2 pagi terasa menyenangkan. Gagasan saya adalah drag tidak harus mengikuti hukum fisika dunia nyata, dan drag seharusnya terasa seperti satu tab LSD yang sangat kuat. Semua orang larut di dalamnya dan masing-masing mengajukan ide
Artikel ini, jika mengesampingkan berbagai pengecualian yang muncul di peramban web, mengimplementasikan drag yang relatif mendasar. Saya penasaran apakah ada materi tentang drag yang memiliki batasan seperti snap ke guideline, mencegah tabrakan dengan batas atau objek lain, atau target drop beranimasi yang ukuran atau posisinya berubah sebagai respons terhadap operasi drag
Dulu saya ingin membuat UI timer Pomodoro kustom yang membagi jam berbentuk lingkaran menjadi potongan-potongan irisan dengan panjang berbeda untuk mendefinisikan interval fokus/istirahat. Implementasi pengubahan urutan drag untuk potongan-potongan irisan itu tidak banyak mengalami kemajuan
Saya ingin memisahkan sistem constraint dari sistem penanganan event. Library seperti jquery-ui mengikat keduanya, sehingga sistem penanganan event harus mengetahui semua constraint yang mungkin. jquery-ui mendukung bounding box, axis, grid persegi, dan snap ke elemen DOM. Namun jika ingin snap ke grid heksagonal, grid berskala log, atau batas melingkar, itu tidak didukung
Di kode terlihat
state.pos = …, dan di situlah state ditetapkan. Untuk constraint,posdisembunyikan di balik setter. Dengan begitu, meski kode penanganan event drag tidak tahu constraint apa yang diperlukan, setter bisa menerapkan constraint tersebutSepertinya saya perlu memperbarui halaman untuk menunjukkan contoh constraint. Saya benar-benar lupa menyebut aspek ini dalam resep kode
Contoh constraint lama ada di https://www.redblobgames.com/articles/curved-paths/making-of..., dan contoh pencegahan tabrakan ada di https://redblobgames.github.io/circular-obstacle-pathfinding.... Namun saya belum mencoba pengubahan urutan drag atau target drop beranimasi
Saya juga berterima kasih pada situs ini. Saat merancang sistem heksagonal untuk analisis geospasial di Uber, saya banyak merujuk ke https://www.redblobgames.com/grids/hexagons/
Ini artikel yang merangkum dengan baik jebakan dan hal-hal yang perlu diperhatikan saat menangani interaksi yang layak
Untuk sisi yang lebih “siap pakai”, saya sudah cukup lama menggunakan interactjs di beberapa proyek
Terima kasih semuanya. Saya terkejut ini muncul di HN hari ini, dan saya bisa menjawab pertanyaan
draggable="true"dan event dragSaya juga penasaran apa kekurangan fitur bawaan itu dibandingkan dengan kode ini
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement...
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement...
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att...
Ada pertanyaan singkat. Saya penasaran bagaimana cara membatasi gerakan drag ke satu axis dengan event DOM bawaan seperti
dragstart. Saya sudah mengimplementasikan fitur drag-and-drop dengan eventdragstart/dragenter/dragover/drop, tetapi belum menemukan cara cepat untuk membatasi gerakan ke sumbu x. Seingat saya API drag-and-drop JQuery mendukung ini. Saya berusaha hanya memakai event/API DOM native, jadi akan senang jika ada informasi atau petunjuk terkaitMengingat Safari baru menambahkan dukungannya pada 2020, saya penasaran apakah Anda tidak khawatir peramban tidak mendukungnya. Pengguna macOS Mojave pun mungkin memakai Safari 13 atau 14, jadi saya berharap Safari 12 secara praktis sudah tidak dipakai lagi, tetapi jika fitur sesederhana drag tidak berjalan di aplikasi produksi yang ditujukan untuk pengguna web umum, itu akan cukup buruk
Menambahkan event handler ke
documentsaat drag adalah praktik lama, dan peramban terus menambahkan fitur baru untuk menyederhanakan use case tertentu, tetapi masing-masing punya pengecualian dan jebakan. Artikel ini juga mengatakan belum membahas semuanya sepenuhnya. Hasil akhirnya pun masih menyisakan kombinasi pointer event dan touch eventSaya penasaran apakah “kesederhanaan” dalam arti berkurangnya kode cukup bernilai untuk menanggung biaya berupa pengecualian tambahan, dukungan peramban yang lebih rendah, serta kebutuhan pengembang untuk memahami detail dan perbedaan peramban pada pointer event yang kemungkinan lebih kurang dipahami dan terdokumentasi dibanding mouse event
Ada satu hal yang terlewat: aksesibilitas. Bagaimana cara membuat drag bisa dilakukan dengan kontrol keyboard?
Dalam contoh saya, jawaban untuk aksesibilitas adalah menyediakan kontrol keyboard tambahan untuk menyelesaikan tugas tersebut. Kalau bisa, sebaiknya dengan cara yang tidak mengharuskan tombol terus ditekan. Misalnya, masuk ke mode panning lewat shortcut, lalu memindahkan viewport di sekitar kanvas dengan tombol panah—itu menyelesaikan masalah
Untuk objek yang bisa di-drag, tugasnya adalah mengubah urutan. Agar ini aksesibel, cukup sediakan cara alternatif untuk mengubah urutan. Misalnya, gunakan TAB untuk memutar fokus di antara objek, pilih objek yang sedang fokus dengan tombol, lalu gunakan tombol panah untuk memindahkan pratinjau drag ke posisi valid terdekat ke arah tersebut
Dengan kata lain, jangan memaksakan dukungan keyboard ke dalam implementasi drag; tambahkan fungsi keyboard yang bermakna secara terpisah dari fitur drag
Bagus. Awal minggu ini saya sedang mengimplementasikan drag elemen SVG di aplikasi Vue, dan saya harus menemukan sendiri hampir semua hal yang dijelaskan penulis dalam urutan yang sama; hasil akhirnya juga menjadi komponen yang hampir sama
Hanya saja, alih-alih komponen fungsional, saya menulisnya sebagai utilitas composable bernama
useDragging