Masalah yang saya alami adalah bahwa dragleave
peristiwa elemen dipecat ketika melayang elemen anak dari elemen itu. Juga, dragenter
tidak dipecat ketika melayang kembali elemen induk lagi.
Saya membuat biola yang disederhanakan: http://jsfiddle.net/pimvdb/HU6Mk/1/ .
HTML:
<div id="drag" draggable="true">drag me</div>
<hr>
<div id="drop">
drop here
<p>child</p>
parent
</div>
dengan JavaScript berikut:
$('#drop').bind({
dragenter: function() {
$(this).addClass('red');
},
dragleave: function() {
$(this).removeClass('red');
}
});
$('#drag').bind({
dragstart: function(e) {
e.allowedEffect = "copy";
e.setData("text/plain", "test");
}
});
Apa yang seharusnya dilakukan adalah memberi tahu pengguna dengan membuat drop div
red saat menyeret sesuatu ke sana. Ini bekerja, tetapi jika Anda menyeret ke p
anak, dragleave
itu dipecat dan div
tidak merah lagi. Kembali ke drop div
juga tidak membuatnya merah lagi. Sangat penting untuk keluar sepenuhnya dari drop div
dan menyeret kembali ke dalamnya untuk membuatnya merah.
Apakah mungkin untuk mencegah dragleave
menembak ketika menyeret elemen anak?
Pembaruan 2017: TL; DR, Cari CSS pointer-events: none;
seperti dijelaskan dalam jawaban @ HD di bawah ini yang berfungsi di browser modern dan IE11.
dragleave
masih menyala dalam kasus itu.Jawaban:
Anda hanya perlu menyimpan penghitung referensi, menambahkannya ketika Anda mendapatkan dragenter, mengurangi ketika Anda mendapatkan dragleave. Saat penghitung berada di 0 - hapus kelas.
Catatan: Dalam acara drop, setel ulang penghitung ke nol, dan hapus kelas yang ditambahkan.
Anda bisa menjalankannya di sini
sumber
pointer-events: none
anak-anak. Ketika saya mulai menyeret saya menambahkan kelas yang memiliki properti ini, ketika seret saya menghapus kelas. Bekerja dengan baik di Safari dan Chrome, tetapi tidak di Firefox.dragenter
saya simpanevent.currentTarget
dalam variabel barudragEnterTarget
. SelamadragEnterTarget
diatur, saya mengabaikandragenter
acara lebih lanjut , karena mereka dari anak-anak. Dalam semuadragleave
acara saya periksadragEnterTarget === event.target
. Jika ini salah acara akan diabaikan karena dipecat oleh seorang anak. Jika ini benar, saya mengatur ulangdragEnterTarget
keundefined
.Iya.
CSS itu tampaknya cukup untuk Chrome.
Saat menggunakannya dengan Firefox, #drop seharusnya tidak memiliki node teks secara langsung (kalau tidak ada masalah aneh di mana elemen "biarkan sendiri" ), jadi saya sarankan untuk meninggalkannya hanya dengan satu elemen (misalnya, gunakan div di dalam #drop untuk memasukkan semuanya ke dalam)
Inilah jsfiddle yang memecahkan contoh pertanyaan asli (rusak) .
Saya juga telah membuat versi yang disederhanakan bercabang dari contoh @Theodore Brown, tetapi hanya didasarkan pada CSS ini.
Namun, tidak semua browser menerapkan CSS ini: http://caniuse.com/pointer-events
Melihat kode sumber Facebook saya bisa menemukan ini
pointer-events: none;
beberapa kali, namun mungkin digunakan bersama-sama dengan fallback degradasi anggun. Setidaknya itu sangat sederhana dan menyelesaikan masalah untuk banyak lingkungan.sumber
Di sini, solusi Cross-Browser paling sederhana (serius):
jsfiddle <- coba seret beberapa file di dalam kotak
Anda dapat melakukan sesuatu seperti itu:
Dalam beberapa kata, Anda membuat "topeng" di dalam dropzone, dengan lebar & tinggi diwarisi, posisi absolut, yang hanya akan ditampilkan saat dragover dimulai.
Jadi, setelah menunjukkan topeng itu, Anda dapat melakukan trik dengan melampirkan yang lain dragleave & drop peristiwa di atasnya.
Setelah meninggalkan atau menjatuhkan, Anda hanya menyembunyikan topeng lagi.
Sederhana dan tanpa komplikasi.
(Obs .: Saran Greg Pettit - Anda harus yakin bahwa topeng mengarahkan seluruh kotak, termasuk perbatasan)
sumber
Sudah cukup lama setelah pertanyaan ini diajukan dan banyak solusi (termasuk hacks jelek) disediakan.
Saya berhasil memperbaiki masalah yang sama dengan yang baru-baru ini saya terima berkat jawaban dalam jawaban ini dan saya pikir mungkin bermanfaat bagi seseorang yang membuka halaman ini. Idenya adalah untuk menyimpan
evenet.target
diondrageenter
setiap kali dipanggil pada salah satu elemen orang tua atau anak. Kemudianondragleave
periksa apakah target saat ini (event.target
) sama dengan objek tempat Anda menyimpanondragenter
.Satu-satunya kasus keduanya cocok adalah ketika tarik Anda meninggalkan jendela browser.
Alasan bahwa ini berfungsi dengan baik adalah ketika mouse meninggalkan elemen (katakanlah
el1
) dan memasuki elemen lain (katakanlahel2
), pertamael2.ondragenter
disebut dan kemudianel1.ondragleave
. Hanya ketika seret meninggalkan / memasuki jendela browser,event.target
akan berada''
di keduanyael2.ondragenter
danel1.ondragleave
.Ini sampel kerja saya. Saya telah mengujinya di IE9 +, Chrome, Firefox dan Safari.
Dan di sini adalah halaman html sederhana:
Dengan gaya yang tepat yang telah saya lakukan adalah membuat div bagian dalam (# file-drop-area) jauh lebih besar setiap kali file diseret ke layar sehingga pengguna dapat dengan mudah menjatuhkan file ke tempat yang tepat.
sumber
==
lebih===
. Anda membandingkan referensi objek target acara, jadi===
berfungsi dengan baik juga.Cara "benar" untuk menyelesaikan masalah ini adalah untuk menonaktifkan peristiwa pointer pada elemen turunan dari target yang dijatuhkan (seperti dalam jawaban @ HD). Inilah jsFiddle yang saya buat yang menunjukkan teknik ini . Sayangnya, ini tidak berfungsi dalam versi Internet Explorer sebelum IE11, karena mereka tidak mendukung peristiwa pointer .
Untungnya, saya bisa datang dengan solusi yang tidak bekerja di versi lama IE. Pada dasarnya, ini melibatkan mengidentifikasi dan mengabaikan
dragleave
peristiwa yang terjadi ketika menyeret elemen anak. Karenadragenter
acara dipecat pada simpul anak sebelumdragleave
acara pada induknya, pendengar peristiwa terpisah dapat ditambahkan ke setiap simpul anak yang menambah atau menghapus kelas "abaikan-seret-cuti" dari target yang dijatuhkan. Kemudiandragleave
pendengar acara target drop hanya dapat mengabaikan panggilan yang terjadi ketika kelas ini ada. Inilah jsFiddle yang menunjukkan solusi ini . Ini diuji dan bekerja di Chrome, Firefox, dan IE8 +.Memperbarui:
Saya membuat jsFiddle mendemonstrasikan solusi gabungan menggunakan deteksi fitur, di mana peristiwa pointer digunakan jika didukung (saat ini Chrome, Firefox, dan IE11), dan browser kembali ke menambahkan acara ke node anak jika dukungan acara pointer tidak tersedia (IE8 -10).
sumber
dragleave
peristiwa tersebut menembaki Chrome, Firefox, dan IE11 +. Saya juga memperbarui solusi saya yang lain untuk mendukung IE8 dan IE9, selain IE10. Sengaja bahwa efek dropzone hanya ditambahkan ketika menyeret tautan "Seret saya". Orang lain dapat merasa bebas untuk mengubah perilaku ini sebagaimana diperlukan untuk mendukung kasus penggunaannya.Masalahnya adalah bahwa
dragleave
peristiwa tersebut dipecat ketika mouse berjalan di depan elemen anak.Saya sudah mencoba berbagai metode pemeriksaan untuk melihat apakah
e.target
elemen itu sama denganthis
elemen, tetapi tidak bisa mendapatkan perbaikan.Cara saya memperbaiki masalah ini sedikit meretas, tetapi bekerja 100%.
sumber
if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
e.x
dane.y
.jika Anda menggunakan HTML5, Anda bisa mendapatkan clientRect induk:
Kemudian di parent.dragleave ():
ini jsfiddle
sumber
border-radius
, memindahkan pointer ke sudut akan benar - benar meninggalkan elemen, tetapi kode ini masih berpikir kita berada di dalam (kita telah meninggalkan elemen tetapi kita masih dalam persegi panjang yang terikat). Makadragleave
pengendali acara tidak akan dipanggil sama sekali.Solusi yang sangat sederhana adalah dengan menggunakan
pointer-events
properti CSS . Cukup tetapkan nilainya kenone
atas dragstart pada setiap elemen anak. Elemen-elemen ini tidak akan memicu peristiwa yang berhubungan dengan mouse lagi, sehingga mereka tidak akan menangkap mouse di atasnya dan dengan demikian tidak akan memicu dragleave pada induknya.Jangan lupa untuk mengatur properti ini kembali
auto
saat menyelesaikan drag;)sumber
Solusi yang cukup sederhana ini bekerja untuk saya sejauh ini, dengan anggapan acara Anda melekat pada masing-masing elemen seret secara terpisah.
sumber
Solusi yang sangat sederhana:
sumber
Anda dapat memperbaikinya di Firefox dengan sedikit inspirasi dari kode sumber jQuery :
Sayangnya itu tidak berfungsi di Chrome karena
relatedTarget
tampaknya tidak ada padadragleave
acara, dan saya menganggap Anda bekerja di Chrome karena contoh Anda tidak berfungsi di Firefox. Ini adalah versi dengan kode di atas diimplementasikan.sumber
Dan ini dia, solusi untuk Chrome:
sumber
border-radius
seperti yang saya jelaskan di komentar saya pada jawaban @ azlar di atas.Berikut solusi lain menggunakan document.elementFromPoint:
Semoga ini berhasil, ini biola .
sumber
event.clientX, event.clientY
sebagai gantinya, karena mereka relatif terhadap viewport dan bukan halaman. Saya harap itu membantu jiwa yang hilang di luar sana.Solusi sederhana adalah menambahkan aturan css
pointer-events: none
ke komponen anak untuk mencegah pemicuondragleave
. Lihat contoh:sumber
dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
Tidak yakin apakah ini lintas browser, tetapi saya menguji di Chrome dan itu memecahkan masalah saya:
Saya ingin menarik dan melepas file ke seluruh halaman, tetapi dragleave saya diaktifkan ketika saya menyeret elemen anak. Perbaikan saya adalah dengan melihat x dan y dari mouse:
saya memiliki div yang menutupi seluruh halaman saya, ketika halaman memuat saya menyembunyikannya.
ketika Anda seret dokumen saya tunjukkan, dan ketika Anda menjatuhkan orang tua itu menangani itu, dan ketika Anda meninggalkan orang tua saya periksa x dan y.
sumber
Saya telah menulis perpustakaan kecil bernama Dragster untuk menangani masalah ini, bekerja di mana-mana kecuali diam-diam tidak melakukan apa pun di IE (yang tidak mendukung DOM Event Constructor, tetapi akan sangat mudah untuk menulis sesuatu yang serupa menggunakan acara khusus jQuery)
sumber
Saya mengalami masalah yang sama dan mencoba menggunakan solusi pk7s. Ini berhasil tetapi bisa dilakukan sedikit lebih baik tanpa elemen dom tambahan.
Pada dasarnya idenya sama - tambahkan hamparan tak terlihat ekstra di atas area droppable. Hanya lakukan ini tanpa elemen dom tambahan. Inilah bagian yang elemen-elemen pseudo-CSS datang untuk dimainkan.
Javascript
CSS
Aturan setelah ini akan membuat overlay yang tertutup sepenuhnya untuk area yang dapat dijatuhkan.
Inilah solusi lengkapnya: http://jsfiddle.net/F6GDq/8/
Saya harap ini membantu siapa pun dengan masalah yang sama.
sumber
Cukup periksa apakah elemen yang diseret adalah anak, jika ya, maka jangan hapus kelas gaya 'dragover' Anda. Cukup sederhana dan berfungsi untuk saya:
sumber
Saya telah menemukan masalah yang sama dan inilah solusi saya - yang saya pikir jauh lebih mudah daripada di atas. Saya tidak yakin apakah itu crossbrowser (mungkin bergantung pada urutan genap)
Saya akan menggunakan jQuery untuk kesederhanaan, tetapi solusi harus kerangka kerja yang independen.
Acara ini meletup ke orang tua dengan cara demikian:
Kami melampirkan acara
Dan itu saja. :) ini berfungsi karena meskipun onEnter on child fires sebelum onLeave on parent, kami menunda sedikit membalikkan urutan, sehingga kelas dihapus terlebih dahulu kemudian diterapkan kembali setelah satu milidetik.
sumber
Saya menulis modul drag-and-drop yang disebut drip-drop yang memperbaiki perilaku aneh ini, antara lain. Jika Anda mencari modul seret-dan-jatuhkan tingkat rendah yang baik yang dapat Anda gunakan sebagai dasar untuk apa saja (unggah file, seret-dan-lepas dalam aplikasi, seret dari atau ke sumber eksternal), Anda harus memeriksa ini modul keluar:
https://github.com/fresheneesz/drip-drop
Ini adalah bagaimana Anda akan melakukan apa yang Anda coba lakukan di tetes-drop:
Untuk melakukan ini tanpa pustaka, teknik penghitung adalah apa yang saya gunakan dalam drip-drop, sehingga jawaban dengan nilai tertinggi melewatkan langkah-langkah penting yang akan menyebabkan hal-hal rusak untuk semuanya kecuali drop pertama. Inilah cara melakukannya dengan benar:
sumber
Solusi kerja alternatif, sedikit lebih sederhana.
sumber
clientX
atas 0 ketika mouse berada di luar kotak. Memang, elemen saya adalahposition:absolute
Setelah menghabiskan waktu berjam-jam, saya mendapat saran yang bekerja persis seperti yang dimaksudkan. Saya ingin memberikan isyarat hanya ketika file diseret, dan mendokumentasikan dragover, dragleave menyebabkan flicker yang menyakitkan di browser Chrome.
Ini adalah bagaimana saya menyelesaikannya, juga memberikan isyarat yang tepat bagi pengguna.
sumber
Saya memiliki masalah yang sama - kode saya untuk menyembunyikan dropzone pada acara dragleave untuk tubuh dipecat secara kontroversial ketika melayang-layang elemen anak yang membuat dropzone berkedip di Google Chrome.
Saya bisa menyelesaikan ini dengan menjadwalkan fungsi untuk menyembunyikan dropzone alih-alih memanggilnya segera. Kemudian, jika dragover atau dragleave lain dipecat, panggilan fungsi yang dijadwalkan dibatalkan.
sumber
" Dragleave " acara dipecat ketika pointer mouse keluar daerah menyeret wadah sasaran.
Yang membuat banyak akal karena dalam banyak kasus hanya orang tua yang mungkin droppable dan bukan keturunan. Saya pikir event.stopPropogation () seharusnya menangani kasus ini tetapi sepertinya tidak berhasil.
Di atas disebutkan beberapa solusi tampaknya bekerja untuk sebagian besar kasus, tetapi gagal dalam kasus anak-anak yang tidak mendukung acara dragenter / dragleave , seperti iframe .
1 solusinya adalah untuk memeriksa event.relatedTarget dan memverifikasi jika itu berada di dalam wadah kemudian abaikan acara dragleave seperti yang saya lakukan di sini:
Anda dapat menemukan biola yang berfungsi di sini !
sumber
Terpecahkan ..!
Deklarasikan sembarang array untuk contoh:
Tidak perlu khawatir tentang elemen anak.
sumber
Saya berjuang BANYAK dengan ini, bahkan setelah membaca semua jawaban ini, dan berpikir saya mungkin berbagi solusi dengan Anda, karena saya pikir itu mungkin salah satu pendekatan yang lebih sederhana, meskipun agak berbeda. Pikiranku hanya menghilangkan
dragleave
pendengar acara sepenuhnya, dan mengkode perilaku dragleave dengan setiap acara dragenter baru dipecat, sambil memastikan bahwa acara dragenter tidak akan dipecat secara tidak perlu.Dalam contoh saya di bawah ini, saya memiliki tabel, di mana saya ingin dapat bertukar konten baris tabel satu sama lain melalui drag & drop API. Pada
dragenter
, kelas CSS akan ditambahkan ke elemen baris di mana Anda saat ini menyeret elemen Anda, untuk menyorotnya, dan padadragleave
, kelas ini akan dihapus.Contoh:
Tabel HTML yang sangat mendasar:
Dan fungsi dragenter event handler, ditambahkan ke setiap sel tabel (samping
dragstart
,dragover
,drop
, dandragend
penangan, yang tidak spesifik untuk pertanyaan ini, sehingga tidak disalin di sini):Komentar yang sangat mendasar ditinggalkan dalam kode, sehingga kode ini cocok untuk pemula juga. Semoga ini bisa membantu Anda! Perhatikan bahwa Anda tentu saja perlu menambahkan semua pendengar acara yang saya sebutkan di atas ke setiap sel tabel agar ini berfungsi.
sumber
Berikut ini adalah pendekatan lain berdasarkan waktu kejadian.
The
dragenter
event dikirim dari elemen anak dapat ditangkap oleh elemen induk dan selalu terjadi sebelumdragleave
. Pengaturan waktu antara kedua peristiwa ini benar-benar singkat, lebih pendek dari semua aksi mouse manusia yang mungkin. Jadi, idenya adalah untuk menghafal saat ketikadragenter
terjadi dan menyaringdragleave
peristiwa yang terjadi "tidak terlalu cepat" setelah ...Contoh singkat ini berfungsi di Chrome dan Firefox:
sumber
pimvdb ..
Mengapa Anda tidak mencoba menggunakan drop daripada dragleave . Ini berhasil untuk saya. Semoga ini bisa menyelesaikan masalah Anda.
Silakan periksa jsFiddle: http://jsfiddle.net/HU6Mk/118/
sumber
Anda dapat menggunakan batas waktu dengan
transitioning
bendera dan mendengarkan elemen paling atas.dragenter
/dragleave
dari acara anak-anak akan meluap ke wadah.Karena
dragenter
pada elemen anak diaktifkan sebelumdragleave
wadah, kami akan mengatur acara bendera sebagai transisi untuk 1 ms ...dragleave
pendengar akan memeriksa bendera sebelum 1 ms naik.Bendera hanya akan benar selama transisi ke elemen anak, dan tidak akan benar ketika beralih ke elemen induk (dari wadah)
jsfiddle: http://jsfiddle.net/ilovett/U7mJj/
sumber
Anda harus menghapus peristiwa pointer untuk semua objek anak dari target seret.
sumber