'dragleave' ขององค์ประกอบพาเรนต์ยิงเมื่อลากไปวางองค์ประกอบของเด็ก


163

ภาพรวม

ฉันมีโครงสร้าง HTML ต่อไปนี้และฉันได้แนบdragenterและdragleaveกิจกรรมกับ<div id="dropzone">องค์ประกอบ

<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>

ปัญหา

เมื่อฉันลากไฟล์ไป<div id="dropzone">บนdragenterเหตุการณ์จะเริ่มทำงานตามที่คาดไว้ แต่เมื่อฉันเลื่อนเมาส์ของฉันมากกว่าองค์ประกอบลูกเช่น<div id="drag-n-drop">การdragenterจัดกิจกรรมที่เป็นเชื้อเพลิงสำหรับ<div id="drag-n-drop">องค์ประกอบแล้วdragleaveเหตุการณ์ถูกยิงสำหรับ<div id="dropzone">องค์ประกอบ

หากฉันเลื่อนเมาส์ไปที่<div id="dropzone">องค์ประกอบอีกครั้งdragenterเหตุการณ์จะถูกยิงอีกครั้งซึ่งเยี่ยมยอด แต่จากนั้นdragleaveเหตุการณ์จะถูกไล่ออกสำหรับองค์ประกอบลูกเพิ่งออกไปดังนั้นremoveClassคำสั่งจะถูกดำเนินการซึ่งไม่เย็น

พฤติกรรมนี้เป็นปัญหาด้วยเหตุผล 2 ประการ:

  1. ฉันก็แค่ติดdragenterและdragleaveไป<div id="dropzone">ดังนั้นผมจึงไม่เข้าใจว่าทำไมองค์ประกอบเด็กมีเหตุการณ์เหล่านี้ที่แนบมาด้วยเช่นกัน

  2. ฉันยังคงลาก<div id="dropzone">องค์ประกอบขณะโฉบเหนือลูกของมันดังนั้นฉันไม่ต้องการdragleaveยิง!

jsFiddle

นี่คือ jsFiddle เพื่อคนจรจัดด้วย: http://jsfiddle.net/yYF3S/2/

คำถาม

ดังนั้น ... ฉันจะทำให้มันเป็นแบบนั้นได้อย่างไรเมื่อฉันลากไฟล์ไปไว้บน<div id="dropzone">องค์ประกอบdragleaveอย่ายิงแม้ว่าฉันจะลากองค์ประกอบใด ๆ ของเด็ก ... มันควรจะทำงานเมื่อฉันออกจาก<div id="dropzone">องค์ประกอบ .. . การวางเมาส์ / ลากไปรอบ ๆ บริเวณใด ๆ ภายในขอบเขตขององค์ประกอบไม่ควรทำให้เกิดdragleaveเหตุการณ์

ฉันต้องการสิ่งนี้เพื่อให้สามารถทำงานร่วมกับเบราว์เซอร์ข้ามเบราว์เซอร์ที่รองรับ HTML5 drag-n-drop ได้อย่างน้อยดังนั้นคำตอบนี้นี้ไม่เพียงพอ

ดูเหมือนว่า Google และ Dropbox ได้คิดสิ่งนี้ออกมา แต่ซอร์สโค้ดของพวกมันนั้นเล็ก / ซับซ้อนดังนั้นฉันจึงไม่สามารถที่จะเข้าใจสิ่งนี้จากการใช้งานของพวกเขา


ป้องกันการแพร่กระจายเหตุการณ์ผ่านe.stopPropagation();
Blender

ฉันคิดว่าฉันเป็นอยู่แล้ว ... ตรวจสอบอัปเดต
Hristo

คุณสามารถโพสต์ตัวอย่างจากคนที่jsfiddle.netสามารถใช้คนจรจัดได้หรือไม่?
เครื่องปั่น

@ ผู้หักบัญชี ... แน่นอนสิขอให้ฉันสักสองสามนาที!
Hristo

@ ผู้หักบัญชี ... ฉันอัปเดตคำถามของฉันด้วยซอ
Hristo

คำตอบ:


171

หากคุณไม่จำเป็นต้องเชื่อมเหตุการณ์เข้ากับองค์ประกอบลูกคุณสามารถใช้คุณสมบัติตัวชี้เหตุการณ์ได้ตลอดเวลา

.child-elements {
  pointer-events: none;
}

4
ทางออกที่ดีที่สุด! ฉันไม่ได้คิดด้วยซ้ำว่าฉันจะสามารถแก้ปัญหาด้วยการใช้ CSS อย่างสง่างาม ขอบคุณมาก!
serg66

1
โอ้ใช่. นี่ไง!
mcmlxxxiii

23
ข้อเสียเพียงอย่างเดียวของวิธีการนี้คือมันจะทำให้เกิดเหตุการณ์ตัวชี้ทั้งหมดบนองค์ประกอบลูก (เช่นจะไม่มี:hoverลักษณะที่แยกจากกันหรือตัวจัดการเหตุการณ์คลิกคลิก) ในกรณีที่คุณต้องการรักษาเหตุการณ์เหล่านี้นี่เป็นวิธีแก้ปัญหาอื่นที่ฉันใช้อยู่: bensmithett.github.io/dragster
Ben

2
ใช้ร่วมกับตัวเลือกลูก css เพื่อเข้าถึงลูก ๆ ของ dropzone ของคุณ: .dropzone * {pointer-events: none;}
ฟิลิปส์ F

7
คุณใช้ jQuery อยู่แล้วดังนั้น$('.element').addClass('dragging-over')ไปที่องค์ประกอบที่คุณลากไปมาและ$('.element').removeClass('dragging-over')เมื่อคุณลากออก จากนั้นใน CSS .element.dragging-over * { pointer-events: none; }ของคุณคุณสามารถมี ซึ่งจะลบกิจกรรมตัวชี้ออกจากองค์ประกอบลูกทั้งหมดเฉพาะเมื่อคุณลากไป
Gavin

56

ในที่สุดฉันก็พบทางออกที่ฉันมีความสุข ที่จริงฉันพบหลายวิธีที่จะทำสิ่งที่ฉันต้องการ แต่ไม่มีใครประสบความสำเร็จเท่าที่แก้ปัญหาในปัจจุบัน ... ในวิธีการแก้ปัญหาหนึ่งฉันพบกระพริบบ่อยครั้งเป็นผลมาจากการเพิ่ม / ลบชายแดนไป#dropzoneองค์ประกอบ ... ในอีกชายแดน ไม่เคยถูกลบออกหากคุณโฮเวอร์จากเบราว์เซอร์

อย่างไรก็ตามโซลูชันแฮ็คที่ดีที่สุดของฉันคือ:

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;
});

วิธีนี้ใช้งานได้ดี แต่ปัญหาขึ้นมาใน Firefox เพราะ Firefox มีการเรียกซ้ำ dragenterดังนั้นตัวนับของฉันจึงปิด แต่อย่างไรก็ตามมันไม่ได้เป็นทางออกที่หรูหรามาก

จากนั้นฉันก็สะดุดกับคำถามนี้: วิธีการตรวจสอบเหตุการณ์ dragleave ใน Firefox เมื่อลากออกไปนอกหน้าต่าง

ดังนั้นฉันจึงตอบและนำไปใช้กับสถานการณ์ของฉัน:

$.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;
    }
});

นี่คือสะอาดและสง่างามและดูเหมือนว่าจะทำงานในทุกเบราว์เซอร์ที่ฉันได้ทดสอบจนถึงขณะนี้ (ยังไม่ได้ทดสอบ IE)


นี่เป็นทางออกที่ดีที่สุดจนถึงตอนนี้ปัญหาเดียวคือรหัสนี้ใช้ไม่ได้เมื่อคุณเพิ่มไฟล์ลบออกแล้วลากไฟล์อื่นทับทับ หลังจากยก HoverEnd และยก HoverStart อีกครั้งรหัสนี้จะทำงานอีกครั้ง
MysticEarth

@MagicEarth คุณสามารถรวบรวม jsFiddle เพื่อสาธิตเมื่อมันไม่ทำงาน?
Hristo

1
ขอบคุณสำหรับคำตอบ. สิ่งนี้มีประโยชน์จริง ๆ เนื่องจากส่วนต่อประสานการลากและวาง HTML5 และผู้ฟังเหตุการณ์สามารถเจ็บปวดได้จริง ๆ นี่เป็นวิธีที่ดีที่สุดในการจัดการกับเหตุการณ์บ้าคลั่งโดยเฉพาะอย่างยิ่งเมื่อคุณต้องการจัดการกับองค์ประกอบอินสแตนซ์โซนหล่น
Nico O

ทำไมเราต้องเรียก stopPropagation () PreventDefault () และส่งกลับค่าเท็จ ผลตอบแทนที่ผิดควรจะเพียงพอ ควรเป็น ?
Dejo

มันซ้ำซ้อน แต่การโทรstopPropagation()และpreventDefault()เป็นที่ต้องการ ฉันจะreturn falseไป
Peeja

38

นี่มันช่างน่าเกลียดนิดหน่อย แต่ก็ใช้งานได้! ...

ที่ตัวจัดการ 'dragenter' ของคุณจะเก็บ event.target (ในตัวแปรภายในการปิดของคุณหรืออะไรก็ตาม) จากนั้นในตัวจัดการ 'dragleave' ของคุณจะยิงโค้ดของคุณเฉพาะถ้า event.target === อันที่คุณเก็บไว้

หาก 'dragenter' ของคุณเริ่มทำงานเมื่อคุณไม่ต้องการให้มัน (เช่นเมื่อมันเข้ามาหลังจากปล่อยองค์ประกอบย่อย) แล้วครั้งสุดท้ายที่มันจะยิงก่อนที่เมาส์จะออกจากพ่อแม่ก็จะอยู่ในพาเรนต์ดังนั้นผู้ปกครองจะเป็น 'dragenter' สุดท้ายก่อน 'dragleave' ที่ตั้งใจไว้

(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");
        }
    });

}());

ฉันจะลองดูทีหลัง ถ้ามีอะไรมันดูสะอาดกว่าโซลูชันของฉัน แต่ฉันไม่แน่ใจว่ามันเข้ากันได้กับเบราว์เซอร์อย่างไรโดยดูจากมัน คุณทดสอบสิ่งนี้ที่ไหน
Hristo

1
Chrome เท่านั้นและอาจทำงานบนสมมติฐานที่ว่ามีช่องว่างทางกายภาพระหว่างองค์ประกอบย่อยและคอนเทนเนอร์
hacklikecrack

5
คำตอบที่ฉันโปรดปรานที่นี่ ไม่รู้สึกน่าเกลียดเลย :)
แมตต์เวย์

1
ได้ผลสำหรับฉันใน IE, Chrome, FF
Vicentiu Bacioiu

1
สิ่งนี้ใช้ไม่ได้หากการลากเริ่มต้นกับเด็ก ๆ ตัวอย่างเช่นการลากจากบางอย่างจากหน้าต่างอื่นในลักษณะที่การลากเริ่มขึ้นภายในเด็ก
FINDarkside

29

ตอนแรกฉันเห็นด้วยกับคนที่ทิ้งpointer-events: noneแนวทาง แต่ฉันก็ถามตัวเองว่า:

คุณต้องการตัวชี้เหตุการณ์เพื่อทำงานกับองค์ประกอบลูกในขณะที่กำลังลากอยู่หรือไม่

ในกรณีของฉันฉันมีสิ่งต่าง ๆ มากมายที่เกิดขึ้นในเด็ก ๆ เช่นโฮเวอร์เพื่อแสดงปุ่มสำหรับการดำเนินการเพิ่มเติมการแก้ไขแบบอินไลน์ ฯลฯ ... อย่างไรก็ตามไม่มีสิ่งใดที่จำเป็นหรือที่จริงแล้วยังต้องการในระหว่างการลาก

ในกรณีของฉันฉันใช้สิ่งนี้เพื่อปิดตัวชี้เหตุการณ์สำหรับโหนดลูกทั้งหมดของพาเรนต์คอนเทนเนอร์:

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

ใช้วิธีการที่คุณชื่นชอบเพื่อเพิ่ม / ลบคลาสdragging-in-progressในตัวจัดการเหตุการณ์dragEnter/ dragLeaveเช่นที่ฉันทำหรือทำเช่นเดียวกันในdragStart, et อัล


1
วิธีนี้เป็นวิธีที่ง่ายที่สุดและพิสูจน์ได้อย่างเต็มที่ (เท่าที่ฉันสามารถบอกได้) เพียงแค่เพิ่มระดับในเหตุการณ์และลบออกในdragstart dragend
cmann

นี่เป็นทางออกที่ดี แต่ไม่สมบูรณ์ หากการลากเริ่มต้นบนองค์ประกอบลูก (การลากจากหน้าต่างอื่น) dragEnter จะไม่เริ่มทำงาน อาจเป็นไปได้ที่จะเลื่อนเมาส์เร็วพอที่ dragEnter จะไม่ถูกยิงเมื่อคุณย้ายไปที่ลูก แต่ไม่แน่ใจ 100%
FINDarkside

12

นี่น่าจะเป็นบั๊กของ Chrome

วิธีแก้ปัญหาเดียวที่ฉันนึกได้คือการสร้างองค์ประกอบภาพซ้อนทับที่โปร่งใสเพื่อจับภาพกิจกรรมของคุณ: 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>

ขอบคุณสำหรับคำตอบปั่น ฉันได้ลองสิ่งนี้แล้ว ปัญหาของการแก้ปัญหานี้คือการวางซ้อนองค์ประกอบ "ซ้อนทับ" องค์ประกอบของเด็กที่ฉันต้องมีปฏิสัมพันธ์กับ ... ดังนั้นการมีการซ้อนทับปิดการใช้งานการโต้ตอบที่ฉันต้องการกับเด็ก ๆ ... คิดว่ามันเป็นไฟล์ drag-n - ดร็อปบ็อกซ์ ... คุณสามารถลากและวางหรือคุณสามารถคลิกองค์ประกอบอินพุตเพื่อเลือกไฟล์ของคุณ
Hristo

นี่คือตัวอย่างซอที่โซลูชันของคุณไม่พอใจ ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo

ฉันสับสน ทำไมคุณต้องมีปฏิสัมพันธ์กับองค์ประกอบลูกด้วย?
ปั่น

ในกรณีเฉพาะของฉันองค์ประกอบลูกของ "dropzone" เป็นปุ่มเลือกไฟล์ ... ดังนั้นหากฉันไม่ต้องการลากและวางไฟล์ฉันสามารถเลือกได้โดยใช้องค์ประกอบอินพุต แต่ด้วยการซ้อนทับ ... ฉันไม่สามารถคลิกองค์ประกอบอินพุตได้ เหมาะสมไหม
Hristo

@Blender กรณีส่วนใหญ่เราต้องลากไฟล์จากเดสก์ท็อปไม่ใช่องค์ประกอบจากหน้าดังนั้นนี่จึงใช้งานไม่ได้
Mārtiņš Briedis

5

ปัญหาคือองค์ประกอบของคุณในดรอปโซนเป็นส่วนหนึ่งของดรอปโซนและเมื่อคุณป้อนลูกคุณจะออกจากผู้ปกครอง การแก้ปัญหานี้ไม่ใช่เรื่องง่าย คุณอาจลองเพิ่มกิจกรรมให้กับเด็ก ๆ ด้วยเช่นกันเพิ่มชั้นเรียนของคุณอีกครั้งในแม่

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

    // add a class to #dropzone

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

กิจกรรมของคุณจะยังคงยิงหลายครั้ง แต่ไม่มีใครเห็น

// แก้ไข: ใช้เหตุการณ์ dragmove เพื่อเขียนทับเหตุการณ์ dragleave อย่างถาวร:

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

    // add a class to #dropzone

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

กำหนดเหตุการณ์ dragleave สำหรับ dropzone เท่านั้น


2
ที่จะไม่แก้ปัญหาแม้ว่า ปัญหาที่แท้จริงคือdragleave... เมื่อฉันปล่อยให้เด็กฉันยังคงอยู่ในผู้ปกครอง#dropzoneแต่นั่นจะไม่ยิงdragenterเหตุการณ์อีกครั้ง :(
Hristo

คุณแน่ใจหรือไม่ว่า dragenter ไม่ถูกยิงอีกครั้ง
Oliver

อ่า ... มันถูกไล่ออกอีกครั้งอย่างไรก็ตามหลังจากถูกไล่ออกแล้วdragleaveเหตุการณ์จะถูกไล่ออกสำหรับองค์ประกอบย่อยที่เพิ่งออกไปดังนั้นremoveClassคำสั่งจะถูกดำเนินการอีกครั้ง
Hristo

ฉันไม่สามารถนิยามdragleaveเฉพาะสำหรับ#dropzone ... ถ้าทำได้ฉันจะไม่มีปัญหานี้
Hristo

ไม่ฉันหมายถึงไม่ต้องเพิ่มกิจกรรมสำหรับ dragleave ให้กับเด็ก ๆ
Oliver

4

ในฐานะที่เป็นbenrกล่าวถึงในคำตอบนี้คุณสามารถป้องกันโหนดลูกที่จะยิงในเหตุการณ์ แต่ถ้าคุณต้องการที่จะผูกเหตุการณ์บางอย่างที่ทำเช่นนี้:

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

และเพิ่มอันนี้ลงในรหัส JS ของคุณ:

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

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

2
สำหรับฉันแล้วผลลัพธ์นี้ทำให้ภาพซ้อนทับกระพริบอยู่ตลอดเวลา
Andrey Mikhaylov - lolmaus

3

หากคุณใช้ jQuery ให้ลองใช้ดู: https://github.com/dancork/jquery.event.dragout

มันยอดเยี่ยมจริงๆ

กิจกรรมพิเศษที่สร้างขึ้นเพื่อจัดการฟังก์ชั่น dragleave ที่แท้จริง

เหตุการณ์ dragleave HTML5 ทำงานได้เหมือนเมาส์เอาต์ ปลั๊กอินนี้ถูกสร้างขึ้นเพื่อทำซ้ำฟังก์ชั่นสไตล์ mouseleave ในขณะที่ลาก

ตัวอย่างการใช้งาน:

$ ('# myelement'). on ('dragout', ฟังก์ชั่น (เหตุการณ์) {// YOUR CODE});

แก้ไข: ที่จริงฉันไม่คิดว่ามันขึ้นอยู่กับ jQuery คุณอาจจะสามารถใช้รหัสได้แม้ว่าจะไม่มีก็ตาม


ว้าว! Dragout เป็นสิ่งเดียวที่ทำงานกับเลย์เอาต์ของฉันพร้อมองค์ประกอบของเด็ก ๆ มากมายที่ยิงdragleaveแม้ว่าฉันจะไม่ได้ออกจาก dropzone ของผู้ปกครอง
Mark Kasson

หลังจากต่อสู้กับปัญหานี้ในชั่วโมงที่ผ่านมาdragoutเป็นทางออกเดียวที่ได้ผลสำหรับฉัน
Jason Hazel

2

@ Christo ฉันมีทางออกที่สวยงามมากขึ้น ตรวจสอบว่าเป็นสิ่งที่คุณสามารถใช้ได้

ความพยายามของคุณไม่สูญเปล่า ฉันจัดการเพื่อใช้ของคุณในตอนแรก แต่มีปัญหาแตกต่างกันใน FF, Chrome หลังจากใช้เวลาหลายชั่วโมงฉันได้รับคำแนะนำนั้นตรงตามที่ตั้งใจไว้

นี่คือวิธีการใช้งาน ฉันยังใช้ประโยชน์จากการชี้นำแบบเห็นภาพเพื่อแนะนำผู้ใช้อย่างถูกต้องเกี่ยวกับการปล่อยโซน

$(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
});

ดูเหมือนว่าคุณจะขาดคำจำกัดความของตัวแปรขอบเขตบางอย่างฉันคิดว่าvar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen

@TimoHuovinen ใช่ถูกต้อง ทั้งdropZoneHideDelay, dropZoneVisibleสอง'on'เหตุการณ์จำเป็นต้องใช้กับเอกสารสองเหตุการณ์ในการนำไปใช้ของฉัน
visitb

2

My cents สองตัว: ซ่อนเลเยอร์เหนือ dropzone ของคุณแล้วแสดงเมื่อคุณลากและกำหนดเป้าหมาย dragleave

การสาธิต: 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;
});

ง่ายและมีประสิทธิภาพ! ฉันกำลังจะลงเส้นทาง stopPropagation () และฉันไม่ชอบที่จะใช้ตัวจัดการเหตุการณ์กับเด็กทุกคน ดูเหมือนว่าจะยุ่ง แต่ก็ใช้งานได้ดีและใช้งานง่าย ความคิดที่ดี.
Jon Catmull

1

ดังนั้นสำหรับฉันวิธีการpointer-events: none;ทำงานไม่ดีเกินไป ... ดังนั้นนี่คือทางเลือกของฉัน:

    #dropzone {
        position: relative;
    }

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

วิธีนี้มันเป็นไปไม่ได้กับdragleaveผู้ปกครอง (กับเด็ก) หรือdragoverองค์ประกอบลูก หวังว่าจะช่วย :)

* The' .active' ระดับฉันเพิ่มเมื่อฉันหรือdragenter dragleaveแต่ถ้าคุณทำงานโดยที่ไม่ต้องออกจากชั้นเรียน


1
ฉันลองสิ่งนี้ในองค์ประกอบ EmberJS D&D และการเพิ่มคลาส 'แอ็คทีฟ' โดยทางโปรแกรมช้าเกินไปประมาณ 40% ของเวลาที่เมาส์เข้าสู่ div parent (ember-view) ไม่แน่ใจว่าทำไม แต่ทิ้งสิ่งนั้นไว้และใช้งานได้ดีดังนั้นขอขอบคุณสำหรับวิธีแก้ปัญหาง่ายๆนี้ ทดสอบบน Mac ในเวอร์ชันล่าสุด (ณ วันนี้) ของ Safari, Chrome, Firefox
rmcsharry

1

การแก้ไขอย่างรวดเร็วอย่างง่ายสุด ๆ สำหรับสิ่งนี้ไม่ผ่านการทดสอบอย่างกว้างขวาง แต่ใช้งานได้ใน Chrome ทันที

ขอโทษ 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

วิธีนี้แก้ไขข้อผิดพลาดการสั่นไหวของ Chrome หรืออย่างน้อยก็การเปลี่ยนรูปซึ่งทำให้ฉันเกิดปัญหา



1

รุ่นของฉัน:

$(".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');
  }
});

ด้วยรูปแบบนี้:

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

ผมได้ทำการถ่ายโอนข้อมูลของการเรียนองค์ประกอบสำหรับe.target, e.currentTarget, e.relatedTargetสำหรับทั้งสองdragoverและdragleaveเหตุการณ์ที่เกิดขึ้น

มันแสดงให้ฉันเห็นว่าการออกจากบล็อกหลัก ( .dropzone) e.relatedTargetไม่ใช่ลูกของบล็อกนี้ดังนั้นฉันรู้ว่าฉันออกจากดรอปโซน


0

นี่คือหนึ่งในวิธีแก้ปัญหาที่ง่ายที่สุด●︿●

ดูซอนี้<- ลองลากไฟล์บางไฟล์เข้าไปในกล่อง

คุณสามารถทำสิ่งนี้:

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);

โดยที่คุณควรรู้อยู่แล้วว่าเกิดอะไรขึ้นที่นี่
เพียงแค่ดูซอคุณรู้


0

ฉันมีปัญหาที่คล้ายกันและฉันแก้ไขด้วยวิธีนี้:

ปัญหา: การปล่อยฟังก์ชั่น (EV) เกิดขึ้นเมื่อผู้ใช้หยดองค์ประกอบใน "drop zone" (องค์ประกอบ ul) แต่น่าเสียดายที่เมื่อองค์ประกอบถูกทิ้งในหนึ่งในองค์ประกอบย่อย (องค์ประกอบ li)

การแก้ไข:

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));} 
} 

0

ฉันพยายามใช้ตัวเองนี้สำหรับกล่องอัปโหลดไฟล์ซึ่งสีของกล่องจะเปลี่ยนไปเมื่อผู้ใช้ลากไฟล์ไปยังพื้นที่

ฉันพบวิธีแก้ปัญหาที่เป็นการผสมผสานที่ดีของ Javascript และ CSS สมมติว่าคุณมีโซน droppable ด้วยdivมี #dropid เพิ่มใน Javascript ของคุณ:

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

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

จากนั้นเพิ่มสิ่งนี้ใน CSS ของคุณเพื่อปิดใช้งานเด็กทุกคนจะเข้าคลาส.inactive:

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

ดังนั้นองค์ประกอบลูกจะไม่ทำงานตราบใดที่ผู้ใช้ลากองค์ประกอบไปที่กล่อง


0

ฉันไม่รู้สึกพึงพอใจกับวิธีแก้ไขปัญหาใด ๆ ที่นำเสนอที่นี่เพราะฉันไม่ต้องการสูญเสียการควบคุมองค์ประกอบของเด็ก ๆ

ดังนั้นผมจึงได้ใช้วิธีการตรรกะที่แตกต่างกัน, การแปลลงในปลั๊กอิน jQuery เรียกjQuery-draghandler มันไม่ได้จัดการ DOM อย่างแน่นอนรับประกันประสิทธิภาพสูง การใช้งานง่าย:

$(document).ready(function() {

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

});

มันจัดการกับปัญหาอย่างไม่มีที่ติโดยไม่กระทบต่อการทำงานของ DOM

ดาวน์โหลดรายละเอียดและคำอธิบายในตัวพื้นที่เก็บข้อมูล Git


ฉันจะรับเหตุการณ์ลากเข้ามาในปลั๊กอินของคุณได้อย่างไร
วู้ดดี้

ฉันไม่ได้ทดสอบคำสั่งของคุณจริง ๆ แต่ฉันคิดว่ามันไม่ทำงานถ้าองค์ประกอบหลัก (A) ไม่ได้ล้อมรอบองค์ประกอบที่ฉันต้องการตรวจสอบ (B) ตัวอย่างเช่นหากไม่มีช่องว่างภายในระหว่างขอบล่างของ B และขอบล่างของ A ดังนั้น dragenter จะไม่ถูกไล่ออกสำหรับองค์ประกอบ A ใช่ไหม?
marcelj

@marcelj Element Aสามารถเป็นเอกสารร่างกายได้ดังนั้นคุณไม่จำเป็นต้องมีองค์ประกอบโดยรอบ (ร่างกายทำงานได้) โดยมีการเติมขั้นต่ำ ข้อ จำกัด ที่ฉันได้พบในภายหลังด้วยวิธีนี้คือถ้าคุณทำงานกับframes และองค์ประกอบของคุณอยู่ที่เส้นขอบของมัน ในกรณีนี้ฉันไม่แนะนำวิธีการนี้
Luca Fagioli

0

ฉันชอบสิ่งที่เห็นในhttps://github.com/lolmaus/jquery.dragbetter/แต่ต้องการแบ่งปันทางเลือกที่เป็นไปได้ กลยุทธ์ทั่วไปของฉันคือการใช้สไตล์พื้นหลังกับ dropzone (ไม่ใช่ลูก ๆ ) เมื่อลากมันหรือลูก ๆ ของมัน (ผ่านการเดือดปุด ๆ ) จากนั้นฉันก็ลบสไตล์เมื่อ dragle กำลังดร็อปโซน แนวคิดคือตอนที่ย้ายไปอยู่กับเด็กแม้ว่าฉันจะลบสไตล์ออกจากดรอปโซนเมื่อปล่อยไว้ (ไฟ dragleave) ฉันก็จะนำสไตล์ไปใช้กับ Dropzone แม่ในการลากลูก ปัญหาคือแน่นอนว่าเมื่อย้ายจาก dropzone ไปยังลูกของ dropzone dragenter จะถูกไล่ออกบนเด็กก่อน dragleave ดังนั้นสไตล์ของฉันจึงถูกนำไปใช้อย่างไม่เป็นระเบียบ ทางออกสำหรับฉันคือการใช้ตัวจับเวลาเพื่อบังคับให้เหตุการณ์ dragenter กลับไปที่คิวข้อความทำให้ฉันสามารถประมวลผลได้หลังจาก dragleave ฉันใช้การปิดเพื่อเข้าถึงกิจกรรมบนตัวจับเวลาการโทรกลับ

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

ดูเหมือนว่าจะทำงานในโครเมี่ยมเช่น Firefox และทำงานโดยไม่คำนึงถึงจำนวนเด็กใน Dropzone ฉันไม่สบายใจเล็กน้อยเกี่ยวกับการหมดเวลาที่จะรับประกันความต้องการของเหตุการณ์ แต่ดูเหมือนว่าจะทำงานได้ดีสำหรับกรณีการใช้งานของฉัน


0

ฉันพยายามที่จะใช้งานด้วยตัวเองและฉันไม่ต้องการ Jquery หรือปลั๊กอินใด ๆ

ฉันต้องการจัดการกับการอัปโหลดไฟล์ตามวิธีที่ถือว่าดีที่สุดสำหรับฉัน:

โครงสร้างไฟล์:

--- / uploads {ไดเรกทอรีที่อัพโหลด}

--- /js/slyupload.js {ไฟล์ javascript}

--- index.php

--- upload.php

--- styles.css {ใส่สไตล์เล็กน้อย .. }

รหัส 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>

นี่คือไฟล์ Javascript ที่แนบมา :: '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;
}


0

ขออภัย javascript ไม่ใช่ jquery แต่สำหรับฉันแล้วมันเป็นวิธีที่สมเหตุสมผลที่สุดในการแก้ปัญหานี้ เบราว์เซอร์ควรเรียก dropleave (ขององค์ประกอบก่อนหน้า) ก่อนที่ dropenter (ขององค์ประกอบใหม่เนื่องจากสิ่งที่ไม่สามารถป้อนอย่างอื่นก่อนที่จะปล่อยให้สิ่งที่ forst ฉันไม่เข้าใจว่าทำไมพวกเขาทำเช่นนั้น! de dropleave เช่นนั้น:

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

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

และผู้วางจะเกิดขึ้นหลังจากหยดนั่นคือทั้งหมด!


-1

ใช้กับเด็กเป็นหน้าที่ของgreedy : true droppableจากนั้นเริ่มเฉพาะเหตุการณ์ที่คลิกบนเลเยอร์แรก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.