자식 요소 위로 드래그하면 부모 요소의 'dragleave'가 발생합니다.
개요
다음 HTML 구조 를 가지고 있으며 요소에 dragenter
and 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
명령이 실행되지만 멋지지 않습니다.
이 동작은 두 가지 이유로 문제가 있습니다.
나는
dragenter
&dragleave
에 첨부<div id="dropzone">
하기 때문에 어린이 요소에 왜 이러한 이벤트가 첨부되어 있는지 이해할 수 없습니다.나는
<div id="dropzone">
아이들을 가리킬 때 여전히 요소를 드래그 하고dragleave
있으므로 발사 하고 싶지 않습니다 !
jsFiddle
http://jsfiddle.net/yYF3S/2/ 와 함께 땜질하는 jsFiddle은 다음과 같습니다.
질문
따라서 ... <div id="dropzone">
요소 위로 파일을 드래그 할 때 dragleave
하위 요소를 드래그하더라도 실행되지 않도록하려면 어떻게해야합니까? <div id="dropzone">
요소를 떠날 때만 실행되어야합니다 . 요소의 경계 내에서 마우스를 움직이거나 끌어다 놓으면 이벤트가 트리거 되지 않아야 합니다 dragleave
.
적어도 HTML5 드래그 앤 드롭을 지원하는 브라우저에서 브라우저 간 호환이 가능해야 하므로이 답변 이 적합하지 않습니다.
Google과 Dropbox가 이것을 알아 낸 것처럼 보이지만 소스 코드가 축소 / 복잡해 구현에서이를 파악할 수 없었습니다.
자식 요소에 이벤트를 바인딩 할 필요가 없으면 항상 pointer-events 속성을 사용할 수 있습니다.
.child-elements {
pointer-events: none;
}
나는 마침내 내가 만족하는 해결책을 찾았다. 실제로 원하는 것을 수행하는 여러 가지 방법을 찾았지만 현재 솔루션만큼 성공하지 못했습니다 ... 한 솔루션에서 #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가 두 번 호출 dragenter
되어 내 카운터가 꺼져 있기 때문에 Firefox에서 문제가 발생 했습니다. 그러나 그럼에도 불구하고 매우 우아한 해결책은 아닙니다.
그런 다음이 질문을 우연히 발견했습니다 : 창 밖으로 드래그 할 때 Firefox에서 dragleave 이벤트를 감지하는 방법
그래서 나는 대답을 받아 내 상황에 적용했습니다.
$.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는 아직 테스트하지 않았습니다).
이것은 조금 못 생겼지 만 젠장 작동합니다! ...
'dragenter'처리기에서 event.target (클로저 내부의 변수 등)을 저장 한 다음 'dragleave'처리기에서 event.target === 저장 한 코드 만 실행하십시오.
원하지 않는 경우 (예 : 자식 요소를 떠난 후 입력 할 때) '급격한'발동이 발생하면 마우스가 부모를 떠나기 전에 마지막으로 발사 될 때 부모가 항상 부모에 있습니다. 의도 된 'dragleave'앞의 마지막 'dragenter'.
(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");
}
});
}());
처음에는 pointer-events: none
접근 방식을 포기하는 사람들과 동의했습니다 . 그러나 나는 나 자신에게 물었다 :
드래그가 진행되는 동안 자식 요소에서 작동하려면 포인터 이벤트가 정말로 필요 합니까?
내 경우에는, 내가 물건의 많은 등, 인라인 편집 추가 작업을위한 버튼을 표시 예를 들어, 가져가, 아이들에 어떤 일이 일어나고있다 ... 그러나 아무도 그 필요하거나 원하는 사실에없는 동안 드래그.
필자의 경우 다음과 같이 부모 컨테이너의 모든 자식 노드에 대해 포인터 이벤트를 선택적으로 해제합니다.
div.drag-target-parent-container.dragging-in-progress * {
pointer-events: none;
}
내가 좋아하는 접근 방식을 사용하여 / 이벤트 핸들러 dragging-in-progress
에서 클래스를 추가 / 제거하십시오 . 알.dragEnter
dragLeave
dragStart
이것은 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>
문제는 드롭 존 내부의 요소는 물론 드롭 존의 일부이며 자식을 입력하면 부모를 떠나는 것입니다. 이것을 해결하는 것은 쉽지 않습니다. 자녀에게 이벤트를 추가해도 부모에게 클래스를 다시 추가 할 수 있습니다.
$("#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;
});
dropzone에 대해서만 dragleave 이벤트를 정의하십시오.
으로 benr는 언급 이 답변 , 당신은 이벤트에 대한 화재에 자식 노드를 방지 할 수 있지만, 당신은 몇 가지 이벤트를 바인딩해야하는 경우,이 작업을 수행 :
#dropzone.dragover *{
pointer-events: none;
}
그리고 이것을 JS 코드에 추가하십시오 :
$("#dropzone").on("dragover", function (event) {
$("#dropzone").addClass("dragover");
});
$("#dropzone").on("dragleave", function (event) {
$("#dropzone").removeClass("dragover");
});
jQuery를 사용하는 경우 https://github.com/dancork/jquery.event.dragout을 확인 하십시오.
정말 굉장합니다.
진정한 드래그 리브 기능을 처리하기 위해 만들어진 특별 이벤트.
HTML5 드래그 리브 이벤트는 마우스 아웃과 유사하게 작동합니다. 이 플러그인은 드래그하는 동안 mouseleave 스타일 기능을 복제하기 위해 만들어졌습니다.
사용 예 :
$ ( '# myelement'). on ( 'dragout', function (event) {// 당신의 코드});
편집 : 실제로, 그것은 jQuery에 의존하지 않는다고 생각합니다. 아직 코드 없이도 코드를 사용할 수 있습니다.
@ hristo 나는 훨씬 더 우아한 솔루션을 가지고 있습니다. 그것이 사용할 수 있는지 확인하십시오.
당신의 노력은 결국 낭비되지 않았습니다. 처음에는 귀하의 것을 사용했지만 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
});
내 두 센트 : 드롭 존 위에 레이어를 숨긴 다음 드래그 할 때 표시하고 드래그 리브를 대상으로합니다.
데모 : 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;
});
그래서 나를 위해 접근 방식 pointer-events: none;
이 너무 잘 작동하지 않았습니다 ... 그래서 여기 내 대안 솔루션이 있습니다.
#dropzone {
position: relative;
}
#dropzone(.active)::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
content: '';
}
이런 식으로 dragleave
부모 (자식) 나 dragover
자식 요소 는 불가능합니다 . 도움이 되었기를 바랍니다 :)
* dragenter
또는 때 '.active'클래스를 추가 dragleave
합니다. 그러나 당신이 그것없이 일하고 있다면 그냥 수업을 떠나십시오.
광범위하게 테스트되지는 않았지만 현재 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 깜박임 버그 또는 적어도 문제를 일으킨 버그가 수정되었습니다.
내 버전 :
$(".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
의 자식이 아니므로 드롭 영역을 벗어났습니다.
여기에서 가장 간단한 해결책 중 하나 ● ︿ ●
이 바이올린을 보세요 <-상자 안의 파일을 드래그 해보세요
다음과 같이 할 수 있습니다 :
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);
이를 통해 이미 무슨 일이 일어나고 있는지 알아야합니다.
그냥 바이올린을보세요.
비슷한 문제가 있었고이 방법으로 수정했습니다.
문제 : 사용자가 "드롭 영역"(ul 요소)에 요소를 놓을 때 기능 drop (ev)이 발생하지만 불행히도 요소가 하위 요소 중 하나 (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));}
}
사용자가 파일을 공간으로 드래그 할 때 상자 색상이 변경되는 파일 업로드 상자에 대해 직접 구현하려고했습니다.
Javascript와 CSS가 잘 혼합 된 솔루션을 찾았습니다. div
id 가있는 놓기 가능한 영역이 있다고 가정하십시오 #drop
. 이것을 자바 스크립트에 추가하십시오 :
$('#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;
}
따라서 사용자가 요소를 상자 위로 드래그하는 동안 하위 요소는 비활성화됩니다.
자식 요소에 대한 통제력을 잃고 싶지 않기 때문에 여기에 제시된 해결 방법에 만족하지 않았습니다.
그래서 다른 로직 접근법을 사용하여 jquery-draghandler 라는 jQuery 플러그인으로 변환했습니다 . DOM을 조작하지 않으므로 고성능을 보장합니다. 사용법은 간단합니다.
$(document).ready(function() {
$(selector).draghandler({
onDragEnter: function() {
// $(this).doSomething();
},
onDragLeave: function() {
// $(this).doSomethingElse();
}
});
});
DOM 기능을 손상시키지 않고 문제를 완벽하게 처리합니다.
Git 저장소 에서 다운로드, 세부 사항 및 설명 .
나는 실제로 https://github.com/lolmaus/jquery.dragbetter/ 에서 보는 것을 좋아합니다.그러나 가능한 대안을 공유하고 싶었습니다. 내 일반적인 전략은 배경이나 스타일을 자식으로 드래그 할 때 배경 스타일을 자식이 아닌 드롭 영역에 적용하는 것입니다. 그런 다음 놓기 영역을 끌 때 스타일을 제거합니다. 아이를 옮길 때 아이디어는 드롭 존에서 스타일을 제거하더라도 (드래그 리브 발생) 아이를 드래그 할 때 부모 드롭 존에 스타일을 다시 적용하는 것입니다. 문제는 dropzone에서 dropzone의 자식으로 이동할 때 dragleave 전에 자식에서 dragenter가 시작되어 내 스타일이 잘못 적용되는 것입니다. 나를위한 해결책은 타이머를 사용하여 dragenter 이벤트를 메시지 큐로 되돌려 놓아 드래그 리브 후에 처리 할 수있었습니다. 타이머 콜백에서 이벤트에 액세스하기 위해 클로저를 사용했습니다.
$('.dropzone').on('dragenter', function(event) {
(function (event) {
setTimeout(function () {
$(event.target).closest('.dropzone').addClass('highlight');
}, 0);
}) (event.originalEvent);
});
이것은 크롬, 즉 파이어 폭스에서 작동하는 것으로 보이고 dropzone의 어린이 수에 관계없이 작동합니다. 이벤트 재 시퀀싱을 보장하는 시간 초과에 대해 약간 불안하지만, 내 사용 사례에는 잘 작동하는 것 같습니다.
이 답변은 여기에서 찾을 수 있습니다.
자식 요소를 가리킬 때 HTML5 드래그 리브가 시작되었습니다.
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');
}
}
});
나는 이것을 직접 구현하려고했지만 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>
그런 다음 첨부 된 자바 스크립트 파일입니다 : '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;
}
미안하지만 jquery가 아닌 자바 스크립트이지만, 나를 해결하는 가장 논리적 인 방법입니다. 브라우저는 dropenter 전에 (이전 요소의) dropleave를 호출해야합니다 (새로운 요소의 경우 가장 먼저 물건을 떠나기 전에 다른 것으로 들어갈 수 없기 때문에 왜 그런지 이해하지 못합니다!) 드랍 리브는 이렇게 :
function mydropleave(e)
{
e.preventDefault();
e.stopPropagation();
setTimeout(function(e){ //the things you want to do },1);
}
dropdroper는 dropleave 후에 일어날 것입니다. 그게 전부입니다!
greedy : true
의 기능으로 자식을 사용하십시오 droppable
. 그런 다음 첫 번째 레이어에서 클릭 한 이벤트 만 시작하십시오.
'IT' 카테고리의 다른 글
Eclipse IDE에서 키보드 단축키를 수정하는 방법은 무엇입니까? (0) | 2020.06.09 |
---|---|
OS 레벨 구성에서 환경 변수를 제거하는 명령 행 (0) | 2020.06.09 |
SOAP 요청을 보내고 응답을받는 클라이언트 (0) | 2020.06.09 |
Visual Studio Code에 Nuget 패키지 설치 (0) | 2020.06.09 |
$ .each를 사용하여 Json 데이터의 jquery 루프 (0) | 2020.06.09 |