it-swarm.com.ru

HTML5 dragleave срабатывает при наведении на дочерний элемент

У меня проблема в том, что событие 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, найдите CSS pointer-events: none;, как описано в ответе @ H.D. ниже, который работает в современных браузерах и IE11.

238
pimvdb

Вам просто нужно сохранить счетчик ссылок, увеличивать его, когда вы получаете перетаскиватель, уменьшать, когда вы получаете перетаскивание. Когда счетчик на 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');
        }
    }
});

Примечание. В событии сброса сбросьте счетчик до нуля и очистите добавленный класс.

Вы можете запустить его здесь

278
Woody

Можно ли предотвратить запуск Dragleave при перетаскивании в дочерний элемент?

Да.

#drop * {pointer-events: none;}

Этого CSS достаточно для Chrome.

При использовании его с Firefox, #drop не должно иметь текстовых узлов напрямую (иначе есть странная проблема , когда элемент «оставь это себе» ), поэтому я предлагаю оставить его только с одним элементом (например, используйте div внутри #drop, чтобы поместить все внутрь)

Вот jsfiddle , решающий пример оригинальный вопрос (неработающий) .

Я также сделал упрощенную версию , разветвленную из примера @Theodore Brown, но основанную только на этом CSS.

Однако не во всех браузерах реализован этот CSS: http://caniuse.com/pointer-events

Увидев исходный код Facebook, я мог найти этот pointer-events: none; несколько раз, однако он, вероятно, использовался вместе с грациозным снижением производительности. По крайней мере, это так просто и решает проблему для многих сред.

62
H.D.

Здесь самое простое кросс-браузерное решение (серьезно):

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

В нескольких словах, вы создаете «маску» внутри зоны сброса, с унаследованной шириной и высотой, абсолютной позицией, которая будет просто отображаться, когда начинается драгувер.
Итак, после показа этой маски вы можете сделать трюк, прикрепив к нему другие события Dragleave & Drop.

После ухода или падения вы просто снова прячете маску. 
Просто и без осложнений.

(Обс .: совет Грега Петтита - Вы должны быть уверены, что маска нависает над всей коробкой, включая границу)

38
Diego T. Yamaguchi

«Правильный» способ решить эту проблему - отключить события указателя на дочерних элементах цели удаления (как в ответе @ H.D.). Вот созданный мной jsFiddle, который демонстрирует эту технику . К сожалению, это не работает в версиях Internet Explorer до IE11, так как они не поддерживают события указателя .

К счастью, мне удалось найти обходной путь, который делает работает в старых версиях IE. По сути, это включает в себя выявление и игнорирование событий dragleave, которые происходят при перетаскивании дочерних элементов. Поскольку событие dragenter запускается на дочерних узлах до события dragleave на родительском узле, к каждому дочернему узлу можно добавить отдельных прослушивателей событий, которые добавляют или удаляют класс ignore-drag-left из цели удаления. Тогда прослушиватель событий dragleave цели удаления может просто игнорировать вызовы, которые происходят, когда этот класс существует. Вот jsFiddle, демонстрирующий этот обходной путь . Он протестирован и работает в Chrome, Firefox и IE8 +.

Обновление:

Я создал jsFiddle, демонстрирующий комбинированное решение с использованием функции обнаружения, где используются события указателя, если они поддерживаются (в настоящее время Chrome, Firefox и IE11), и браузер возвращается к добавлению событий в дочерние узлы, если поддержка событий указателя не поддерживается. t доступно (IE8-10).

32
Theodore Brown

Прошло довольно много времени после того, как этот вопрос был задан, и было предложено множество решений (включая некрасивые хаки). 

Мне удалось решить ту же проблему, с которой я столкнулся недавно благодаря ответу в этом answer , и подумал, что это может быть полезно для тех, кто переходит на эту страницу . Вся идея заключается в том, чтобы сохранить evenet.target в ondrageenter каждый раз, когда он вызывается для любого из родительских или дочерних элементов. Затем в ondragleave проверьте, совпадает ли текущая цель (event.target) с объектом, который вы сохранили в ondragenter.

Единственный случай, когда эти два значения совпадают, это когда ваше перетаскивание покидает окно браузера.

Причина, по которой это работает нормально, заключается в том, что мышь покидает элемент (скажем, el1) и вводит другой элемент (скажем, el2), сначала вызывается el1.ondragenter, а затем el2.ondragleave. Только когда перетаскивание покидает/входит в окно браузера, event.target будет '' как в el1.ondragenter, так и el2.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) намного больше, когда файл перетаскивается на экран, чтобы пользователь мог легко поместить файлы в нужное место.

25
Ali Motevallian

Проблема заключается в том, что событие 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');
               }
           }
10
Greg

Очень простое решение - использовать CSS-свойство pointer-events . Просто установите его значение none после dragstart для каждого дочернего элемента. Эти элементы больше не будут вызывать связанные с мышью события, поэтому они не будут ловить мышь над ними и, следовательно, не будут вызывать dragleave на родительском элементе. 

Не забудьте установить это свойство обратно на auto при завершении перетаскивания;)

8
FruityFred

Вы можете исправить это в Firefox с небольшим вдохновением из исходного кода jQuery :

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

    if (related !== this) {

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

        if (!inside) {

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

}

К сожалению, это не работает в Chrome, потому что relatedTarget , кажется, не существует в событиях dragleave, и я предполагаю, что вы работаете в Chrome, потому что ваш пример не работал в Firefox. Вот версия с реализованным выше кодом.

8
robertc

И вот оно, решение для 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!');
                    }
                })
7
Aldekein

Вот еще одно решение с использованием 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');
   }
}

Надеюсь, что это работает, вот скрипка .

6
marcelj

Я написал небольшую библиотеку под названием Dragster для решения этой конкретной проблемы, работает везде, кроме тихого бездействия в IE (который не поддерживает конструкторы событий DOM, но написать его было бы довольно легко что-то похожее, используя пользовательские события jQuery)

5
Ben

Не уверен, что это кросс-браузер, но я тестировал в Chrome, и это решает мою проблему:

Я хочу перетащить файл на всю страницу, но моя перетаскивание срабатывает, когда я перетаскиваю дочерний элемент. Я решил посмотреть на х и у мыши:

у меня есть 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;
});
4
chrisallick

У меня возникла та же проблема, и я попытался использовать решение pk7s. Это сработало, но можно было бы сделать немного лучше без каких-либо дополнительных элементов DOM.

По сути, идея та же самая - добавить дополнительное невидимое наложение поверх области опускания. Только давайте делать это без каких-либо дополнительных элементов DOM. Вот та часть, где псевдоэлементы CSS играют роль.

Javascript

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/

Я надеюсь, что это помогает любому с той же проблемой.

4
rasmusx

Простое решение - добавить правило css pointer-events: none в дочерний компонент, чтобы предотвратить запуск 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>

4
Alexandre Annic

Я столкнулся с той же проблемой, и вот мое решение, которое, я думаю, намного проще, чем выше. Я не уверен, что это кроссбраузер (может зависеть даже от порядка пузырьков)

Я буду использовать 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)

И это все. :) это работает, потому что хотя onEnter на дочернем объекте срабатывает перед onLeave на родительском, мы немного задерживаем его, изменяя порядок, поэтому сначала класс удаляется, а затем повторно вызывается через миллисекунду.

3
Marcin Raczkowski

Просто проверьте, является ли перетаскиваемый элемент дочерним, если он есть, то не удаляйте свой класс стиля dragover. Довольно просто и работает для меня: 

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

если вы используете 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

3
azlar

Альтернативное рабочее решение, немного проще.

//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) {
2
Profet

Потратив так много часов, я понял, что это предложение работает точно так, как задумано. Я хотел предоставить сигнал только тогда, когда файлы перетаскивались, а при перетаскивании документов, dragleave вызывал болезненные мерцания в браузере 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
});
2
visitsb

Я написал модуль перетаскивания под названием drip-drop , который, среди прочего, исправляет это странное поведение. Если вы ищете хороший низкоуровневый модуль перетаскивания, который вы можете использовать в качестве основы для чего-либо (загрузка файла, перетаскивание в приложении, перетаскивание из или на внешние источники), вы должны проверить это модуль из:

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

Событие "dragleave" возникает, когда указатель мыши покидает область перетаскивания целевого контейнера.

Это имеет большой смысл, поскольку во многих случаях только droppable может быть только родитель, а не потомки . Я думаю, event.stopPropogation () должен был обработать этот случай, но, похоже, это не так. Трюк.

Вышеупомянутые решения, кажется, работают в большинстве случаев, но не работают в случае тех дочерних элементов, которые не поддерживают события dragenter/dragleave, такие как iframe

Обходной путь - проверить 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
Lalit Nankani

У меня была похожая проблема - мой код для скрытия dropzone на событии dragleave для body был запущен одновременно, когда парящие дочерние элементы вызывали мерцание dropzone в Google Chrome.

Я смог решить эту проблему, запланировав функцию скрытия дропзоны, а не вызывая ее сразу. Затем, если запускается другой драговер или драглэйв, запланированный вызов функции отменяется.

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

Вы можете использовать тайм-аут с флагом transitioning и прослушивать верхний элемент. dragenter/dragleave из дочерних событий будет пузыриться до контейнера.

Так как dragenter в дочернем элементе срабатывает перед dragleave контейнера, мы установим флаг show как переходящий на 1 мс ... слушатель 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
ilovett

используйте этот код http://jsfiddle.net/HU6Mk/258/ :

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

         dragleave: function(event) {
             var x = event.clientX, y = event.clientY,
                 elementMouseIsOver = document.elementFromPoint(x, y);
             if(!$(elementMouseIsOver).closest('.red').length) {
                 $(this).removeClass('red');
             }
        }
    });
0
Mohammad Rezaei

Вот еще один подход, основанный на времени событий.

Событие 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
smrtl

Вам необходимо удалить события указателя для всех дочерних объектов цели перетаскивания.

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) {
                //
            }
        }
    }
0
EddieB

Просто попробуйте использовать event.eventPhase. Он будет установлен на 2 (Event.AT_TARGET), только если цель не введена, в противном случае он будет установлен на 3 (Event.BUBBLING_PHASE).

Я использовал EventPhase, чтобы связать или развязать событие Dragleave.

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

  if(e.eventPhase === Event.AT_TARGET) {

    $('.dropzone').addClass('drag-over');

    $('.dropzone').on('dragleave', function(e) {
      $('.dropzone').removeClass('drag-over');
    });

  }else{

    $('.dropzone').off('dragleave');

  }
})

Guido

0
GNaruhn

Я нашел похожее, но более элегантное решение для ответа @ azlar, и вот мое решение:

$(document).on({
    dragenter: function(e) {
        e.stopPropagation();
        e.preventDefault();
        $("#dragging").show();
    },
    dragover: function(e) {
        e.stopPropagation();
        e.preventDefault();
    },
    dragleave: function(e) {
        e.stopPropagation();
        e.preventDefault();
        if (e.clientX <= 0 ||
            // compare clientX with the width of browser viewport
            e.clientX >= $(window).width() ||
            e.clientY <= 0 ||
            e.clientY >= $(window).height())
            $("#dragging").hide();
    }
});

Этот метод определяет, покинула ли мышь страницу…. Он хорошо работает в Chrome и Edge.

0
zysite

Это довольно простое решение работает для меня до сих пор, при условии, что ваше событие прикреплено к каждому элементу перетаскивания индивидуально. 

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

Очень простое решение:

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

pimvdb ..

Почему бы вам не попробовать использовать drop вместо dragleave. Это сработало для меня. надеюсь, что это решит вашу проблему. 

Пожалуйста, проверьте 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");
                 }
                });
0
abhi