masalah pemesanan onclick () dan onblur ()

102

Saya memiliki kolom input yang menampilkan menu drop-down kustom. Saya ingin fungsi berikut:

  • Saat pengguna mengklik di mana saja di luar bidang input, menu harus dihapus.
  • Jika, lebih khusus lagi, pengguna mengklik div di dalam menu, menu tersebut harus dihapus, dan pemrosesan khusus akan terjadi berdasarkan div mana yang diklik.

Inilah implementasi saya:

Bidang masukan memiliki onblur()peristiwa yang menghapus menu (dengan menyetel induknya innerHTMLke string kosong) setiap kali pengguna mengklik di luar bidang masukan. Div di dalam menu juga memiliki onclick()event yang menjalankan pemrosesan khusus.

Masalahnya adalah bahwa onclick()kejadian tidak pernah menyala saat menu diklik, karena bidang masukan onblur()diaktifkan terlebih dahulu dan menghapus menu, termasukonclick() s!

Saya memecahkan masalah dengan membagi menu divs ' onclick()menjadi onmousedown()dan onmouseup()event dan mengatur bendera global pada mouse ke bawah yang dihapus pada mouse ke atas, mirip dengan yang disarankan dalam jawaban ini . Karena onmousedown()diaktifkan sebelumnya onblur(), flag akan disetel onblur()jika salah satu div menu diklik, tetapi tidak jika di tempat lain di layar ada. Jika menu itu diklik, saya langsung kembali dari onblur()tanpa menghapus menu, lalu menunggu onclick()hingga menyala, pada titik mana saya dapat menghapus menu dengan aman.

Apakah ada solusi yang lebih elegan?

Kode tersebut terlihat seperti ini:

<div class="menu" onmousedown="setFlag()" onmouseup="doProcessing()">...</div>
<input id="input" onblur="removeMenu()" ... />

var mouseflag;

function setFlag() {
    mouseflag = true;
}

function removeMenu() {
    if (!mouseflag) {
        document.getElementById('menu').innerHTML = '';
    }
}

function doProcessing(id, name) {
    mouseflag = false;
    ...
}
1 ''
sumber

Jawaban:

168

Saya mengalami masalah yang sama persis dengan Anda, UI saya dirancang persis seperti yang Anda gambarkan. Saya memecahkan masalah hanya dengan mengganti onClickuntuk item menu dengan onMouseDown. Saya tidak melakukan apa-apa lagi; tidak onMouseUp, tidak ada bendera. Ini menyelesaikan masalah dengan mengizinkan browser mengurutkan ulang secara otomatis berdasarkan prioritas penanganan kejadian ini, tanpa pekerjaan tambahan dari saya.

Adakah alasan mengapa ini tidak berhasil juga untuk Anda?

johnbakers
sumber
3
Ini benar-benar berhasil. Saya telah mengujinya di FF dan berfungsi seperti pesona.
Supreme Dolphin
3
Berhasil. Sepertinya onmousedowndijalankan sebelum onblurDiuji di Firefox, Chrome dan Internet Explorer.
Tonatio
11
Perlu dicatat bahwa ini mengubah perilaku sedikit secara alami - interaksi klik kemudian ditangani dengan mouse ke bawah daripada mouse ke atas. Bagi kebanyakan orang itu mungkin baik-baik saja (termasuk dalam kasus ini sendiri) tetapi ada beberapa kekurangan. Terutama saya sering mengklik lalu menarik tombol jika saya salah klik, yang mencegah onclick dipanggil - jika tombol melakukan fungsi non-Murni (hapus, posting dll) Anda mungkin ingin mempertahankan ini dan menggunakan pendekatan bendera.
Brizee
2
Bagaimana dengan aksesibilitas pada saat Anda menyetel mouseDown alih-alih onClick Anda menghentikan aksesibilitas untuk semua elemen <button>: /
ncubica
2
Jawaban yang tercantum di bawah oleh @ ian-macfarlane adalah cara yang benar untuk menangani masalah ini. Singkatnya ... onMouseDowndengan event.preventDefault() dan onClickdengan tindakan yang Anda inginkan.
Conal Trinitrotoluene Da Costa
47

onClick tidak boleh diganti dengan onMouseDown .

Meskipun pendekatan ini berhasil , keduanya adalah peristiwa yang pada dasarnya berbeda yang memiliki harapan berbeda di mata pengguna. Menggunakan, onMouseDownbukanonClick akan merusak prediktabilitas perangkat lunak Anda dalam kasus ini. Jadi, kedua peristiwa itu tidak dapat dipertukarkan.

Sebagai ilustrasi: ketika mengklik tombol secara tidak sengaja, pengguna berharap dapat menahan klik mouse, menyeret kursor ke luar elemen, dan melepaskan tombol mouse, yang pada akhirnya tidak menghasilkan tindakan apa pun. onClickMelakukan hal ini. onMouseDowntidak mengizinkan pengguna untuk menahan mouse, dan sebaliknya akan segera memicu tindakan, tanpa bantuan apa pun bagi pengguna.onClickadalah standar yang kami harapkan untuk memicu tindakan di komputer.

Dalam situasi ini, sebut event.preventDefault()pada onMouseDownacara. onMouseDownakan menyebabkan acara buram secara default, dan tidak akan melakukannya saat preventDefaultdipanggil. Kemudian, onClickakan memiliki kesempatan untuk dipanggil. Peristiwa kabur masih akan terjadi, hanya setelahnyaonClick .

Bagaimanapun, onClickperistiwa tersebut adalah kombinasi dari onMouseDowndan onMouseUp, jika dan hanya jika keduanya terjadi dalam elemen yang sama.

Ian MacFarlane
sumber
7
Ini adalah solusi sempurna untuk saya dan komentar sepenuhnya benar - mengganti onMouseDown membingungkan untuk pengalaman pengguna. Telah memilih.
Paul Bartlett
5
ini seharusnya jawaban yang benar. terima kasih telah memposting ini
pengguna1189352
1
Perlu dicatat bahwa ini juga memiliki beberapa efek samping kecil, misalnya tidak dapat memilih teks dengan mengklik di dalam elemen. Tidak dapat memikirkan terlalu banyak kasus di mana ini akan menjadi masalah, hanya saja rasanya sedikit lucu.
Rei Miyasaka
1
Jika saya gunakan event.preventDefault()di onMouseDownacara, acara saya onFocustidak disebut
Batman
4

Ganti onmousedowndenganonfocus . Jadi acara ini akan dipicu saat fokus ada di dalam kotak teks.

Ganti onmouseupdenganonblur . Saat Anda mengalihkan fokus dari kotak teks, onblur akan dijalankan.

Saya rasa inilah yang mungkin Anda butuhkan.

MEMPERBARUI :

ketika Anda menjalankan fungsi Anda onfocus -> hapus kelas yang akan Anda terapkan di onblur dan tambahkan kelas yang ingin Anda jalankan onfocus

dan

ketika Anda menjalankan fungsi Anda onblur -> hapus kelas yang akan Anda terapkan di onfocus dan tambahkan kelas yang ingin Anda jalankan onblur

Saya tidak melihat adanya kebutuhan akan variabel bendera.

UPDATE 2:

Anda dapat menggunakan acara onmouseout dan onmouseover

onmouseover -Mendeteksi saat kursor berada di atasnya.

onmouseout -Mendeteksi saat kursor pergi.

HIRA THAKUR
sumber
Saya telah mengedit pertanyaan saya untuk kejelasan. Bagaimana solusi ini menghindari kebutuhan untuk menetapkan bendera? Juga, saya menggunakan onmousedown()dan onmouseup()pada menu, bukan kotak teks / kolom input.
1 ''
Saya belum bisa menerima jawaban ini karena saya tidak tahu apakah itu benar. Bisakah Anda menanggapi kekhawatiran yang saya sampaikan dalam komentar saya di atas?
1 ''
Tentu, saya dapat melihat bagaimana Anda dapat menggunakan onmouseover dan onmouseout mirip dengan yang saya lakukan saat ini, untuk menyetel bendera saat mouse berada di atas menu. Namun, saya masih berpikir Anda perlu menyetel bendera di onmouseover yang dihapus di onmouseout, sehingga Anda akan tahu apakah Anda berada di atas menu saat Anda mengklik. Dapatkah Anda memikirkan cara yang tidak memerlukan pengaturan bendera?
1 ''
Dapatkah saya melihat Anda kode ... taruh di biola ... cukup letakkan bagian yang relevan dan benar-benar ok jika saya tidak melihat hasilnya .. hanya kode ..
HIRA THAKUR
mari kita lanjutkan diskusi ini dalam obrolan
HIRA THAKUR
2

Solusi yang lebih elegan (tapi mungkin kurang berkinerja):

Daripada menggunakan onblur input untuk menghapus menu, gunakan document.onclick, yang diaktifkan setelahnya onblur.

Namun, ini juga berarti bahwa menu tersebut dihapus saat input itu sendiri diklik, yang merupakan perilaku yang tidak diinginkan. Setel input.onclickdengan event.stopPropagation()untuk menghindari penyebaran klik ke acara klik dokumen.

1 ''
sumber
5
Nah, masalah dengan jawaban ini, adalah jika bukan peristiwa klik yang akhirnya memicu keburaman. Bagaimana jika pengguna keluar dari input? Itu akan menyebabkan peristiwa blur dipicu tetapi menu flyout akan tetap terlihat.
Christoph
0

ubah onclick dengan onfocus

bahkan jika onblur dan onclick tidak rukun, tapi jelas onfocus dan onblur ya. karena bahkan setelah menu ditutup, onfokus masih berlaku untuk elemen yang diklik di dalam.

Saya melakukannya dan berhasil.

Brasil
sumber
-7

Anda dapat menggunakan setIntervalfungsi di dalam onBlurhandler Anda , seperti ini:

<input id="input" onblur="removeMenu()" ... />

function removeMenu() {
    setInterval(function(){
        if (!mouseflag) {
            document.getElementById('menu').innerHTML = '';
        }
    }, 0);
}

yang setIntervalfungsi akan menghapus Anda onBlur fungsi keluar dari tumpukan panggilan, tambahkan karena Anda set waktu untuk 0, fungsi ini akan segera dipanggil setelah event lainnya selesai

pengguna5271069
sumber
5
setIntervalItu ide yang buruk, Anda tidak pernah membatalkannya sehingga itu akan terus menjalankan fungsi Anda dan kemungkinan besar menyebabkan situs Anda menggunakan max cpu. Gunakan setTimeoutsebagai gantinya jika Anda akan menggunakan teknik ini (yang tidak saya rekomendasikan). Membuat aplikasi Anda bergantung pada waktu peristiwa tertentu adalah bisnis yang berisiko.
casey
6
BAHAYA AKAN ROBINSON! BAHAYA! Kondisi balapan di depan!
GoreDefex
Saya awalnya datang dengan solusi ini (yah setTimeouttidak setInterval) tetapi saya harus meningkatkannya hingga 250ms sebelum waktunya terjadi dalam urutan yang benar. Bug ini mungkin akan tetap ada di perangkat yang lebih lambat dan juga membuat penundaan terlihat. Saya mencoba onMouseDowndan berfungsi dengan baik. Saya ingin tahu apakah itu membutuhkan acara sentuh juga.
Brennan Cheung
Sesuatu seperti interval tidak buruk jika Anda benar-benar ingin menggunakan onClick. Tetapi Anda menginginkan batas waktu rekursif dengan kondisi daripada interval sehingga tidak berjalan selamanya. Ini adalah pola yang sama yang digunakan oleh selenium conditional waits.
Will Cain