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

내가 겪고있는 문제 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 를 찾으십시오 .



답변

당신은 참조 카운터를 유지하고 드래그를 얻을 때 증가하고 드래그 리브를 얻을 때 감소해야합니다. 카운터가 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으로 재설정하고 추가 된 클래스를 지우십시오.

여기서 실행할 수 있습니다


답변

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

예.

#drop * {pointer-events: none;}

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

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

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

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

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

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


답변

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

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 조언-마스크가 테두리를 포함한 전체 상자를 가리켜 야합니다)


답변

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

나는이 답변 에 대한 답변 덕분에 최근에 겪었던 것과 동일한 문제를 해결할 수 있었고이 페이지를 방문하는 사람에게 도움이 될 것이라고 생각했습니다. 전체 아이디어는 부모 또는 자식 요소 중 하나에서 호출 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)를 훨씬 더 크게 만들어 사용자가 파일을 적절한 위치에 쉽게 놓을 수 있습니다.


답변

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

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

최신 정보:

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


답변

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


답변

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입니다