'dragleave' elemen induk diaktifkan saat menyeret elemen anak-anak

163

Gambaran

Saya memiliki struktur HTML berikut dan saya telah melampirkan dragenterdan dragleaveacara ke <div id="dropzone">elemen.

<div id="dropzone">
    <div id="dropzone-content">
        <div id="drag-n-drop">
            <div class="text">this is some text</div>
            <div class="text">this is a container with text and images</div>
        </div>
    </div>
</div>

Masalah

Ketika saya menyeret file ke atas <div id="dropzone">, dragenteracara tersebut diaktifkan seperti yang diharapkan. Namun, ketika saya memindahkan mouse saya ke elemen anak, seperti <div id="drag-n-drop">, dragenteracara dipecat untuk <div id="drag-n-drop">elemen dan kemudian dragleaveacara dipecat untuk<div id="dropzone"> elemen.

Jika saya mengarahkan <div id="dropzone">elemen lagi, dragenteracara kembali dipecat, yang keren, tapi kemudian dragleaveacara dipecat untuk elemen anak yang baru saja pergi, jadiremoveClass instruksi dijalankan, yang tidak keren.

Perilaku ini bermasalah karena 2 alasan:

  1. Saya hanya melampirkan dragenter& dragleaveke <div id="dropzone">jadi saya tidak mengerti mengapa elemen anak-anak memiliki acara ini juga terpasang.

  2. Saya masih menyeret <div id="dropzone">elemen sambil melayang-layang di atas anak-anaknya jadi saya tidak ingin dragleavemenembak!

jsFiddle

Berikut adalah jsFiddle untuk bermain-main dengan: http://jsfiddle.net/yYF3S/2/

Pertanyaan

Jadi ... bagaimana saya bisa membuatnya sehingga ketika saya menyeret file di atas <div id="dropzone">elemen, dragleavetidak memecat bahkan jika saya menyeret elemen anak-anak ... itu hanya akan menyala ketika saya meninggalkan <div id="dropzone">elemen .. melayang-layang / menyeret di mana saja dalam batas-batas elemen tidak boleh memicu dragleaveacara.

Saya ingin ini kompatibel lintas-browser, setidaknya di browser yang mendukung HTML5 drag-n-drop, jadi jawaban ini ini tidak memadai.

Sepertinya Google dan Dropbox sudah menemukan ini, tetapi kode sumber mereka diperkecil / rumit jadi saya belum bisa mengetahuinya dari penerapannya.

Hristo
sumber
Cegah penyebaran acara melaluie.stopPropagation();
Blender
Saya rasa saya sudah ... periksa pembaruan
Hristo
Bisakah Anda memposting demo di suatu tempat di jsfiddle.net yang dapat dikerjakan orang?
Blender
@Blender ... tentu saja, beri saya beberapa menit!
Hristo
@Blender ... Saya memperbarui pertanyaan saya dengan biola
Hristo

Jawaban:

171

Jika Anda tidak perlu mengikat acara ke elemen turunan, Anda selalu dapat menggunakan properti pointer-events.

.child-elements {
  pointer-events: none;
}
Ben Rudolph
sumber
4
Solusi terbaik! Saya bahkan tidak berpikir bahwa saya bisa menyelesaikan masalah menggunakan css dengan cara yang elegan. Terima kasih banyak!
serg66
1
Oh ya. Ini dia!
mcmlxxxiii
23
Satu-satunya downside ke pendekatan ini adalah bahwa itu nukes semua peristiwa pointer pada elemen anak (misalnya mereka tidak bisa lagi memiliki :hovergaya terpisah atau klik penangan acara). Jika Anda ingin melestarikan acara-acara tersebut, berikut solusi lain yang telah saya gunakan: bensmithett.github.io/dragster
Ben
2
Gabungkan dengan pemilih anak css untuk menjangkau semua anak di dropzone Anda: .dropzone * {pointer-events: none;}
Philipp F
7
Anda sudah menggunakan jQuery, jadi hanya $('.element').addClass('dragging-over')ke elemen yang Anda seret, dan $('.element').removeClass('dragging-over')ketika Anda seret. Maka dalam CSS Anda, Anda dapat memiliki .element.dragging-over * { pointer-events: none; }. Itu menghapus peristiwa pointer dari semua elemen anak hanya ketika Anda menyeret.
Gavin
56

Saya akhirnya menemukan solusi yang saya sukai. Saya benar-benar menemukan beberapa cara untuk melakukan apa yang saya inginkan tetapi tidak ada yang sesukses solusi saat ini ... dalam satu solusi, saya sering mengalami kerlipan sebagai akibat dari menambah / menghapus batas ke#dropzone elemen ... di lain, perbatasan tidak pernah dihapus jika Anda menjauh dari browser.

Bagaimanapun, solusi hacky terbaik saya adalah ini:

var dragging = 0;

attachEvent(window, 'dragenter', function(event) {

    dragging++;
    $(dropzone).addClass('drag-n-drop-hover');

    event.stopPropagation();
    event.preventDefault();
    return false;
});

attachEvent(window, 'dragover', function(event) {

    $(dropzone).addClass('drag-n-drop-hover');

    event.stopPropagation();
    event.preventDefault();
    return false;
});

attachEvent(window, 'dragleave', function(event) {

    dragging--;
    if (dragging === 0) {
        $(dropzone).removeClass('drag-n-drop-hover');
    }

    event.stopPropagation();
    event.preventDefault();
    return false;
});

Ini bekerja cukup baik tetapi masalah muncul di Firefox karena Firefox adalah permintaan ganda dragenter sehingga counter saya tidak aktif. Namun demikian, ini bukan solusi yang sangat elegan.

Lalu saya menemukan pertanyaan ini: Bagaimana mendeteksi acara dragleave di Firefox ketika menyeret keluar jendela

Jadi saya mengambil jawabannya dan menerapkannya pada situasi saya:

$.fn.dndhover = function(options) {

    return this.each(function() {

        var self = $(this);
        var collection = $();

        self.on('dragenter', function(event) {
            if (collection.size() === 0) {
                self.trigger('dndHoverStart');
            }
            collection = collection.add(event.target);
        });

        self.on('dragleave', function(event) {
            /*
             * Firefox 3.6 fires the dragleave event on the previous element
             * before firing dragenter on the next one so we introduce a delay
             */
            setTimeout(function() {
                collection = collection.not(event.target);
                if (collection.size() === 0) {
                    self.trigger('dndHoverEnd');
                }
            }, 1);
        });
    });
};

$('#dropzone').dndhover().on({
    'dndHoverStart': function(event) {

        $('#dropzone').addClass('drag-n-drop-hover');

        event.stopPropagation();
        event.preventDefault();
        return false;
    },
    'dndHoverEnd': function(event) {

        $('#dropzone').removeClass('drag-n-drop-hover');

        event.stopPropagation();
        event.preventDefault();
        return false;
    }
});

Ini bersih dan elegan dan tampaknya berfungsi di setiap browser yang saya uji sejauh ini (belum menguji IE).

Hristo
sumber
Ini adalah solusi terbaik sejauh ini. Satu-satunya masalah adalah kode ini tidak berfungsi ketika Anda menambahkan file, menghapusnya dan menarik file lain ke atasnya. Hanya setelah menaikkan HoverEnd dan menaikkan HoverStart lagi kode ini berfungsi lagi.
MysticEarth
@MysticEarth dapatkah Anda mengumpulkan jsFiddle untuk demo saat tidak berhasil?
Hristo
1
Terima kasih atas jawaban Anda. Ini sangat membantu, karena antarmuka HTML5 drag and drop dan pendengar acara bisa sangat menyakitkan. Ini adalah cara terbaik untuk menangani acara gila, terutama ketika Anda ingin menangani banyak contoh elemen zona drop.
Nico O
Mengapa kita perlu memanggil stopPropagation () preventDefault () dan mengembalikan false. Return false harus cukup. Haruskah itu?
Dejo
Itu berlebihan, tetapi memanggil stopPropagation()dan preventDefault()lebih disukai. Saya akan menjatuhkan return false.
Peeja
38

Ini sedikit jelek tapi berhasil, sial! ...

Pada penangan 'dragenter' Anda simpan event.target (dalam variabel di dalam penutupan Anda, atau apa pun), maka di penangan 'dragleave' Anda hanya memecat kode Anda jika event.target === yang Anda simpan.

Jika 'dragenter' Anda menembak ketika Anda tidak menginginkannya (yaitu ketika ia masuk setelah meninggalkan elemen anak), maka terakhir kali ia menyala sebelum mouse meninggalkan induk, itu pada induknya, sehingga induknya akan selalu 'dragenter' terakhir sebelum 'dragleave' yang dimaksud.

(function () {

    var droppable = $('#droppable'),
        lastenter;

    droppable.on("dragenter", function (event) {
        lastenter = event.target;
        droppable.addClass("drag-over");            
    });

    droppable.on("dragleave", function (event) {
        if (lastenter === event.target) {
            droppable.removeClass("drag-over");
        }
    });

}());
hacklikecrack
sumber
Saya akan coba ini nanti. Jika ada, itu terlihat lebih bersih daripada solusi saya, tetapi saya tidak bisa memastikan bagaimana browser yang kompatibel hanya dengan melihatnya. Di mana Anda menguji ini?
Hristo
1
hanya Chrome, dan hanya dapat bekerja dengan asumsi bahwa ada kesenjangan fisik antara elemen anak dan wadah.
hacklikecrack
5
Jawaban favorit saya di sini. Sama sekali tidak terasa jelek :)
Matt Way
1
Bekerja untuk saya di IE, Chrome, FF
Vicentiu Bacioiu
1
Ini tidak berfungsi jika hambatan dimulai pada anak-anak. Misalnya, menyeret dari sesuatu dari jendela lain dengan cara seret dimulai di dalam anak.
FINDarkside
29

Pada awalnya, saya setuju dengan orang-orang yang mengabaikan pointer-events: nonependekatan itu. Tapi kemudian saya bertanya pada diri sendiri:

Apakah Anda benar-benar membutuhkan pointer-event untuk bekerja pada elemen anak saat menyeret sedang berlangsung ?

Dalam kasus saya, saya memiliki banyak hal yang terjadi pada anak-anak, misalnya mengarahkan untuk menunjukkan tombol untuk tindakan tambahan, mengedit inline, dll ... Namun, tidak ada yang diperlukan atau bahkan bahkan diinginkan selama drag.

Dalam kasus saya, saya menggunakan sesuatu seperti ini untuk mematikan peristiwa pointer secara selektif untuk semua node anak dari wadah induk:

  div.drag-target-parent-container.dragging-in-progress * {
    pointer-events: none;
  }

Gunakan pendekatan favorit Anda untuk menambah / menghapus kelas dragging-in-progressdi dragEnter/ dragLeaveevent handler, seperti yang saya lakukan atau lakukan hal yang sama dragStart, et. Al.

barbar
sumber
1
Solusi ini adalah yang paling sederhana dan (sejauh yang saya tahu) bukti penuh. Cukup tambahkan kelas di dragstartacara tersebut dan hapus di dragend.
cmann
Ini adalah solusi yang cukup bagus tetapi tidak sempurna. Jika tarik dimulai pada elemen turunan (seret dari jendela lain), dragEnter tidak pernah dipecat. Mungkin juga memungkinkan untuk menggerakkan mouse dengan cukup cepat sehingga dragEnter tidak dipecat ketika Anda pindah ke anak, tetapi tidak 100% yakin tentang hal itu.
FINDarkside
12

Ini sepertinya merupakan bug Chrome.

Satu-satunya solusi yang dapat saya pikirkan adalah membuat elemen overlay transparan untuk menangkap acara Anda: http://jsfiddle.net/yYF3S/10/

JS :

$(document).ready(function() {
    var dropzone = $('#overlay');

    dropzone.on('dragenter', function(event) {
        $('#dropzone-highlight').addClass('dnd-hover');
    });

    dropzone.on('dragleave', function(event) {
        $('#dropzone-highlight').removeClass('dnd-hover');
    });

});​

HTML :

<div id="dropzone-highlight">
    <div id="overlay"></div>

    <div id="dropzone" class="zone">
        <div id="drag-n-drop">
            <div class="text1">this is some text</div>
            <div class="text2">this is a container with text and images</div>
        </div>
    </div>
</div>

<h2 draggable="true">Drag me</h2>
Blender
sumber
terima kasih atas jawabannya, Blender. Saya sudah mencoba ini. Masalah dengan solusi ini adalah bahwa elemen overlay "overlay" elemen anak-anak, yang saya perlu berinteraksi dengan ... sehingga memiliki overlay menonaktifkan interaksi yang saya butuhkan dengan anak-anak ... anggap itu sebagai file drag-n -jatuhkan kotak ... Anda dapat menarik dan melepas atau Anda dapat mengklik elemen input untuk memilih file Anda.
Hristo
inilah contoh biola yang solusi Anda tidak memuaskan ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo
Saya bingung. Mengapa Anda perlu berinteraksi dengan elemen anak juga?
Blender
Dalam kasus khusus saya, elemen turunan dari "dropzone" adalah tombol pilih file ... jadi jika saya tidak ingin menarik dan melepas file, saya dapat memilihnya menggunakan elemen input. Tetapi dengan overlay ... Saya tidak bisa mengklik elemen input. masuk akal?
Hristo
@Blender Sebagian besar kasus, kami harus menyeret file dari desktop, bukan elemen dari halaman, jadi ini sepertinya tidak berfungsi.
Mārtiņš Briedis
5

Masalahnya adalah, bahwa elemen Anda di dalam zona drop tentu saja merupakan bagian dari zona drop dan ketika Anda memasukkan anak-anak, Anda meninggalkan orang tua. Memecahkan ini tidak mudah. Anda dapat mencoba menambahkan acara ke anak-anak juga menambahkan kelas Anda lagi ke orang tua.

$("#dropzone,#dropzone *").on('dragenter', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

Acara Anda masih akan berulang kali tetapi tidak ada yang melihat.

// Edit: Gunakan acara dragmove untuk secara permanen menimpa acara dragleave:

$("#dropzone,#dropzone *").on('dragenter dragover', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

Tetapkan acara dragleave hanya untuk dropzone.

Oliver
sumber
2
itu tidak akan menyelesaikan masalah. Masalah sebenarnya adalah dragleave... ketika saya meninggalkan seorang anak, saya mungkin masih berada di dalam orangtua #dropzonetetapi itu tidak akan memadamkan dragenteracara lagi :(
Hristo
Apakah Anda yakin, bahwa dragenter tidak dipecat lagi?
Oliver
Ah benar ... itu dipecat lagi, namun, setelah dipecat, dragleaveacara ini dipecat untuk elemen anak yang baru saja pergi, jadi sekali lagi removeClassinstruksi dijalankan
Hristo
Saya tidak dapat mendefinisikan dragleavehanya untuk #dropzone... jika saya bisa, saya tidak akan memiliki masalah ini.
Hristo
Tidak, maksud saya jangan menambahkan acara untuk dragleave ke anak-anak.
Oliver
4

Seperti benr disebutkan dalam jawaban ini , Anda dapat mencegah node anak untuk api pada peristiwa, tetapi jika Anda perlu untuk mengikat beberapa peristiwa, lakukan ini:

#dropzone.dragover *{
   pointer-events: none;
}

Dan tambahkan yang ini ke kode JS Anda:

$("#dropzone").on("dragover", function (event) {
   $("#dropzone").addClass("dragover");
});

$("#dropzone").on("dragleave", function (event) {
   $("#dropzone").removeClass("dragover");
});
Vahid Ashrafian
sumber
2
Bagi saya ini menghasilkan overlay yang berkedip terus-menerus.
Andrey Mikhaylov - lolmaus
3

Jika Anda menggunakan jQuery, lihat ini: https://github.com/dancork/jquery.event.dragout

Sungguh luar biasa.

Acara khusus dibuat untuk menangani fungsionalitas dragleave yang sebenarnya.

Acara dragleave HTML5 lebih berfungsi seperti mouseout. Plugin ini dibuat untuk mereplikasi fungsionalitas gaya mouseleave sambil menyeret.

Contoh Penggunaan:

$ ('# myelement'). on ('dragout', function (event) {// YOUR CODE});

EDIT: sebenarnya, saya tidak berpikir itu tergantung pada jQuery, Anda mungkin bisa menggunakan kode bahkan tanpa itu.

nkkollaw
sumber
Wow! Dragout adalah SATU hal yang berfungsi untuk tata letak saya dengan banyak elemen anak-anak diaktifkan dragleavemeskipun saya belum meninggalkan dropzone induk.
Mark Kasson
Setelah berjuang dengan masalah ini selama satu jam terakhir, dragoutadalah satu-satunya solusi yang bekerja untuk saya.
Jason Hazel
2

@ hristo Saya punya solusi yang jauh lebih elegan. Periksa apakah itu sesuatu yang bisa Anda gunakan.

Usaha Anda tidak sia-sia. Saya berhasil menggunakan Anda pada awalnya, tetapi memiliki masalah berbeda di FF, Chrome. Setelah menghabiskan waktu berjam-jam, saya mendapat saran yang bekerja persis seperti yang dimaksudkan.

Begini cara implementasinya. Saya juga memanfaatkan isyarat visual untuk memandu pengguna dengan benar tentang zona penurunan.

$(document).on('dragstart dragenter dragover', function(event) {    
    // Only file drag-n-drops allowed, http://jsfiddle.net/guYWx/16/
    if ($.inArray('Files', event.originalEvent.dataTransfer.types) > -1) {
        // Needed to allow effectAllowed, dropEffect to take effect
        event.stopPropagation();
        // Needed to allow effectAllowed, dropEffect to take effect
        event.preventDefault();

        $('.dropzone').addClass('dropzone-hilight').show();     // Hilight the drop zone
        dropZoneVisible= true;

        // http://www.html5rocks.com/en/tutorials/dnd/basics/
        // http://api.jquery.com/category/events/event-object/
        event.originalEvent.dataTransfer.effectAllowed= 'none';
        event.originalEvent.dataTransfer.dropEffect= 'none';

         // .dropzone .message
        if($(event.target).hasClass('dropzone') || $(event.target).hasClass('message')) {
            event.originalEvent.dataTransfer.effectAllowed= 'copyMove';
            event.originalEvent.dataTransfer.dropEffect= 'move';
        } 
    }
}).on('drop dragleave dragend', function (event) {  
    dropZoneVisible= false;

    clearTimeout(dropZoneTimer);
    dropZoneTimer= setTimeout( function(){
        if( !dropZoneVisible ) {
            $('.dropzone').hide().removeClass('dropzone-hilight'); 
        }
    }, dropZoneHideDelay); // dropZoneHideDelay= 70, but anything above 50 is better
});
kunjungan b
sumber
Anda tampaknya kehilangan definisi untuk beberapa variabel di luar ruang lingkup, saya berasumsivar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen
@TimoHuovinen ya, benar. Keduanya dropZoneHideDelay, dropZoneVisiblediperlukan di dua 'on'acara dokumen dalam implementasi saya.
Visitb
2

Dua sen saya: Sembunyikan lapisan di atas dropzone Anda lalu perlihatkan ketika Anda menyeret, dan targetkan dragleave di atasnya.

Demo: https://jsfiddle.net/t6q4shat/

HTML

<div class="drop-zone">
  <h2 class="drop-here">Drop here</h2>
  <h2 class="drop-now">Drop now!</h2>
  <p>Or <a href="#">browse a file</a></p>
  <div class="drop-layer"></div>
</div>

CSS

.drop-zone{
  padding:50px;
  border:2px dashed #999;
  text-align:center;
  position:relative;
}
.drop-layer{
  display:none;
  position:absolute;
  top:0;
  left:0;
  bottom:0;
  right:0;
  z-index:5;
}
.drop-now{
  display:none;
}

JS

$('.drop-zone').on('dragenter', function(e){
    $('.drop-here').css('display','none');
    $('.drop-now').css('display','block');
    $(this).find('.drop-layer').css('display','block');
    return false;
});

$('.drop-layer').on('dragleave', function(e){
    $('.drop-here').css('display','block');
    $('.drop-now').css('display','none');
    $(this).css('display','none');
    return false;
});
jeremie.b
sumber
Sederhana dan efektif! Saya sedang turun rute stopPropagation () dan saya tidak suka menerapkan event handler untuk semua anak. Tampaknya berantakan tetapi ini berfungsi dengan baik dan sederhana untuk diterapkan. Ide bagus.
Jon Catmull
1

Jadi bagi saya pendekatannya pointer-events: none;tidak bekerja dengan baik ... Jadi, inilah solusi alternatif saya:

    #dropzone {
        position: relative;
    }

    #dropzone(.active)::after {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        content: '';
    }

Dengan cara ini tidak mungkin untuk dragleaveorang tua (pada anak) atau dragoverelemen anak. semoga ini membantu :)

* Kelas '.active' yang saya tambahkan saat saya dragenteratau dragleave. Tetapi jika Anda bekerja tanpa itu tinggalkan saja kelas.

MMachinegun
sumber
1
Saya mencoba ini dalam komponen D & D EmberJS dan menambahkan kelas 'aktif' secara pemrograman terlalu lambat sekitar 40% dari waktu mouse memasuki div induk (tampilan ember). Tidak yakin kenapa. Tetapi meninggalkan itu dan itu bekerja dengan baik, jadi terima kasih atas solusi sederhana ini. Diuji pada Mac pada versi terbaru (per hari ini) Safari, Chrome, Firefox.
rmcsharry
1

Perbaikan cepat super sederhana untuk ini, tidak diuji secara luas tetapi berfungsi di Chrome sekarang.

Maafkan Coffeescript.

  dragEndTimer = no

  document.addEventListener 'dragover', (event) ->
    clearTimeout dragEndTimer
    $('body').addClass 'dragging'
    event.preventDefault()
    return no

  document.addEventListener 'dragenter', (event) ->
    $('section').scrollTop(0)
    clearTimeout dragEndTimer
    $('body').addClass 'dragging'

  document.addEventListener 'dragleave', (event) ->
    dragEndTimer = setTimeout ->
      $('body').removeClass 'dragging'
    , 50

Ini memperbaiki bug flicker Chrome, atau setidaknya permutasi yang menyebabkan saya bermasalah.

Dom Vinyard
sumber
1

Jawaban ini dapat ditemukan di sini:

Dragleave HTML5 dipecat saat melayang elemen anak

var counter = 0;

$('#drop').bind({
    dragenter: function(ev) {
        ev.preventDefault(); // needed for IE
        counter++;
        $(this).addClass('red');
    },

    dragleave: function() {
        counter--;
        if (counter === 0) { 
            $(this).removeClass('red');
        }
    }
});
nol dan satu
sumber
1

Versi saya:

$(".dropzone").bind("dragover", function(e){
    console.log('dragover');
});

$(".dropzone").bind("dragleave", function(e) {
  var stopDrag = false;
  if (!e.relatedTarget) stopDrag = true;
  else {
    var parentDrop = $(e.relatedTarget).parents('.dropzone');
    if (e.relatedTarget != this && !parentDrop.length) stopDrag = true;
  }

  if (stopDrag) {
    console.log('dragleave');
  }
});

Dengan tata letak ini:

<div class="dropzone">
  <div class="inner-zone">Inner-zone</div>
</div>

Aku sudah membuat dump kelas elemen untuk e.target, e.currentTarget, e.relatedTargetuntuk kedua dragoverdandragleave peristiwa.

Itu menunjukkan kepada saya bahwa saat meninggalkan blok induk ( .dropzone) e.relatedTargetbukan anak dari blok ini jadi saya tahu saya keluar dari dropzone.

mortalis
sumber
0

Di sini, salah satu solusi paling sederhana ● ︿ ●

Lihatlah biola ini <- coba seret beberapa file di dalam kotak

Anda dapat melakukan sesuatu seperti ini:

var dropZone= document.getElementById('box');
var dropMask = document.getElementById('drop-mask');


dropZone.addEventListener('dragover', drag_over, false);
dropMask.addEventListener('dragleave', drag_leave, false);
dropMask.addEventListener('drop', drag_drop, false);

Dengan itu Anda harus sudah tahu apa yang terjadi di sini.
Lihatlah biola itu, kau tahu.

Diego T. Yamaguchi
sumber
0

Saya memiliki masalah yang sama dan saya memperbaikinya dengan cara ini:

Masalahnya: Fungsi drop (ev) diaktifkan ketika pengguna menjatuhkan elemen di "drop zone" (elemen ul), tetapi, sayangnya, juga ketika elemen tersebut dijatuhkan di salah satu anak-anaknya (elemen li).

Cara mengatasinya:

function drop(ev) { 
ev.preventDefault(); 
data=ev.dataTransfer.getData('Text'); 
if(ev.target=="[object HTMLLIElement]")  
{ev.target.parentNode.appendChild(document.getElementById(data));}
else{ev.target.appendChild(document.getElementById(data));} 
} 
Pao
sumber
0

Saya mencoba menerapkan ini sendiri untuk kotak unggah file tempat warna kotak akan berubah ketika pengguna menyeret file ke ruang kosong.

Saya menemukan solusi perpaduan yang bagus antara Javascript dan CSS. Katakanlah Anda memiliki zona droppable divdengan id #drop. Tambahkan ini ke Javascript Anda:

$('#drop').on('dragenter', function() {
    $(this).addClass('dragover');
    $(this).children().addClass('inactive');
});

$('#drop').on('dragleave', function() {
    $(this).removeClass('dragover');
    $(this).children().removeClass('inactive');
});

Kemudian, tambahkan ini ke CSS Anda, untuk menonaktifkan semua anak akan kelas .inactive:

#drop *.inactive {
    pointer-events: none;
}

Dengan demikian, elemen anak-anak akan tidak aktif selama pengguna menyeret elemen ke atas kotak.

David A
sumber
0

Saya tidak merasa puas dengan solusi yang disajikan di sini, karena saya tidak ingin kehilangan kendali atas elemen anak-anak.

Jadi saya telah menggunakan pendekatan logika yang berbeda, menerjemahkannya ke dalam plugin jQuery, yang disebut jquery-draghandler . Sama sekali tidak memanipulasi DOM, menjamin kinerja tinggi. Penggunaannya sederhana:

$(document).ready(function() {

    $(selector).draghandler({
        onDragEnter: function() {
            // $(this).doSomething();
        },
        onDragLeave: function() {
            // $(this).doSomethingElse();
        }
    });

});

Ini menangani masalah dengan sempurna tanpa mengorbankan fungsi DOM.

Unduh, detail, dan penjelasan di repositori Git -nya .

Luca Fagioli
sumber
Bagaimana cara mendapatkan acara seret masuk di plugin Anda?
Woody
Saya belum benar-benar menguji arahan Anda, tetapi saya pikir itu tidak berfungsi jika elemen induk (A) tidak benar-benar mengelilingi elemen yang ingin saya periksa (B). Misalnya, jika tidak ada bantalan antara tepi bawah B dan tepi bawah A, maka dragenter tidak akan pernah dipecat untuk elemen A, kan?
marcelj
@marcelj Elemen A bisa menjadi dokumen tubuh itu sendiri, sehingga Anda tidak benar-benar membutuhkan elemen di sekitarnya (tubuh melakukan pekerjaan) dengan padding minimum. Keterbatasan yang saya temukan kemudian dengan pendekatan ini adalah bahwa jika Anda bekerja dengan frames dan elemen Anda berada di perbatasannya. Dalam hal ini, saya tidak menyarankan pendekatan ini.
Luca Fagioli
0

Saya sebenarnya menyukai apa yang saya lihat https://github.com/lolmaus/jquery.dragbetter/tetapi ingin berbagi alternatif yang mungkin. Strategi umum saya adalah menerapkan gaya latar belakang ke dropzone (bukan anak-anaknya) saat menyeretnya atau anak mana pun darinya (melalui menggelegak). Lalu saya menghapus gaya ketika menyeret zona penurunan. Gagasannya adalah ketika pindah ke seorang anak, bahkan jika saya menghapus gaya dari dropzone ketika meninggalkannya (dragleave menyala), saya hanya akan menerapkan kembali gaya ke dropzone induk pada menyeret setiap anak. Masalahnya tentu saja adalah bahwa ketika pindah dari dropzone ke anak dari dropzone, dragenter dipecat pada anak sebelum dragleave, jadi gaya saya diterapkan rusak. Solusi bagi saya adalah dengan menggunakan timer untuk memaksa acara dragenter kembali ke antrian pesan, memungkinkan saya untuk memprosesnya SETELAH dragleave. Saya menggunakan penutupan untuk mengakses acara di callback timer.

$('.dropzone').on('dragenter', function(event) {
  (function (event) {
    setTimeout(function () {
      $(event.target).closest('.dropzone').addClass('highlight');
    }, 0);
  }) (event.originalEvent); 
});

Ini sepertinya bekerja di chrome, yaitu, firefox dan berfungsi terlepas dari jumlah anak di dropzone. Saya sedikit gelisah tentang batas waktu yang menjamin penyeimbangan kembali peristiwa, tetapi tampaknya bekerja cukup baik untuk kasus penggunaan saya.

mefopolis
sumber
0

Saya mencoba menerapkan ini sendiri, dan saya tidak ingin Jquery atau plugin apa pun.

Saya ingin menangani unggahan file, dengan cara berikut yang dianggap terbaik bagi saya:

Struktur File:

--- / uploads {uploads directory}

--- /js/slyupload.js {file javascript.}

--- index.php

--- unggah.php

--- styles.css {hanya sedikit gaya ..}

Kode HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>my Dzone Upload...</title>
<link rel="stylesheet" href="styles.css" />
</head>

<body>
    <div id="uploads"></div>        
    <div class="dropzone" id="dropzone">Drop files here to upload</div>     
    <script type="text/javascript" src="js/slyupload.js"></script>      
</body>
</html>

Maka ini adalah File Javascript terlampir :: 'js / slyupload.js'

<!-- begin snippet:  -->

<!-- language: lang-js -->

    // JavaScript Document

    //ondragover, ondragleave...


    (function(){        

        var dropzone = document.getElementById('dropzone');
        var intv;

        function displayUploads(data){
            //console.log(data, data.length);
            var uploads = document.getElementById('uploads'),anchor,x;

            for(x=0; x < data.length; x++){

                //alert(data[x].file);
                anchor = document.createElement('a');
                anchor.href = data[x].file;
                anchor.innerText = data[x].name;

                uploads.appendChild(anchor);
            }               
        }

        function upload(files){
            //console.log(files);
            var formData = new FormData(), 
                xhr      = new XMLHttpRequest(),    //for ajax calls...
                x;                                  //for the loop..

                for(x=0;x<files.length; x++){
                    formData.append('file[]', files[x]);

                    /*//do this for every file...
                    xhr = new XMLHttpRequest();

                    //open... and send individually..
                    xhr.open('post', 'upload.php');
                    xhr.send(formData);*/
                }

                xhr.onload = function(){
                    var data = JSON.parse(this.responseText);   //whatever comes from our php..
                    //console.log(data);
                    displayUploads(data);

                    //clear the interval when upload completes... 
                    clearInterval(intv);
                }                   

                xhr.onerror = function(){
                    console.log(xhr.status);
                }

                //use this to send all together.. and disable the xhr sending above...

                //open... and send individually..
                intv = setInterval(updateProgress, 50);
                xhr.open('post', 'upload.php');
                xhr.send(formData);

                //update progress... 
                 /* */                   
        }

        function updateProgress(){
            console.log('hello');
         }          

        dropzone.ondrop = function(e){
            e.preventDefault(); //prevent the default behaviour.. of displaying images when dropped...
            this.className = 'dropzone';
            //we can now call the uploading... 
            upload(e.dataTransfer.files); //the event has a data transfer object...
        }

        dropzone.ondragover = function(){
            //console.log('hello');
            this.className = 'dropzone dragover';
            return false;
        }

        dropzone.ondragleave = function(){
            this.className = 'dropzone';
            return false;
        }           
    }(window));

CSS:

body{
	font-family:Arial, Helvetica, sans-serif; 
	font-size:12px;
}

.dropzone{
	width:300px; 
	height:300px;
	border:2px dashed #ccc;
	color:#ccc;
	line-height:300px;
	text-align:center;
}

.dropzone.dragover{
	border-color:#000;
	color:#000;
}

Ande Caleb
sumber
0

Maaf itu javascript bukan jquery, tetapi bagi saya itu adalah cara paling logis untuk menyelesaikannya. Browser harus memanggil dropleave (elemen sebelumnya) sebelum dropenter (atau elemen baru, karena sesuatu tidak dapat memasukkan sesuatu yang lain sebelum meninggalkan hal pertama, saya tidak mengerti mengapa mereka melakukan itu! Jadi Anda hanya perlu menunda de dropleave seperti itu:

function mydropleave(e)
{
    e.preventDefault();
    e.stopPropagation();

    setTimeout(function(e){ //the things you want to do },1);
}

Dan dropenter akan terjadi setelah dropleave, itu saja!

Manfaatkan kesempatan
sumber
-1

Gunakan greedy : trueuntuk anak sebagai fungsi dari droppable. Maka mulailah acara hanya diklik pada lapisan pertama.

Adam
sumber