Prompt dan konteks
Reka bentuk bar sisi mudah alih boleh seret. Pengguna desktop menjangkakan Escape akan menutupnya, pengguna Android menjangkakan gerak isyarat atau butang kembali, dan borang yang belum disimpan mesti memerlukan pengesahan. Gunakan CloseWatcher untuk satu aliran penutupan dan terangkan cancel, close, requestClose(), destroy(), fokus, berbilang watcher, pelayar yang tidak disokong, dan sempadan sejarah.
MDN menerangkan CloseWatcher sebagai antara muka untuk menjadikan komponen tersuai bertindak balas terhadap tindakan tutup khusus peranti. HTML Standard juga mentakrifkan pengelompokan close-watcher dan perlindungan terhadap penyalahgunaan tindakan sejarah. Artikel ini mensintesis bahan awam dan tidak mendakwa sebagai soalan temu duga khusus syarikat.
Perkara yang sedang diuji oleh penemu duga
Penemu duga ingin melihat sama ada anda membezakan permintaan penutupan daripada penutupan serta-merta, menghalang penutupan semasa fasa cancel, dan mengekalkan satu pengendali close yang bertanggungjawab untuk pembersihan UI. Jawapan yang mantap menyebut pengaktifan pengguna, pengelompokan berbilang watcher, jangka hayat AbortSignal, pemulangan fokus, dan sandaran butang eksplisit; jawapan yang lemah hanya mendengar keydown.
Soalan untuk dijelaskan terlebih dahulu
- Adakah bar sisi bersifat modal, bukan modal, atau terikat pada keadaan navigasi sejarah?
- Patutkah kandungan yang belum disimpan menyekat setiap permintaan penutupan atau hanya apabila medan tertentu telah diubah (dirty)?
- Patutkah gerak isyarat kembali menutup komponen atau menavigasi ke entri sejarah sebelumnya?
- Adakah pelayar sasaran menyokong CloseWatcher, dan bolehkah ciri tersebut diturunkan kepada butang tutup eksplisit?
Jawapan 30 saat
“Saya akan menormalkan setiap titik masuk penutupan sebagai permintaan penutupan. Apabila bar sisi dibuka, cipta CloseWatcher dengan AbortSignal. Dalam cancel, semak sama ada borang telah diubah; halang permintaan dan tunjukkan pengesahan jika ya, jika tidak benarkannya. close menyembunyikan komponen, memulihkan fokus, dan membersihkan sumber, dan butang tutup eksplisit mengikut laluan yang sama. Pelayar yang tidak disokong mengekalkan butang dan sandaran Escape yang terhad; pelayar atau penghala memiliki navigasi kembali apabila tiada komponen yang boleh ditutup.”
Penyelesaian langkah demi langkah
Asingkan kedua-dua tindakan tersebut. requestClose() mensimulasikan permintaan tutup peranti dan mencetuskan cancel; jika ia tidak dihalang, close menyusul. close() mencetuskan close serta-merta tanpa cancel, manakala destroy() hanya menyahaktifkan watcher. Selepas menyimpan, menyahlekap (unmount), atau meninggalkan laluan, pilih secara eksplisit permintaan penutupan atau pembersihan paksa dan bukannya mencampuradukkan maknanya.
function openDrawer() {
const controller = new AbortController();
const watcher = new CloseWatcher({ signal: controller.signal });
watcher.addEventListener("cancel", (event) => {
if (!formIsDirty()) return;
event.preventDefault();
showDiscardConfirmation(() => watcher.close());
});
watcher.addEventListener("close", () => {
hideDrawer();
restoreFocusToTrigger();
controller.abort();
});
return { watcher, controller };
}Dialog pengesahan tidak sepatutnya mencipta watcher yang tidak boleh ditutup secara rekursif. Selepas pengesahan buang yang eksplisit, panggil close() watcher semasa; membatalkan pengesahan membiarkan bar sisi terbuka. Selepas menyimpan, kosongkan keadaan dirty dan panggil requestClose() supaya laluan pembersihan yang sama dijalankan.
Tingkah laku fokus bergantung pada komponen. Bar sisi modal harus mengalihkan fokus ke tajuk yang mudah difahami atau kawalan pertama dan mengembalikannya ke pencetus semasa ditutup. Panel bukan modal tidak boleh mencuri fokus, tetapi ia masih memerlukan butang tutup yang boleh dicapai dan keadaan yang kelihatan. Permintaan penutupan mesti mengemas kini nama yang boleh diakses, scrim, penguncian tatal, dan susunan papan kekunci, bukan sekadar menogol CSS.
Berbilang watcher mempunyai sempadan khas. Tanpa pengaktifan pengguna, spesifikasi membenarkan watcher dikelompokkan, jadi satu permintaan penutupan boleh menutup beberapa daripadanya. Jangan cipta watcher tanpa syarat untuk setiap panel kecil. Utamakan satu komponen boleh tutup peringkat atas yang memiliki watcher; anak meminta penutupan melalui peristiwa, dan proses unmount memanggil destroy() atau membatalkan isyarat yang berkaitan.
Gerak isyarat kembali bukan sekadar satu klik. Platform mungkin menganggapnya sebagai pelayaran sejarah atau permintaan penutupan, dan pengurus close-watcher pelayar memilih sasarannya. Aplikasi harus mengesahkan hanya apabila ia boleh memintas dan komponen dibuka; tanpa komponen yang boleh ditutup, pengunduran sejarah biasa mesti diteruskan. Jangan sekat popstate atau gerak isyarat kembali secara global untuk melindungi satu borang tempatan.
Pengesanan keupayaan melindungi laluan teras. Semak window.CloseWatcher sebelum membinanya. Apabila tidak disokong, kekalkan butang tutup eksplisit, tambah pendengar Escape yang terhad jika perlu, dan biarkan penghala sedia ada mengendalikan tingkah laku kembali. Jangan mendakwa bahawa pendengar tersuai menghasilkan semula sepenuhnya semantik kembali Android. Ukur sokongan, permintaan yang disekat, pembuangan selepas pengesahan, dan kegagalan pemulihan fokus.
Contoh jawapan yang mantap
Saya akan menormalkan setiap entri tutup bar sisi sebagai permintaan penutupan. Semasa dibuka, cipta CloseWatcher dengan AbortSignal. cancel hanya menyemak keadaan yang belum disimpan: apabila dirty, panggil preventDefault() dan tunjukkan pengesahan; apabila bersih, benarkan permintaan. Selepas pengesahan buang, panggil close(); selepas menyimpan, kosongkan keadaan dirty dan panggil requestClose(). close ialah satu-satunya titik pembersihan UI: sembunyikan panel, pulihkan fokus ke pencetus, alih keluar penguncian tatal, dan tamatkan watcher.
Saya akan mengehadkan bilangan watcher supaya kejadian yang tidak diaktifkan tidak dikelompokkan secara tidak sengaja; unmount atau perubahan laluan memusnahkannya. Komponen modal dan bukan modal mendapat peraturan fokus dan scrim yang berbeza, dan gerak isyarat kembali sampai ke sejarah pelayar apabila tiada komponen dibuka. Pelayar tanpa CloseWatcher mengekalkan butang eksplisit dan sandaran papan kekunci terhad tanpa menyekat navigasi teras. Metrik mengesahkan penutupan, pengesahan, dan tingkah laku kebolehcapaian.
Kesilapan biasa
- Gejala → Menggunakan
close()untuk setiap titik masuk; mengapa ia gagal → Ia melangkau fasa pengesahan belum disimpan; pembetulan → Niat pengguna dan platform menggunakanrequestClose(), manakala pembersihan paksa menggunakanclose(). - Gejala → Hanya mendengar Escape; mengapa ia gagal → Kembali pada Android dan tindakan tutup peranti lain terlepas; pembetulan → Gunakan CloseWatcher dan kekalkan butang eksplisit.
- Gejala → Mencipta watcher untuk setiap panel anak; mengapa ia gagal → Watcher yang tidak diaktifkan mungkin dikelompokkan; pembetulan → Biarkan komponen boleh tutup peringkat atas memiliki satu kejadian.
- Gejala → Membiarkan fokus pada nod tersembunyi; mengapa ia gagal → Pengguna papan kekunci dan teknologi bantuan kehilangan kedudukan mereka; pembetulan → Simpan pencetus dan pulihkan fokus dalam
close. - Gejala → Menyekat peristiwa kembali secara global; mengapa ia gagal → Navigasi sejarah terganggu apabila tiada komponen dibuka; pembetulan → Sekat hanya permintaan penutupan komponen yang terbuka apabila pengesahan diperlukan.
Soalan susulan dan jawapan
Bilakah anda patut menggunakan requestClose() berbanding close()?
Gunakan requestClose() untuk niat pengguna atau platform kerana ia memberi cancel peluang untuk menghalang penutupan. Gunakan close() selepas pengesahan buang eksplisit, semasa pembersihan unmount, atau bila-bila masa penutupan mesti dilakukan serta-merta. Kedua-duanya harus bertumpu pada pengendali pembersihan close yang sama.
Patutkah dialog pengesahan belum disimpan mempunyai CloseWatcher sendiri?
Tidak semestinya. Berikan dialog pengesahan butang tutup eksplisit dan laluan fokus yang boleh diakses untuk mengelakkan rekursi atau penutupan berkelompok dengan watcher induk. Induk memanggil close() selepas pengesahan; menutup anak hanya mengubah keadaan pengesahan.
Bagaimanakah anda mengendalikan beberapa panel yang terbuka?
Takrifkan tindanan atau pemilikan peringkat atas: satu permintaan hanya menutup komponen paling atas yang sebenarnya boleh ditutup, manakala yang lain kekal terbuka. Jangan bergantung pada pengelompokan tersirat watcher yang tidak diaktifkan sebagai tindanan perniagaan anda; rekod susunan dan kembalikan fokus dalam keadaan aplikasi.
Apakah sandaran apabila CloseWatcher tidak disokong?
Kekalkan butang tutup eksplisit dan pengendalian asas Escape, menggunakan semula pengesahan keadaan dirty, pemulihan fokus, dan fungsi pembersihan yang sama. Jangan pintas gerak isyarat kembali atau sejarah secara global. Ukur kohort yang terdegradasi sebelum memutuskan sama ada untuk memperluaskan penambahbaikan.