'dragleave' của phần tử cha sẽ kích hoạt khi kéo qua phần tử con


163

Tổng quat

Tôi có cấu trúc HTML sau đây và tôi đã đính kèm dragenterdragleavecác sự kiện vào thành <div id="dropzone">phần.

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

Vấn đề

Khi tôi kéo một tập tin qua <div id="dropzone">, dragentersự kiện sẽ được kích hoạt như mong đợi. Tuy nhiên, khi tôi di chuyển chuột qua một phần tử con, chẳng hạn như <div id="drag-n-drop">, dragentersự kiện được kích hoạt cho <div id="drag-n-drop">phần tử và sau đó dragleavesự kiện được kích hoạt cho <div id="dropzone">phần tử đó.

Nếu tôi di chuột qua <div id="dropzone">phần tử một lần nữa, dragentersự kiện lại được kích hoạt, điều này thật tuyệt, nhưng sau đó dragleavesự kiện được kích hoạt cho phần tử con chỉ còn lại, do đó, removeClasslệnh được thực thi, điều đó không hay.

Hành vi này có vấn đề vì 2 lý do:

  1. Tôi chỉ gắn dragenter& dragleaveđến <div id="dropzone">vì vậy tôi không hiểu tại sao các yếu tố trẻ em có những sự kiện gắn liền là tốt.

  2. Tôi vẫn đang kéo <div id="dropzone">phần tử trong khi di chuột qua các con của nó vì vậy tôi không muốn dragleavebắn!

jsFiddle

Đây là một jsFiddle để tinker với: http://jsfiddle.net/yYF3S/2/

Câu hỏi

Vậy ... làm thế nào tôi có thể làm cho nó sao cho khi tôi kéo tập tin qua <div id="dropzone">phần tử, dragleavekhông kích hoạt ngay cả khi tôi kéo qua bất kỳ phần tử con nào ... nó chỉ nên kích hoạt khi tôi rời <div id="dropzone">phần tử .. . lơ lửng / kéo xung quanh bất cứ nơi nào trong ranh giới của phần tử sẽ không kích hoạt dragleavesự kiện.

Tôi cần điều này để tương thích với nhiều trình duyệt, ít nhất là trong các trình duyệt hỗ trợ kéo thả HTML5, vì vậy câu trả lời này không đầy đủ.

Có vẻ như Google và Dropbox đã tìm ra điều này, nhưng mã nguồn của họ bị thu nhỏ / phức tạp nên tôi không thể tìm ra điều này từ việc triển khai của họ.


Ngăn chặn sự lan truyền sự kiện quae.stopPropagation();
Blender

Tôi nghĩ rằng tôi đã ... kiểm tra cập nhật
Hristo

Bạn có thể đăng một bản demo ở đâu đó trên jsfiddle.net mọi người có thể tinker với nó không?
Máy xay sinh tố

@Blender ... chắc chắn rồi, cho tôi vài phút!
Hristo

@Blender ... Tôi đã cập nhật câu hỏi của mình bằng một câu đố
Hristo

Câu trả lời:


171

Nếu bạn không cần liên kết các sự kiện với các phần tử con, bạn luôn có thể sử dụng thuộc tính sự kiện con trỏ.

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

4
Giải pháp tốt nhất! Tôi thậm chí không nghĩ rằng tôi có thể giải quyết vấn đề bằng cách sử dụng css theo cách thanh lịch như vậy. Cảm ơn rât nhiều!
serg66

1
Ồ, vâng. Đây là nó!
mcmlxxxiii

23
Nhược điểm duy nhất của phương pháp này là nó ngăn chặn tất cả các sự kiện con trỏ trên các phần tử con (ví dụ: chúng không còn có :hoverkiểu riêng hoặc trình xử lý sự kiện nhấp chuột). Trong trường hợp bạn muốn lưu giữ những sự kiện đó, đây là một cách giải quyết khác mà tôi đã sử dụng: bensmithett.github.io/dragster
Ben

2
Kết hợp với bộ chọn con css để tiếp cận tất cả trẻ em trong dropzone của bạn: .dropzone * {pointer-events: none;}
Philipp F

7
Bạn đã sử dụng jQuery, vì vậy chỉ cần $('.element').addClass('dragging-over')đến phần tử bạn đang kéo và $('.element').removeClass('dragging-over')khi bạn kéo ra. Sau đó, trong CSS của bạn, bạn có thể có .element.dragging-over * { pointer-events: none; }. Điều đó loại bỏ các sự kiện con trỏ khỏi tất cả các phần tử con chỉ khi bạn kéo qua.
Gavin

56

Cuối cùng tôi đã tìm thấy một giải pháp tôi hài lòng. Tôi thực sự đã tìm thấy một số cách để làm những gì tôi muốn nhưng không có cách nào thành công như giải pháp hiện tại ... trong một giải pháp, tôi gặp phải tình trạng nhấp nháy thường xuyên do thêm / xóa đường viền cho #dropzonephần tử ... ở đường viền khác, đường viền không bao giờ bị xóa nếu bạn di chuột ra khỏi trình duyệt.

Dù sao, giải pháp hack tốt nhất của tôi là thế này:

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

Điều này hoạt động khá tốt nhưng các vấn đề xuất hiện trong Firefox vì Firefox đã được gọi hai lần dragenternên bộ đếm của tôi bị tắt. Nhưng tuy nhiên, nó không phải là một giải pháp rất thanh lịch.

Sau đó, tôi vấp phải câu hỏi này: Cách phát hiện sự kiện dragleave trong Firefox khi kéo ra ngoài cửa sổ

Vì vậy, tôi đã lấy câu trả lời và áp dụng nó vào tình huống của mình:

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

Điều này rõ ràng và thanh lịch và dường như đang hoạt động trong mọi trình duyệt mà tôi đã thử nghiệm cho đến nay (chưa thử nghiệm IE).


Đây là giải pháp tốt nhất cho đến nay. Vấn đề duy nhất là mã này không hoạt động khi bạn thêm một tệp, xóa nó và kéo một tệp khác qua nó. Chỉ sau khi tăng HoverEnd và tăng HoverStart, mã này mới hoạt động trở lại.
MysticEarth

@MysticEarth bạn có thể kết hợp một jsFiddle để demo khi nó không hoạt động không?
Hristo

1
Cảm ơn bạn vì câu trả lời. Điều này thực sự hữu ích, vì giao diện kéo và thả HTML5 và người nghe sự kiện có thể thực sự đau đớn. Đây là cách tốt nhất để xử lý các sự kiện điên rồ, đặc biệt là khi bạn muốn xử lý nhiều trường hợp của các thành phần khu vực thả.
Nico O

Tại sao chúng ta cần gọi stopPropagation () notifyDefault () và trả về false. Trả lại sai nên là đủ. Có nên không?
Dejo

Nó là dư thừa, nhưng gọi stopPropagation()preventDefault()được ưa thích. Tôi muốn thả return false.
Peeja

38

Điều này hơi xấu nhưng nó hoạt động rất tệ! ...

Trên trình xử lý 'dragenter' của bạn lưu trữ event.target (trong một biến bên trong bao đóng của bạn hoặc bất cứ thứ gì), sau đó, trong trình xử lý 'dragleave' của bạn chỉ kích hoạt mã của bạn nếu event.target === cái bạn đã lưu trữ.

Nếu 'người kéo' của bạn bắn khi bạn không muốn nó (tức là khi nó nhập sau khi rời khỏi các phần tử con), thì lần cuối cùng nó bắn trước khi chuột rời khỏi cha mẹ, đó là bố mẹ, vì vậy bố mẹ sẽ luôn 'dragenter' cuối cùng trước 'dragleave' dự định.

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

}());

Tôi sẽ thử cái này sau. Nếu bất cứ điều gì, nó trông sạch hơn giải pháp của tôi nhưng tôi không thể chắc chắn làm thế nào nó tương thích với trình duyệt chéo chỉ bằng cách nhìn vào nó. Bạn đã thử nghiệm điều này ở đâu?
Hristo

1
chỉ Chrome và chỉ có thể hoạt động với giả định rằng có một khoảng cách vật lý giữa các thành phần con và vùng chứa.
hacklikecrack

5
Câu trả lời yêu thích của tôi ở đây. Hoàn toàn không cảm thấy xấu xí :)
Matt Way

1
Hoạt động với tôi trong IE, Chrome, FF
Vicentiu Bacioiu

1
Điều này không hoạt động nếu kéo bắt đầu vào trẻ em. Ví dụ, kéo từ một cái gì đó từ một cửa sổ khác theo cách kéo bắt đầu bên trong đứa trẻ.
FINDarkside

29

Lúc đầu, tôi đồng ý với những người từ chối pointer-events: nonecách tiếp cận. Nhưng rồi tôi tự hỏi:

Bạn có thực sự cần các sự kiện con trỏ để làm việc trên các phần tử con trong khi quá trình kéo đang diễn ra không?

Trong trường hợp của tôi, tôi có rất nhiều thứ đang diễn ra ở trẻ em, ví dụ như di chuột để hiển thị các nút cho các hành động bổ sung, chỉnh sửa nội tuyến, v.v ... Tuy nhiên, không có điều gì là cần thiết hoặc thực tế thậm chí là mong muốn trong khi kéo.

Trong trường hợp của tôi, tôi sử dụng một cái gì đó như thế này để tắt các sự kiện con trỏ một cách chọn lọc cho tất cả các nút con của vùng chứa cha:

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

Sử dụng phương pháp ưa thích của bạn để thêm / gỡ bỏ các lớp dragging-in-progresstrong dragEnter/ dragLeavexử lý sự kiện, như tôi đã làm hoặc làm trong cùng dragStart, et. al.


1
Giải pháp này là đơn giản nhất và là (theo như tôi có thể nói) đầy đủ bằng chứng. Đơn giản chỉ cần thêm lớp trong dragstartsự kiện và loại bỏ nó trong dragend.
cmann

Đây là giải pháp khá đẹp nhưng không hoàn hảo. Nếu kéo bắt đầu trên phần tử con (kéo từ cửa sổ khác), drag Entry không bao giờ được bắn. Có lẽ bạn cũng có thể di chuyển chuột đủ nhanh để kéo Drag không bị bắn khi bạn chuyển sang trẻ, nhưng không chắc chắn 100% về điều đó.
FINDarkside

12

Đây dường như là một lỗi của Chrome.

Cách giải quyết duy nhất mà tôi có thể nghĩ đến là tạo một phần tử lớp phủ trong suốt để ghi lại các sự kiện của bạn: 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>

cảm ơn vì câu trả lời, Blender. Tôi đã thử điều này tuy nhiên. Vấn đề với giải pháp này là phần tử lớp phủ "lớp phủ" các phần tử con mà tôi cần tương tác với ... do đó, lớp phủ sẽ vô hiệu hóa sự tương tác tôi cần với trẻ em ... nghĩ về nó như một tệp kéo-n hộp -drop ... bạn có thể kéo và thả hoặc bạn có thể nhấp vào yếu tố đầu vào để chọn tệp của mình.
Hristo

đây là một ví dụ thú vị rằng giải pháp của bạn không thỏa mãn ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo

Tôi bối rối. Tại sao bạn cũng cần phải tương tác với các yếu tố con?
Máy xay sinh tố

Trong trường hợp cụ thể của tôi, một phần tử con của "dropzone" là nút chọn tệp ... vì vậy nếu tôi không muốn kéo và thả tệp, tôi có thể chọn chúng bằng phần tử đầu vào. Nhưng với lớp phủ ... Tôi không thể nhấp vào phần tử đầu vào. có lý?
Hristo

@Blender Hầu hết các trường hợp chúng ta cần kéo một tệp từ máy tính để bàn, không phải là một phần tử từ trang, vì vậy loại này không hoạt động.
Mārtiņš Briedis

5

Vấn đề là, các yếu tố của bạn bên trong dropzones tất nhiên là một phần của dropzone và khi bạn nhập con, bạn rời khỏi cha mẹ. Giải quyết điều này là không dễ dàng. Bạn có thể thử thêm các sự kiện cho trẻ em thêm lớp của bạn một lần nữa cho phụ huynh.

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

    // add a class to #dropzone

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

Sự kiện của bạn vẫn sẽ cháy nhiều lần nhưng không ai thấy.

// Chỉnh sửa: Sử dụng sự kiện dragmove để ghi đè vĩnh viễn sự kiện dragleave:

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

    // add a class to #dropzone

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

Xác định sự kiện dragleave chỉ cho dropzone.


2
Điều đó sẽ không giải quyết vấn đề mặc dù. Vấn đề thực sự là dragleave... khi tôi rời khỏi một đứa trẻ, tôi vẫn có thể ở bên cha mẹ #dropzonenhưng điều đó sẽ không kích hoạt dragentersự kiện một lần nữa :(
Hristo

Bạn có chắc chắn, người kéo không bị bắn nữa không?
Oliver

À đúng rồi ... nó lại bị bắn, tuy nhiên, sau khi nó bị bắn, dragleavesự kiện được bắn cho phần tử con chỉ còn lại, vì vậy một lần nữa removeClasslệnh được thực thi
Hristo

Tôi không thể dragleavechỉ xác định cho #dropzone... nếu tôi có thể, tôi sẽ không gặp vấn đề này.
Hristo

Không, ý tôi là không thêm các sự kiện cho dragleave cho trẻ em.
Oliver

4

Như benr đã đề cập trong câu trả lời này , bạn có thể ngăn các nút con phát ra các sự kiện, nhưng nếu bạn cần liên kết một số sự kiện, hãy làm điều này:

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

Và thêm mã này vào mã JS của bạn:

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

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

2
Đối với tôi kết quả này trong lớp phủ nhấp nháy liên tục.
Andrey Mikhaylov - lolmaus

3

Nếu bạn đang sử dụng jQuery, hãy kiểm tra điều này: https://github.com/dancork/jquery.event.dragout

Nó thực sự tuyệt vời.

Sự kiện đặc biệt được tạo để xử lý chức năng dragleave thực sự.

Sự kiện dragleave HTML5 hoạt động giống như mouseout. Plugin này được tạo để sao chép chức năng kiểu mouseleave trong khi kéo.

Ví dụ sử dụng:

$ ('# myelement'). on ('dragout', function (event) {// MÃ CỦA BẠN});

EDIT: thực ra, tôi không nghĩ nó phụ thuộc vào jQuery, bạn có thể chỉ cần sử dụng mã ngay cả khi không có nó.


Ồ Dragout là MỘT điều làm việc cho bố cục của tôi với rất nhiều yếu tố trẻ em bắn ra dragleavemặc dù tôi đã không rời khỏi dropzone cha mẹ.
Đánh dấu Kasson

Sau khi chiến đấu với vấn đề này trong giờ cuối cùng, dragoutlà giải pháp duy nhất hiệu quả với tôi.
Jason Hazel

2

@hristo Mình có giải pháp tao nhã hơn nhiều. Kiểm tra xem nếu đó là một cái gì đó bạn có thể sử dụng.

Rốt cuộc nỗ lực của bạn không bị lãng phí. Tôi đã quản lý để sử dụng của bạn lúc đầu, nhưng có các vấn đề khác nhau trong FF, Chrome. Sau khi dành rất nhiều giờ, tôi nhận được đề nghị đó hoạt động chính xác như dự định.

Đây là cách thực hiện. Tôi cũng đã tận dụng các tín hiệu trực quan để hướng dẫn chính xác người dùng về vùng thả.

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

bạn dường như đang thiếu các định nghĩa cho một số biến trong phạm vi, tôi giả sửvar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen

@TimoHuovinen có, đúng. Cả hai dropZoneHideDelay, dropZoneVisibleđều cần thiết trong hai 'on'sự kiện tài liệu trong việc thực hiện của tôi.
lượt truy cập

2

Hai xu của tôi: Ẩn một lớp trên dropzone của bạn sau đó hiển thị nó khi bạn kéo và nhắm mục tiêu kéo trên đó.

Bản trình diễn: 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;
}

Mã não

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

Đơn giản và hiệu quả! Tôi đã đi xuống tuyến đường stopPropagation () và tôi không thích áp dụng trình xử lý sự kiện cho tất cả trẻ em. Nó có vẻ lộn xộn nhưng điều này hoạt động tốt và đơn giản để thực hiện. Ý tưởng tốt.
Jon Catmull

1

Vì vậy, đối với tôi cách tiếp cận pointer-events: none;không hiệu quả lắm ... Vì vậy, đây là giải pháp thay thế của tôi:

    #dropzone {
        position: relative;
    }

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

Bằng cách này, không thể cho dragleavecha mẹ (trên một đứa trẻ) hoặc dragovermột phần tử con. hi vọng điêu nay co ich :)

* Lớp '.active' tôi thêm khi tôi dragenterhoặc dragleave. Nhưng nếu bạn đang làm việc mà không có điều đó, hãy rời khỏi lớp học.


1
Tôi đã thử điều này trong một thành phần D & D của EmberJS và việc thêm lớp 'hoạt động' theo chương trình là quá chậm khoảng 40% thời gian chuột vào div cha (chế độ xem ember). Không chắc chắn lý do tại sao. Nhưng bỏ nó đi và nó hoạt động rất tốt, vì vậy cảm ơn bạn vì giải pháp đơn giản này. Đã thử nghiệm trên máy Mac trên các phiên bản mới nhất (tính đến ngày hôm nay) của Safari, Chrome, Firefox.
rmcsharry

1

Cách khắc phục nhanh siêu đơn giản cho việc này, không được thử nghiệm rộng rãi nhưng hoạt động trong Chrome ngay bây giờ.

Xin lỗi cho 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

Điều này sửa lỗi Chrome nhấp nháy hoặc ít nhất là sự hoán vị của nó gây ra sự cố cho tôi.



1

Phiên bản của tôi:

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

Với cách bố trí này:

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

Tôi đã thực hiện một bãi chứa của các tầng lớp yếu tố cho e.target, e.currentTarget, e.relatedTargetcho cả hai dragoverdragleavesự kiện.

Nó cho tôi thấy rằng khi rời khỏi khối cha ( .dropzone)e.relatedTarget không phải là con của khối này nên tôi biết tôi đã thoát khỏi dropzone.


0

Ở đây, một trong những giải pháp đơn giản nhất ● ●

Hãy nhìn vào câu đố này <- thử kéo một số tệp trong hộp

Bạn có thể làm một cái gì đó như thế này:

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

Do đó bạn nên biết những gì đang xảy ra ở đây.
Chỉ cần nhìn vào fiddle, bạn biết.


0

Tôi đã có một vấn đề tương tự và tôi đã khắc phục nó theo cách này:

Vấn đề: Hàm thả (ev) được kích hoạt khi người dùng thả một phần tử trong "vùng thả" (phần tử ul), nhưng, thật không may, cũng là khi phần tử bị rơi ở một trong các phần tử con của nó (phần tử li).

Cách khắc phục:

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

Tôi đã cố gắng tự thực hiện điều này cho một hộp tải lên tệp trong đó màu hộp sẽ thay đổi khi người dùng kéo tệp vào không gian.

Tôi tìm thấy một giải pháp là sự kết hợp tốt giữa Javascript và CSS. Giả sử bạn có một khu vực nhỏ giọt divvới id #drop. Thêm phần này vào Javascript của bạn:

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

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

Sau đó, thêm phần này vào CSS của bạn, để hủy kích hoạt tất cả trẻ em sẽ lớp .inactive:

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

Do đó, các phần tử con sẽ không hoạt động miễn là người dùng đang kéo phần tử trên hộp.


0

Tôi không cảm thấy hài lòng với bất kỳ cách giải quyết nào được trình bày ở đây, vì tôi không muốn mất kiểm soát đối với các yếu tố trẻ em.

Vì vậy, tôi đã sử dụng một cách tiếp cận logic khác, dịch nó thành một plugin jQuery, được gọi là jquery-Drainghandler . Nó hoàn toàn không thao túng DOM, đảm bảo hiệu suất cao. Cách sử dụng của nó rất đơn giản:

$(document).ready(function() {

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

});

Nó giải quyết vấn đề hoàn hảo mà không ảnh hưởng đến bất kỳ chức năng DOM nào.

Tải về, chi tiết và giải thích về nó kho Git .


Làm cách nào để có được sự kiện kéo vào trong plugin của bạn?
Woody

Tôi chưa thực sự kiểm tra chỉ thị của bạn, nhưng tôi nghĩ rằng nó không hoạt động nếu phần tử cha (A) không thực sự bao quanh phần tử tôi muốn kiểm tra (B). Ví dụ, nếu không có phần đệm giữa cạnh dưới của B và cạnh dưới của A, thì trình kéo sẽ không bao giờ được kích hoạt cho phần tử A, phải không?
marcelj

@marcelj Phần tử A có thể là tài liệu cơ thể, vì vậy bạn không thực sự cần một phần tử xung quanh (phần thân thực hiện công việc) với phần đệm tối thiểu. Hạn chế mà tôi đã tìm thấy sau này với phương pháp này là nếu bạn làm việc với frames và phần tử của bạn nằm ở biên của nó. Trong trường hợp đó, tôi không đề xuất phương pháp này.
Luca Fagioli

0

Tôi thực sự thích những gì tôi thấy tại https://github.com/lolmaus/jquery.dragbetter/nhưng muốn chia sẻ một sự thay thế có thể Chiến lược chung của tôi là áp dụng một kiểu nền cho dropzone (không phải con của nó) khi kéo nó hoặc bất kỳ con nào của nó (thông qua bong bóng). Sau đó, tôi loại bỏ kiểu khi kéo thả vùng thả. Ý tưởng là khi chuyển sang một đứa trẻ, ngay cả khi tôi loại bỏ phong cách khỏi dropzone khi rời khỏi nó (dragleave bắn), tôi chỉ đơn giản sẽ áp dụng lại phong cách cho dropzone cha mẹ khi kéo bất kỳ đứa trẻ nào. Vấn đề là tất nhiên là khi chuyển từ dropzone sang con của dropzone, người kéo bị bắn vào đứa trẻ trước khi dragleave, vì vậy các kiểu của tôi đã được áp dụng không theo thứ tự. Giải pháp cho tôi là sử dụng bộ đếm thời gian để buộc sự kiện kéo trở lại hàng đợi tin nhắn, cho phép tôi xử lý nó SAU khi kéo. Tôi đã sử dụng một bao đóng để truy cập sự kiện trên cuộc gọi lại hẹn giờ.

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

Điều này dường như hoạt động trong chrome, tức là firefox và hoạt động bất kể số lượng trẻ em trong dropzone. Tôi hơi khó chịu về việc hết thời gian để đảm bảo việc sắp xếp lại các sự kiện, nhưng có vẻ như nó hoạt động khá tốt cho trường hợp sử dụng của tôi.


0

Tôi đã cố gắng tự thực hiện điều này và tôi cũng không muốn Jquery hoặc bất kỳ plugin nào.

Tôi muốn xử lý việc tải lên tệp, theo cách được coi là tốt nhất đối với tôi:

Cấu trúc tệp:

--- / tải lên {thư mục tải lên}

--- /js/slyupload.js {tệp javascript.}

--- index.php

--- upload.php

--- Styles.css {chỉ một chút kiểu dáng ..}

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

Thì đây là Tệp Javascript đính kèm :: '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

Xin lỗi, nó không phải là javascript, nhưng đối với tôi, đó là cách hợp lý nhất để giải quyết vấn đề đó. Trình duyệt nên gọi dropeave (của phần tử trước) trước dropenter (ofr phần tử mới, vì một cái gì đó không thể nhập vào cái gì khác trước khi bỏ qua phần trước, tôi không hiểu tại sao họ lại làm vậy! Vì vậy, bạn chỉ cần trì hoãn de giọt như thế:

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

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

Và người bỏ học sẽ xảy ra sau khi giọt nước, thế thôi!


-1

Sử dụng greedy : truecho trẻ như là một chức năng của droppable. Sau đó, bắt đầu chỉ sự kiện nhấp vào lớp đầu tiên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.