자식 요소를 가리킬 때 HTML5 드래그 리브가 시작되었습니다.


300

내가 겪고있는 문제 dragleave는 요소의 하위 요소를 가리킬 때 요소 의 이벤트 가 시작된다는 것 입니다. 또한 dragenter상위 요소를 다시 호버링 할 때 발생하지 않습니다.

나는 간단한 바이올린을 만들었습니다 : 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>

다음 JavaScript를 사용하십시오.

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

그것이해야 할 일은 div무언가를 드래그 할 때 빨간색을 빨간색 으로 만들어 사용자에게 알리는 것입니다. 이것은 작동하지만 p자식 으로 드래그하면 dragleave해고되고 div더 이상 빨간색이 아닙니다. 드롭으로 다시 이동 div해도 다시 빨간색으로 표시되지 않습니다. 드롭에서 완전히 빠져 나와서 div다시 끌어서 빨간색으로 만들어야합니다.

dragleave자식 요소로 드래그 할 때 발생 하는 것을 막을 수 있습니까?

2017 업데이트 : TL; DR, pointer-events: none;아래 @HD의 답변에 설명 된 최신 브라우저 및 IE11에서 작동 하는 CSS 를 찾으십시오 .


pimvdb에보고 된 버그는 2012 년 5 월 현재 웹킷에 여전히 존재합니다. 드래그 오버에 클래스를 추가하여 대응하고 있습니다.
ajm

2
@ajm : 감사합니다. 어느 정도 작동합니다. 그러나 Chrome에서는 하위 요소를 입력하거나 떠날 때 플래시 dragleave가 발생합니다.이 경우 여전히 작동 하기 때문 입니다.
pimvdb

나는 연 jQuery를 UI 버그 upvotes는 그들이 리소스를 넣어 결정할 수 있도록 환영을
fguillen

@ fguillen : 죄송하지만 jQuery UI와는 관련이 없습니다. 사실, jQuery는 버그를 유발할 필요조차 없습니다. WebKit 버그를 이미 제출했지만 현재 업데이트가 없습니다.
pimvdb

2
@pimvdb 왜 HD의 대답을 받아들이지 않습니까?
TylerH

답변:


337

당신은 참조 카운터를 유지하고 드래그를 얻을 때 증가하고 드래그 리브를 얻을 때 감소해야합니다. 카운터가 0에있을 때-클래스를 제거하십시오.

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

참고 : 드롭 이벤트에서 카운터를 0으로 재설정하고 추가 된 클래스를 지우십시오.

여기서 실행할 수 있습니다


8
세상에 이것은 가장 확실한 해결책이며 한 번만 투표했습니다 ... 사람들에게 와서, 더 잘할 수 있습니다. 나는 그것에 대해 생각하고 있었지만 처음 몇 가지 대답의 정교함을 보았을 때 나는 거의 그것을 버렸다. 단점이 있습니까?
Arthur Corenzan

11
드래그하는 요소의 가장자리가 다른 드래그 가능한 요소의 가장자리에 닿으면 작동하지 않습니다. 예를 들어 정렬 가능한 목록; 요소를 아래쪽으로 드래그하면 다음 드래그 가능한 항목 위로 카운터가 다시 0으로 감소하지 않고 대신 1로 고정됩니다. 그러나 다른 드래그 가능한 요소에서 옆으로 드래그하면 작동합니다. 나는 pointer-events: none아이들 과 함께 갔다 . 드래그를 시작하면이 속성이있는 클래스를 추가하고 드래그가 끝나면 클래스를 제거합니다. Safari와 Chrome에서는 훌륭하게 작동했지만 Firefox에서는 잘 작동하지 않았습니다.
Arthur Corenzan

13
이것은 Firefox를 제외한 모든 브라우저에서 효과적이었습니다. 필자의 경우 어디에서나 작동하는 새로운 솔루션이 있습니다. 처음 에는 새 변수에 dragenter저장 event.currentTarget합니다 dragEnterTarget. 긴로 dragEnterTarget설정되어, 나는 더 무시 dragenter가 어린이이기 때문에, 이벤트를. 모든 dragleave이벤트에서 확인 dragEnterTarget === event.target합니다. 이것이 거짓 인 경우, 이벤트는 자식에 의해 발생 된대로 무시됩니다. 이것이 사실이라면 나는로 재설정 dragEnterTarget합니다 undefined.
Pipo

3
훌륭한 솔루션. 그래도 드롭 수락을 처리하는 함수에서 카운터를 0으로 재설정해야했습니다. 그렇지 않으면 후속 드래그가 예상대로 작동하지 않았습니다.
Liam Johnston

2
@Woody 나는 여기에있는 수많은 의견 목록에서 그의 의견을 보지 못했지만 예, 그게 정답입니다. 왜 대답에 포함시키지 않습니까?
BT

93

하위 요소로 드래그 할 때 드래그 리브가 실행되는 것을 방지 할 수 있습니까?

예.

#drop * {pointer-events: none;}

그 CSS는 Chrome에 충분할 것 같습니다.

Firefox에서 사용하는 동안 #drop에는 직접 텍스트 노드가 없어야합니다 (다른 요소가 "자신에게 맡기는" 이상한 문제가 있음). 한 요소 만 남겨 두는 것이 좋습니다 (예 : # 모든 것을 넣을 수있는 드롭)

다음원래 질문 (깨진) 예제를 해결 하는 jsfiddle 입니다.

또한 @Theodore Brown 예제에서 단순화 된 버전을 만들었 지만이 CSS에만 기반합니다.

모든 브라우저가이 CSS를 구현하지는 않습니다 : http://caniuse.com/pointer-events

Facebook 소스 코드를 보면이 문제를 pointer-events: none;여러 번 찾을 수 있지만 우아한 저하 폴 백과 함께 사용됩니다. 최소한 너무 간단하고 많은 환경에서 문제를 해결합니다.


pointer-events 속성은 앞으로 올바른 솔루션이지만 불행히도 여전히 널리 사용되는 IE8-IE10에서는 작동하지 않습니다. 또한 현재 jsFiddle은 IE11에서도 작동하지 않습니다. 필요한 이벤트 리스너와 기본 동작 방지 기능을 추가하지 않기 때문입니다.
Theodore Brown

2
포인터 이벤트를 사용하는 것이 실제로 좋은 대답입니다. 나는 스스로 알아 내기 전에 조금 어려움을 겪었습니다. 그 대답은 더 높아야합니다.
floribon

오래된 브라우저를 지원하지 않아도 될만큼 운이 좋은 사람들에게는 환상적인 옵션입니다.
Luke Hansford

7
자녀가 버튼 인 경우 어떻게합니까? oO
David

9
이 솔루션은 그것들을 차단하기 때문에 영역 안에 다른 컨트롤러 요소 (편집, 삭제)가없는 경우에만 작동합니다.
Zoltán Süle

45

가장 간단한 크로스 브라우저 솔루션은 다음과 같습니다.

jsfiddle <-상자 안의 일부 파일을 드래그 해보십시오

당신은 그런 식으로 할 수 있습니다 :

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

몇 마디로, 드롭 존 내부에 "마스크"를 생성합니다. 너비와 높이가 상속되고 위치 절대 값이며 드래그가 시작될 때만 표시됩니다.
따라서 해당 마스크를 표시 한 후 다른 드래그 앤 드롭 이벤트를 연결하여 트릭을 수행 할 수 있습니다.

떠나거나 떨어 뜨린 후에는 마스크를 다시 숨기면됩니다.
간단하고 합병증이 없습니다.

(Obs .: Greg Pettit 조언-마스크가 테두리를 포함한 전체 상자를 가리켜 야합니다)


이유가 확실하지 않지만 Chrome과 일관되게 작동하지 않습니다. 때로는 영역을 벗어나면 마스크가 계속 보입니다.
Greg Pettit

2
실제로는 국경입니다. 마스크가 테두리와 겹치지 않도록하고 테두리가 없어야합니다.
Greg Pettit

1
jsfiddle에 버그가 있습니다. drag_drop에서 "# box-a"가 아닌 "#box"에서 호버 클래스를 제거해야합니다.
entropy

이것은 좋은 해결책이지만 어떻게 든 작동하지 않았습니다. 마침내 해결 방법을 알아 냈습니다. 다른 것을 찾고있는 당신을 위해. 당신은 이것을 시도 할 수 있습니다 : github.com/bingjie2680/jquery-draghover
bingjie2680

아니요. 하위 요소에 대한 통제력을 잃고 싶지 않습니다. 우리를 위해 일하는 문제를 다루는 간단한 jQuery 플러그인을 만들었습니다. 내 대답을 확인하십시오 .
Luca Fagioli

39

이 질문을한지 꽤 시간이 지났고 많은 솔루션 (추악한 해킹 포함)이 제공되었습니다.

나는이 답변 에 대한 답변 덕분에 최근에 겪었던 것과 동일한 문제를 해결할 수 있었고이 페이지를 방문하는 사람에게 도움이 될 것이라고 생각했습니다. 전체 아이디어는 부모 또는 자식 요소 중 하나에서 호출 evenet.targetondrageenter때 마다을 저장하는 것입니다. 그런 다음 ondragleave현재 대상 ( event.target)이에 저장된 객체와 같은지 확인하십시오 ondragenter.

이 두 가지가 일치하는 유일한 경우는 드래그가 브라우저 창을 떠날 때입니다.

이것이 잘 작동하는 이유는 마우스가 요소를 떠난 후 (예 el1를 들어) 다른 요소에 들어간 경우 ( el2먼저 el2.ondragenter)를 호출 한 다음 el1.ondragleave입니다. 드래그가 떠날 경우에만 / 브라우저 창을 입력하는 event.target''모두 el2.ondragenterel1.ondragleave.

여기 내 작업 샘플이 있습니다. IE9 +, Chrome, Firefox 및 Safari에서 테스트했습니다.

(function() {
    var bodyEl = document.body;
    var flupDiv = document.getElementById('file-drop-area');

    flupDiv.onclick = function(event){
        console.log('HEy! some one clicked me!');
    };

    var enterTarget = null;

    document.ondragenter = function(event) {
        console.log('on drag enter: ' + event.target.id);
        enterTarget = event.target;
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-drag-on-top';
        return false;
    };

    document.ondragleave = function(event) {
        console.log('on drag leave: currentTarget: ' + event.target.id + ', old target: ' + enterTarget.id);
        //Only if the two target are equal it means the drag has left the window
        if (enterTarget == event.target){
            event.stopPropagation();
            event.preventDefault();
            flupDiv.className = 'flup-no-drag';         
        }
    };
    document.ondrop = function(event) {
        console.log('on drop: ' + event.target.id);
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-no-drag';
        return false;
    };
})();

그리고 여기 간단한 HTML 페이지가 있습니다 :

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Multiple File Uploader</title>
<link rel="stylesheet" href="my.css" />
</head>
<body id="bodyDiv">
    <div id="cntnr" class="flup-container">
        <div id="file-drop-area" class="flup-no-drag">blah blah</div>
    </div>
    <script src="my.js"></script>
</body>
</html>

올바른 스타일을 사용하면 파일을 화면으로 드래그 할 때마다 내부 div (# file-drop-area)를 훨씬 더 크게 만들어 사용자가 파일을 적절한 위치에 쉽게 놓을 수 있습니다.


4
이것은 최고의 솔루션이며 카운터보다 낫습니다 (특히 이벤트를 위임하는 경우). 드래그 가능한 어린이와도 작동합니다.
Fred

3
이것은 중복 dragenter 이벤트 문제에 도움이되지 않습니다
BT

이것은 CSS 의사 요소 또는 의사 클래스 인 "자식"에 적용됩니까? 나는 그것을 얻지 못했지만 어쩌면 내가 잘못하고 있었을 것입니다.
Josh Bleecher Snyder

1
2020 년 3 월 현재이 솔루션으로 최고의 행운을 얻었습니다. 참고 : 일부 린 터는 ==over 사용에 대해 불평 할 수 있습니다 ===. 이벤트 대상 객체 참조를 비교하고 있으므로 ===잘 작동합니다.
Alex

33

이 문제를 해결하는 "올바른"방법은 드롭 대상의 하위 요소에서 포인터 이벤트를 비활성화하는 것입니다 (@HD의 답변에서와 같이). 이 기술을 보여주는 jsFiddle이 있습니다. 불행히도 IE11 이전의 Internet Explorer 버전에서는 포인터 이벤트를 지원 하지 않기 때문에이 기능이 작동 하지 않습니다 .

다행히, 나는 해결 방법을 마련 할 수 있었다 않는 IE의 이전 버전에서 작업을. 기본적으로 dragleave하위 요소를 드래그 할 때 발생 하는 이벤트를 식별하고 무시 합니다. 부모 에서 dragenter이벤트가 발생하기 전에 자식 노드에서 dragleave이벤트가 시작되므로 드롭 대상에서 "ignore-drag-leave"클래스를 추가하거나 제거하는 각 자식 노드에 별도의 이벤트 리스너를 추가 할 수 있습니다. 그러면 드롭 대상의 dragleave이벤트 리스너는이 클래스가 존재할 때 발생하는 호출을 무시할 수 있습니다. 여기의 이 해결 방법을 보여 jsFiddle은 . Chrome, Firefox 및 IE8 +에서 테스트되고 작동합니다.

최신 정보:

지원되는 경우 포인터 이벤트가 사용되는 (현재 Chrome, Firefox 및 IE11) 기능 감지를 사용하여 결합 된 솔루션보여주는 jsFiddle을 만들었 으며 포인터 이벤트가 지원되지 않는 경우 브라우저는 하위 노드에 이벤트를 추가하는 것으로 돌아갑니다 (IE8 -10).


이 답변은 바람직하지 않은 발사에 대한 해결 방법을 보여 주지만 "자식 요소로 드래그 할 때 드래그 리브가 발사되는 것을 막을 수 있습니까?"라는 질문을 무시합니다. 전적으로.
HD

브라우저 외부에서 파일을 드래그 할 때 동작이 이상해질 수 있습니다. Firefox에는 부모를 떠나지 않고 "자녀 입장-> 부모 입력-> 자녀 떠나기-> 자녀 입장-> 자녀 떠나기"가 있습니다. 이전 IE는 addEventListener에 대한 attachEvent 교체가 필요합니다.
HD

이 솔루션은 버블 링에 크게 의존하므로 모든 addEventListener의 "거짓"은 필수 (기본 동작 임에도 불구하고)로 강조되어야합니다. 많은 사람들이 그것에 대해 알지 못할 수도 있습니다.
HD

이 단일 드래그 가능은 드롭 영역이 드래그 시작 이벤트를 트리거하지 않는 다른 드래그 가능 객체에 사용될 때 나타나지 않는 효과를 드롭 영역에 추가합니다. 아마도 다른 핸들러와 함께 실제 dropzone을 유지하면서 드래그의 효과를 위해 모든 것을 dropzone으로 사용하는 것입니다.
HD

1
@HD dragleaveChrome, Firefox 및 IE11 +에서 이벤트가 발생 하지 않도록 pointer-events CSS 속성 사용에 대한 정보로 답변을 업데이트했습니다 . 또한 IE10 외에도 IE8 및 IE9를 지원하도록 다른 해결 방법을 업데이트했습니다. 드롭 존 효과는 "드래그 저"링크를 드래그 할 때만 추가됩니다. 다른 사람들은 사용 사례를 지원하는 데 필요에 따라이 동작을 자유롭게 변경할 수 있습니다.
Theodore Brown

14

문제는 dragleave마우스가 자식 요소 앞에 갔을 때 이벤트가 시작된다는 것입니다.

e.target요소가 요소와 같은지 확인하기 위해 다양한 방법을 시도했지만 this개선 할 수 없었습니다.

이 문제를 해결 한 방식은 약간의 해킹이지만 100 % 작동합니다.

dragleave: function(e) {
               // Get the location on screen of the element.
               var rect = this.getBoundingClientRect();

               // Check the mouseEvent coordinates are outside of the rectangle
               if(e.x > rect.left + rect.width || e.x < rect.left
               || e.y > rect.top + rect.height || e.y < rect.top) {
                   $(this).removeClass('red');
               }
           }

1
감사! 그러나 Chrome에서는 작동하지 않습니다. 해킹의 바이올린을 제공 할 수 있습니까?
pimvdb

3
나는 또한 좌표를 확인하여 그것을 할 생각이었습니다. 당신은 나를 위해 대부분의 일을했습니다. 나는 약간 조정해야했다 :if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
Christof

이벤트에 e.x및 이 없기 때문에 Chrome에서 작동하지 않습니다 e.y.
Hengjie

나는이 해결책을 좋아한다. Firefox에서 먼저 작동하지 않았습니다. 그러나 ex를 e.clientX로 바꾸고 ey를 e.clientY로 바꾸면 작동합니다. Chrome에서도 작동합니다.
니콜 스투 츠

나를 위해 크롬에서 일하지 않았고 ChrisDaniel Stuts가 제안한 것도
Timo Huovinen

14

HTML5를 사용하는 경우 부모의 clientRect를 얻을 수 있습니다.

let rect = document.getElementById("drag").getBoundingClientRect();

그런 다음 parent.dragleave ()에서 :

dragleave(e) {
    if(e.clientY < rect.top || e.clientY >= rect.bottom || e.clientX < rect.left || e.clientX >= rect.right) {
        //real leave
    }
}

여기는 jsfiddle입니다


2
훌륭한 답변입니다.
broc.seib

고마워 친구, 나는 일반 자바 스크립트 접근법을 좋아한다.
Tim Gerhard

가있는 요소를 사용할 때 border-radius포인터를 모퉁이 근처로 이동하면 실제로 요소가 남지만이 코드는 여전히 내부에 있다고 생각합니다 (요소를 떠났지만 여전히 경계 사각형에 있습니다). 그러면 dragleave이벤트 핸들러가 전혀 호출되지 않습니다.
Jay Dadhania

11

매우 간단한 해결책은 pointer-eventsCSS 속성 을 사용하는 것 입니다. 모든 자식 요소에서 드래그 시작none 시 값을 설정하십시오 . 이러한 요소는 더 이상 마우스 관련 이벤트를 트리거하지 않으므로 마우스를 마우스 위로 가져 가지 않아 부모 에서 드래그 리브 를 트리거하지 않습니다 .

auto드래그를 마칠 때이 속성을 다시 설정하는 것을 잊지 마십시오 .)


11

이벤트가 각 드래그 요소에 개별적으로 연결되어 있다고 가정하면이 매우 간단한 솔루션이 지금까지 효과가 있습니다.

if (evt.currentTarget.contains(evt.relatedTarget)) {
  return;
}

대단해! @kenneth를 공유해 주셔서 감사합니다. 내 하루를 구했습니다! 다른 많은 답변은 놓기 가능한 영역이 하나 인 경우에만 적용됩니다.
antoni

나에게 그것은 Chrome과 Firefox에서 작동하지만 id는 Edge에서 작동하지 않습니다. 참조 : jsfiddle.net/iwiss/t9pv24jo
iwis

8

매우 간단한 해결책 :

parent.addEventListener('dragleave', function(evt) {
    if (!parent.contains(evt.relatedTarget)) {
        // Here it is only dragleave on the parent
    }
}

Safari 12.1에서는 작동하지 않는 것 같습니다 : jsfiddle.net/6d0qc87m
Adam Taylor

7

jQuery 소스 코드 에서 약간의 영감을 받아 Firefox에서 수정할 수 있습니다 .

dragleave: function(e) {
    var related = e.relatedTarget,
        inside = false;

    if (related !== this) {

        if (related) {
            inside = jQuery.contains(this, related);
        }

        if (!inside) {

            $(this).removeClass('red');
        }
    }

}

불행히도 이벤트에는 relatedTarget존재하지 않기 때문에 Chrome에서 작동하지 않으며 dragleaveFirefox에서 예제가 작동 하지 않아 Chrome에서 작업하고 있다고 가정합니다. 위 코드가 구현 된 버전다음과 같습니다 .


대단히 감사합니다. 그러나 실제로이 문제를 해결하려고 노력하는 것은 Chrome입니다.
pimvdb

5
@pimvdb 나는 당신이 버그를 기록 것을 보았습니다 . 다른 사람 이이 답변을 발견 할 경우를 대비하여 여기에 참조를 남겨 두겠습니다.
robertc

나는 실제로했지만 여기에 링크를 추가하는 것을 잊었다. 그렇게 해줘서 고마워
pimvdb

그동안 Chrome 버그가 수정되었습니다.
phk

7

그리고 여기 Chrome 용 솔루션이 있습니다.

.bind('dragleave', function(event) {
                    var rect = this.getBoundingClientRect();
                    var getXY = function getCursorPosition(event) {
                        var x, y;

                        if (typeof event.clientX === 'undefined') {
                            // try touch screen
                            x = event.pageX + document.documentElement.scrollLeft;
                            y = event.pageY + document.documentElement.scrollTop;
                        } else {
                            x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                            y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
                        }

                        return { x: x, y : y };
                    };

                    var e = getXY(event.originalEvent);

                    // Check the mouseEvent coordinates are outside of the rectangle
                    if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
                        console.log('Drag is really out of area!');
                    }
                })

«if (typeof event.clientX === 'undefined')»로 들어가나요?
Aldekein

1
훌륭하게 작동했지만 브라우저 위에 다른 창이있을 수 있으므로 마우스 위치를 가져 와서 직사각형 화면 영역과 비교하는 것만으로는 충분하지 않습니다.
HD

@HD에 동의합니다. 또한 border-radius위의 @azlar의 답변에 대한 설명에서 설명했듯이 요소가 클 때 문제가 발생할 수 있습니다 .
Jay Dadhania

7

다음은 document.elementFromPoint를 사용하는 다른 솔루션입니다.

 dragleave: function(event) {
   var event = event.originalEvent || event;
   var newElement = document.elementFromPoint(event.pageX, event.pageY);
   if (!this.contains(newElement)) {
     $(this).removeClass('red');
   }
}

이것이 효과가 있기를 바랍니다 . 여기에 바이올린이 있습니다.


3
이것은 즉시 나에게 효과가 없었습니다. event.clientX, event.clientY페이지가 아닌 뷰포트와 관련이 있기 때문에 대신 사용해야 했습니다. 다른 영혼을 잃어 버리는 데 도움이되기를 바랍니다.
Jon Abrams 2016 년

7

간단한 해결책은 pointer-events: none의 트리거를 막기 위해 자식 규칙 에 CSS 규칙을 추가하는 것 입니다 ondragleave. 예를보십시오 :

function enter(event) {
  document.querySelector('div').style.border = '1px dashed blue';
}

function leave(event) {
  document.querySelector('div').style.border = '';
}
div {
  border: 1px dashed silver;
  padding: 16px;
  margin: 8px;
}

article {
  border: 1px solid silver;
  padding: 8px;
  margin: 8px;
}

p {
  pointer-events: none;
  background: whitesmoke;
}
<article draggable="true">drag me</article>

<div ondragenter="enter(event)" ondragleave="leave(event)">
  drop here
  <p>child not triggering dragleave</p>
</div>


나는이 같은 문제가 있었고 귀하의 솔루션은 훌륭하게 작동합니다. 다른 사람들을위한 팁, 이것은 내 경우입니다 : 드래그 이벤트를 무시하려고하는 자식 요소 가 드래그중인 요소의 복제본 (시각 미리보기를 시도 중) 인 경우 복제본을 만들 때 dragstart 내에서 이것을 사용할 수 있습니다 :dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
Scaramouche

4

이 크로스 브라우저인지 확실하지 않지만 Chrome에서 테스트했으며 문제를 해결합니다.

전체 페이지에서 파일을 끌어다 놓으려고하지만 하위 요소 위로 드래그하면 드래그 리브가 시작됩니다. 내 수정은 마우스의 x와 y를 보는 것입니다.

페이지가로드 될 때 전체 페이지를 오버레이하는 div가 있습니다.

당신이 문서를 드래그하면 그것을 보여주고 부모를 떨어 뜨릴 때 그것을 처리하고 부모를 떠날 때 x와 y를 확인합니다.

$('#draganddrop-wrapper').hide();

$(document).bind('dragenter', function(event) {
    $('#draganddrop-wrapper').fadeIn(500);
    return false;
});

$("#draganddrop-wrapper").bind('dragover', function(event) {
    return false;
}).bind('dragleave', function(event) {
    if( window.event.pageX == 0 || window.event.pageY == 0 ) {
        $(this).fadeOut(500);
        return false;
    }
}).bind('drop', function(event) {
    handleDrop(event);

    $(this).fadeOut(500);
    return false;
});

1
해 키지 만 영리합니다. 나는 좋아한다. 나를 위해 일했다.
cupcakekid

1
아,이 답변에 더 많은 표가 있었으면 좋겠어요! 감사합니다
Kirby

4

이 정확한 문제를 처리하기 위해 Dragster 라는 작은 라이브러리를 작성 했으며 IE에서 자동으로 수행하지 않는 모든 곳에서 작동합니다 (DOM 이벤트 생성자를 지원하지 않지만 jQuery의 사용자 정의 이벤트를 사용하여 비슷한 것을 작성하는 것은 매우 쉽습니다)


매우 유용합니다 (적어도 Chrome에만 관심이있는 경우 나에게).
M Katz

4

나는 같은 문제가 있었고 pk7s 솔루션을 사용하려고했습니다. 그것은 효과가 있었지만 여분의 돔 요소없이 조금 더 나아질 수있었습니다.

기본적으로 아이디어는 동일합니다. 놓기 가능한 영역 위에 보이지 않는 오버레이를 추가하십시오. 여분의 돔 요소없이이 작업을 수행 할 수 있습니다. 다음은 CSS 의사 요소가 등장한 부분입니다.

자바 스크립트

var dragOver = function (e) {
    e.preventDefault();
    this.classList.add('overlay');
};

var dragLeave = function (e) {
    this.classList.remove('overlay');
};


var dragDrop = function (e) {
    this.classList.remove('overlay');
    window.alert('Dropped');
};

var dropArea = document.getElementById('box');

dropArea.addEventListener('dragover', dragOver, false);
dropArea.addEventListener('dragleave', dragLeave, false);
dropArea.addEventListener('drop', dragDrop, false);

CSS

이후 규칙은 놓기 가능한 영역에 대해 완전히 덮힌 오버레이를 만듭니다.

#box.overlay:after {
    content:'';
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    z-index: 1;
}

전체 솔루션은 다음과 같습니다. http://jsfiddle.net/F6GDq/8/

나는 그것이 같은 문제를 가진 사람에게 도움이되기를 바랍니다.


1
크롬에서 작동하지 않는 경우가 있습니다 (드래그 리브가 제대로 잡히지 않음)
Timo

4

드래그 된 요소가 자식인지 확인하고 '있는 경우'드래그 오버 스타일 클래스를 제거하지 마십시오. 매우 간단하고 나를 위해 작동합니다.

 $yourElement.on('dragleave dragend drop', function(e) {
      if(!$yourElement.has(e.target).length){
           $yourElement.removeClass('is-dragover');
      }
  })

나에게 가장 간단한 해결책처럼 보이며 내 문제를 해결했습니다. 감사!
Francesco Marchetti-Stasi

3

나는 같은 문제에 빠져 들었고 여기에 내 해결책이 있습니다. 크로스 브라우저인지 확실하지 않습니다 (버블 링 순서에 따라 다름)

단순화를 위해 jQuery를 사용하지만 솔루션은 프레임 워크 독립적이어야합니다.

이벤트는 다음과 같이 주어진 방식으로 부모에게 버블 링됩니다.

<div class="parent">Parent <span>Child</span></div>

이벤트를 첨부

el = $('.parent')
setHover = function(){ el.addClass('hovered') }
onEnter  = function(){ setTimeout(setHover, 1) }
onLeave  = function(){ el.removeClass('hovered') } 
$('.parent').bind('dragenter', onEnter).bind('dragleave', onLeave)

그리고 그것은 그것에 관한 것입니다. :) 부모가 onLeave하기 전에 자식에서 onEnter가 발생하더라도 순서를 반대로 뒤집기 때문에 클래스가 먼저 제거 된 후 1 초 후에 다시 추가되기 때문에 작동합니다.


이 스 니펫이하는 것은 다음 틱 사이클을 다시 적용하여 '호버링 된'클래스가 제거되지 않도록하는 것입니다 ( 'dragleave'이벤트를 쓸모 없게 만듭니다).
null

쓸모가 없습니다. 부모를 떠나면 예상대로 작동합니다. 이 솔루션의 장점은 단순하다는 것입니다. 이상적이거나 최선이 아닙니다. 더 나은 해결책은 자녀에게 입장을 표시하고 방금 자녀를 방금 입력했는지 여부와 휴가 이벤트를 유발하지 않는 경우를 확인하는 것입니다. 테스트, 추가 보호원, 손자를위한 등이 필요합니다.
Marcin Raczkowski

3

드립 드롭 이라는 드래그 앤 드롭 모듈을 작성 하여이 이상한 행동을 해결했습니다. 좋은 저수준 드래그 앤 드롭 모듈을 찾고 있다면 무엇이든 (파일 업로드, 인앱 드래그 앤 드롭, 외부 소스에서 드래그하거나 외부 소스로) 사용할 수있는 기초로 사용할 수 있습니다. 모듈 출력 :

https://github.com/fresheneesz/drip-drop

다음은 드립 드롭에서 수행하려는 작업을 수행하는 방법입니다.

$('#drop').each(function(node) {
  dripDrop.drop(node, {
    enter: function() {
      $(node).addClass('red')  
    },
    leave: function() {
      $(node).removeClass('red')
    }
  })
})
$('#drag').each(function(node) {
  dripDrop.drag(node, {
    start: function(setData) {
      setData("text", "test") // if you're gonna do text, just do 'text' so its compatible with IE's awful and restrictive API
      return "copy"
    },
    leave: function() {
      $(node).removeClass('red')
    }
  })
})

라이브러리 없이이 작업을 수행하기 위해 카운터 기술은 내가 드립 드롭에 사용한 것입니다. 최고 등급의 답변은 첫 번째 드롭을 제외한 모든 항목에서 문제가 발생하는 중요한 단계를 놓칩니다. 올바르게 수행하는 방법은 다음과 같습니다.

var counter = 0;    
$('#drop').bind({
    dragenter: function(ev) {
        ev.preventDefault()
        counter++
        if(counter === 1) {
          $(this).addClass('red')
        }
    },

    dragleave: function() {
        counter--
        if (counter === 0) { 
            $(this).removeClass('red');
        }
    },
    drop: function() {
        counter = 0 // reset because a dragleave won't happen in this case
    }
});

2

조금 더 간단한 대체 작업 솔루션.

//Note: Due to a bug with Chrome the 'dragleave' event is fired when hovering the dropzone, then
//      we must check the mouse coordinates to be sure that the event was fired only when 
//      leaving the window.
//Facts:
//  - [Firefox/IE] e.originalEvent.clientX < 0 when the mouse is outside the window
//  - [Firefox/IE] e.originalEvent.clientY < 0 when the mouse is outside the window
//  - [Chrome/Opera] e.originalEvent.clientX == 0 when the mouse is outside the window
//  - [Chrome/Opera] e.originalEvent.clientY == 0 when the mouse is outside the window
//  - [Opera(12.14)] e.originalEvent.clientX and e.originalEvent.clientY never get
//                   zeroed if the mouse leaves the windows too quickly.
if (e.originalEvent.clientX <= 0 || e.originalEvent.clientY <= 0) {

Chrome에서 항상 작동하지는 않습니다. clientX마우스가 상자 밖에있을 때 때때로 0 이상을 받습니다 . 내 요소는 다음과 같습니다position:absolute
Hengjie

항상 또는 가끔 발생합니까? 마우스가 너무 빨리 움직이면 (예 : 창 밖) 잘못된 값을 얻을 수 있습니다.
Profet

시간의 90 %가 발생합니다. 0에 도달 할 수있는 드문 경우 (10 회 중 1 회)가 있습니다. 마우스를 느리게 다시 움직일 수 있지만 빠르게 움직이고 있다고 말할 수는 없습니다 (아마도 보통 속도라고 부르는 것).
Hengjie

2

너무 많은 시간을 보낸 후에 나는 의도 한대로 정확하게 작동하는 제안을 받았습니다. 파일을 드래그 할 때만 큐를 제공하고 문서 드래그, 드래그 리브가 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
});

1

비슷한 문제가 있습니다. Chrome에서 dropzone이 깜박 거리는 자식 요소를 가리킬 때 body의 dragleave 이벤트에서 dropzone을 숨기는 코드가 오염되었습니다.

dropzone을 숨기지 않고 숨기는 기능을 예약 하여이 문제를 해결할 수있었습니다. 그런 다음 다른 끌어 놓기 또는 끌어 놓기가 발생하면 예약 된 함수 호출이 취소됩니다.

body.addEventListener('dragover', function() {
    clearTimeout(body_dragleave_timeout);
    show_dropzone();
}, false);

body.addEventListener('dragleave', function() {
    clearTimeout(body_dragleave_timeout);
    body_dragleave_timeout = setTimeout(show_upload_form, 100);
}, false);

dropzone.addEventListener('dragover', function(event) {
    event.preventDefault();
    dropzone.addClass("hover");
}, false);

dropzone.addEventListener('dragleave', function(event) {
    dropzone.removeClass("hover");
}, false);

이것이 내가 결국 한 일이지만 여전히 스케치입니다.
mpen

1

마우스 포인터가 대상 컨테이너의 드래그 영역을 벗어나면 " dragleave "이벤트가 발생합니다.

어느 단지 부모가있을 수 있습니다 대부분의 경우에서와 같이 많은 이해 낙하 할 후손이 아니라. 나는 event.stopPropogation () 생각 이이 경우를 처리해야 하지만 트릭을 수행하지 않는 것 같습니다.

위에서 언급 한 일부 솔루션은 대부분의 경우 작동하는 것처럼 보이지만 iframe 과 같은 dragenter / dragleave 이벤트를 지원하지 않는 어린이의 경우 실패합니다 .

1 해결 방법은 event.relatedTarget 을 확인하고 컨테이너 안에 있는지 확인한 다음 여기에서 한 것처럼 dragleave 이벤트 를 무시하는 것입니다.

function isAncestor(node, target) {
    if (node === target) return false;
    while(node.parentNode) {
        if (node.parentNode === target)
            return true;
        node=node.parentNode;
    }
    return false;
}

var container = document.getElementById("dropbox");
container.addEventListener("dragenter", function() {
    container.classList.add("dragging");
});

container.addEventListener("dragleave", function(e) {
    if (!isAncestor(e.relatedTarget, container))
        container.classList.remove("dragging");
});

여기 에서 작동하는 바이올린을 찾을 수 있습니다 !


1

해결 ..!

ex에 대한 배열을 선언하십시오.

targetCollection : any[] 

dragenter: function(e) {
    this.targetCollection.push(e.target); // For each dragEnter we are adding the target to targetCollection 
    $(this).addClass('red');
},

dragleave: function() {
    this.targetCollection.pop(); // For every dragLeave we will pop the previous target from targetCollection
    if(this.targetCollection.length == 0) // When the collection will get empty we will remove class red
    $(this).removeClass('red');
}

자식 요소에 대해 걱정할 필요가 없습니다.


1

나는이 답변을 모두 읽은 후에도 이것으로 많은 어려움을 겪었고 솔루션이 조금 더 간단한 접근법 중 하나 일 수 있다고 생각했기 때문에 솔루션을 공유 할 수 있다고 생각했습니다. 내 생각은 단순히dragleave 이벤트 리스너를 완전히 새로운 dragenter 이벤트가 발생했을 때 dragleave 동작을 코딩하는 동안 dragenter 이벤트가 불필요하게 발생하지 않도록하는 것이 었습니다.

아래 예제에는 드래그 앤 드롭 API를 통해 테이블 ​​행 내용을 서로 교환 할 수있는 테이블이 있습니다. 에 dragenter요소를 현재 드래그하고있는 행 요소에 CSS 클래스를 추가하여 강조 표시 한 다음 dragleave이 클래스를 제거합니다.

예:

매우 기본적인 HTML 테이블 :

<table>
  <tr>
    <td draggable="true" class="table-cell">Hello</td>
  </tr>
  <tr>
    <td draggable="true" clas="table-cell">There</td>
  </tr>
</table>

그리고 각 테이블 셀에 추가 DragEnter 이벤트 핸들러 함수 (옆 dragstart, dragover, drop, 그리고 dragend이 질문에 국한되지 않는 핸들러, 그래서 여기에 복사되지 않습니다)

/*##############################################################################
##                              Dragenter Handler                             ##
##############################################################################*/

// When dragging over the text node of a table cell (the text in a table cell),
// while previously being over the table cell element, the dragleave event gets
// fired, which stops the highlighting of the currently dragged cell. To avoid
// this problem and any coding around to fight it, everything has been
// programmed with the dragenter event handler only; no more dragleave needed

// For the dragenter event, e.target corresponds to the element into which the
// drag enters. This fact has been used to program the code as follows:

var previousRow = null;

function handleDragEnter(e) {
  // Assure that dragenter code is only executed when entering an element (and
  // for example not when entering a text node)
  if (e.target.nodeType === 1) {
    // Get the currently entered row
    let currentRow = this.closest('tr');
    // Check if the currently entered row is different from the row entered via
    // the last drag
    if (previousRow !== null) {
      if (currentRow !== previousRow) {
        // If so, remove the class responsible for highlighting it via CSS from
        // it
        previousRow.className = "";
      }
    }
    // Each time an HTML element is entered, add the class responsible for
    // highlighting it via CSS onto its containing row (or onto itself, if row)
    currentRow.className = "ready-for-drop";
    // To know which row has been the last one entered when this function will
    // be called again, assign the previousRow variable of the global scope onto
    // the currentRow from this function run
    previousRow = currentRow;
  }
}

이 코드는 초보자에게도 적합하도록 매우 기본적인 주석이 코드에 남았습니다. 희망이 당신을 도울 것입니다! 물론 위에서 언급 한 모든 이벤트 리스너를 각 테이블 셀에 추가해야합니다.


0

다음은 이벤트 타이밍을 기반으로 한 다른 접근 방식입니다.

dragenter자식 요소에서 전달 된 이벤트는 부모 요소에 의해 캡처 될 수 있으며 항상 dragleave. 이 두 이벤트 사이의 타이밍은 실제로 가능한 모든 마우스 동작보다 짧습니다. 따라서 아이디어는 dragenter발생 시간을 기억하고 dragleave이후에 "너무 빠르지 않은"이벤트를 필터링 하는 것입니다.

이 간단한 예제는 Chrome 및 Firefox에서 작동합니다.

var node = document.getElementById('someNodeId'),
    on   = function(elem, evt, fn) { elem.addEventListener(evt, fn, false) },
    time = 0;

on(node, 'dragenter', function(e) {
    e.preventDefault();
    time = (new Date).getTime();
    // Drag start
})

on(node, 'dragleave', function(e) {
    e.preventDefault();
    if ((new Date).getTime() - time > 5) {
         // Drag end
    }
})

0

pimvdb ..

dragleave 대신 drop 을 사용해보십시오 . 그것은 나를 위해 일했다. 이것이 문제를 해결하기를 바랍니다.

jsFiddle을 확인하십시오 : http://jsfiddle.net/HU6Mk/118/

$('#drop').bind({
                 dragenter: function() {
                     $(this).addClass('red');
                 },

                 drop: function() {
                     $(this).removeClass('red');
                 }
                });

$('#drag').bind({
                 dragstart: function(e) {
                     e.allowedEffect = "copy";
                     e.setData("text/plain", "test");
                 }
                });

2
사용자가 마음을 바꾸고 파일을 브라우저 밖으로 끌어다 놓으면 빨간색으로 유지됩니다.
ZachB

0

transitioning플래그와 함께 타임 아웃을 사용 하고 맨 위 요소를들을 수 있습니다 . dragenter/ dragleave하위 이벤트에서 컨테이너까지 버블 링됩니다.

때문에 dragenter전에 자식 요소 화재에 dragleave컨테이너의, 우리는이 1ms에 대한 전환으로 플래그 쇼를 설정합니다 ... dragleave이 1ms 전에 플래그를 확인합니다 리스너는 최대입니다.

플래그는 자식 요소로 전환하는 동안에 만 참이되고 (컨테이너의) 부모 요소로 전환 할 때 참이되지 않습니다.

var $el = $('#drop-container'),
    transitioning = false;

$el.on('dragenter', function(e) {

  // temporarily set the transitioning flag for 1 ms
  transitioning = true;
  setTimeout(function() {
    transitioning = false;
  }, 1);

  $el.toggleClass('dragging', true);

  e.preventDefault();
  e.stopPropagation();
});

// dragleave fires immediately after dragenter, before 1ms timeout
$el.on('dragleave', function(e) {

  // check for transitioning flag to determine if were transitioning to a child element
  // if not transitioning, we are leaving the container element
  if (transitioning === false) {
    $el.toggleClass('dragging', false);
  }

  e.preventDefault();
  e.stopPropagation();
});

// to allow drop event listener to work
$el.on('dragover', function(e) {
  e.preventDefault();
  e.stopPropagation();
});

$el.on('drop', function(e) {
  alert("drop!");
});

jsfiddle : http://jsfiddle.net/ilovett/U7mJj/


0

드래그 대상의 모든 자식 객체에 대한 포인터 이벤트를 제거해야합니다.

function disableChildPointerEvents(targetObj) {
        var cList = parentObj.childNodes
        for (i = 0; i < cList.length; ++i) {
            try{
                cList[i].style.pointerEvents = 'none'
                if (cList[i].hasChildNodes()) disableChildPointerEvents(cList[i])
            } catch (err) {
                //
            }
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.