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

189

Проблема, с которой я сталкиваюсь, заключается в том, что событие 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.

  • 0
    Обнаруженная ошибка pimvdb все еще существует в Webkit по состоянию на май 2012 года. Я противостоял ей, также добавив класс в dragover, который не очень близок к nice, так как он запускается так часто, но, похоже, немного исправляет проблему.
  • 1
    @ajm: Спасибо, это работает до определенной степени. Однако в Chrome при входе в дочерний элемент или выходе из него возникает вспышка, возможно, потому, что в этом случае dragleave .
Показать ещё 6 комментариев
Теги:
javascript-events
drag-and-drop

26 ответов

217

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

Примечание. В событии drop, reset counter to zero и очистите добавленный класс.

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

  • 7
    О боже, это самое очевидное решение, и только один голос ... Давай, люди, вы можете сделать лучше. Я думал об этом, но, увидев уровень сложности первых нескольких ответов, я почти отбросил его. У тебя были недостатки?
  • 0
    Привет @ArthurCorenzan - спасибо. Нет - все работает нормально, на самом деле я нашел еще более простой способ сделать это, как показано выше.
Показать ещё 26 комментариев
35

Здесь простейшее решение Cross-Browser (серьезно):

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

В нескольких словах вы создаете "маску" внутри dropzone с унаследованностью по ширине и высоте, абсолютной положением, которая будет отображаться только при запуске dragover.
Итак, после показа этой маски, вы можете сделать трюк, добавив другие dragleave и отбросить события на нем.

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

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

  • 0
    Не знаю почему, но он не работает согласованно с Chrome. Иногда при выходе из зоны маска остается видимой.
  • 2
    На самом деле, это граница. Имейте маску, перекрывающую границу, без собственной границы, и она должна работать хорошо.
Показать ещё 5 комментариев
30

Можно ли предотвратить включение 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; несколько раз, однако он, вероятно, использовался вместе с изящными отступниками деградации. По крайней мере, это так просто и решает проблему для многих сред.

  • 0
    Свойство указателя-события является правильным решением в будущем, но, к сожалению, оно не работает в IE8-IE10, которые все еще широко используются. Кроме того, я должен отметить, что ваш текущий jsFiddle даже не работает в IE11, поскольку он не добавляет необходимые прослушиватели событий и предотвращает поведение по умолчанию.
  • 2
    Использование событий указателя действительно хороший ответ, я немного потрудился, прежде чем сам выяснил, что ответ должен быть выше.
Показать ещё 5 комментариев
25

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

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

Update:

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

  • 0
    Этот ответ показывает обходной путь для нежелательного увольнения, но игнорирует вопрос "Можно ли предотвратить выстрел драгла при перетаскивании в дочерний элемент?" полностью.
  • 0
    Поведение может стать странным при перетаскивании файла из-за пределов браузера. В Firefox у меня есть «Ввод дочернего элемента -> Ввод родителя -> Оставляющий ребенка -> Ввод дочернего элемента -> Выйти дочернего элемента», не оставляя родителя, который остался с классом «более». Старому IE потребуется замена attachEvent для addEventListener.
Показать ещё 4 комментария
17

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

Мне удалось решить ту же самую проблему, которую я недавно получил благодаря ответу в этом ответе и подумал, что это может быть полезно для кого-то, кто выходит на эту страницу. Вся идея состоит в том, чтобы хранить 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) намного больше всякий раз, когда файл перетаскивается на экран, чтобы пользователь мог легко отбросить файлы в нужное место.

  • 3
    Это лучшее решение, оно лучше счетчика (особенно если вы делегируете события) и работает также с перетаскиваемыми дочерними элементами.
  • 1
    Очень элегантно, спасибо, я использовал эту концепцию.
Показать ещё 2 комментария
8

Проблема заключается в том, что событие 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 как бы то ни было. Не могли бы вы предоставить рабочую скрипку своего хака?
  • 2
    Я тоже думал об этом, проверяя координаты. Ты сделал большую часть работы для меня, спасибо :). Однако мне пришлось внести некоторые коррективы: if (ex >= (rect.left + rect.width) || ex <= rect.left || ey >= (rect.top + rect.height) || ey <= rect.top)
Показать ещё 4 комментария
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!');
                    }
                })
  • 0
    Я не могу заставить это работать. Не удается найти event.clientX
  • 0
    Это входит в «if (typeof event.clientX === 'undefined')»?
Показать ещё 2 комментария
7

Вы можете исправить это в 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. Здесь реализована версия с указанным выше кодом.

  • 0
    Большое спасибо, но на самом деле это Chrome, в котором я пытаюсь решить эту проблему.
  • 5
    @pimvdb Я вижу, вы зарегистрировали ошибку , я просто оставлю здесь ссылку на случай, если кто-нибудь еще столкнется с этим ответом.
Показать ещё 2 комментария
5

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

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

  • 0
    очень умно, спасибо;)
  • 0
    Пожалуйста ;)
4

Здесь другое решение, использующее 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');
   }
}

Надеюсь, что это работает, здесь fiddle.

  • 0
    Кажется, это тоже работает.
  • 2
    Это не сработало для меня из коробки. Мне нужно было использовать event.clientX, event.clientY , поскольку они event.clientX, event.clientY к области просмотра, а не к странице. Я надеюсь, что это помогает другой потерянной душе там.
4

У меня была такая же проблема и я попытался использовать решение 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/

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

  • 1
    Изредка не работает на хроме (не ловит драглэйв должным образом)
4

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

  • 0
    Очень полезно (по крайней мере, для меня, где я забочусь только о Chrome).
4

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

Я хочу перетащить файл по всей странице, но мой dragleave запускается, когда я перетаскиваю дочерний элемент. Моим решением было посмотреть на 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
    Хаки, но умные. Мне нравится. Работал на меня.
  • 1
    О, как бы мне хотелось, чтобы за этот ответ было больше голосов! Спасибо
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)

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

  • 0
    Единственное, что делает этот фрагмент, это предотвращает удаление класса «hovered» путем его повторного применения в следующем цикле тиков (что делает событие «dragleave» бесполезным).
  • 0
    Это не бесполезно. Если вы оставите родителя, он будет работать как положено. Сила этого решения в том, что оно простое, не идеальное и не лучшее. Лучшим решением было бы отметить вход на дочернем элементе, в проверке onleave, если мы только что внесли дочерний элемент, и если нет, вызвать событие отпускания. Тем не менее, вам понадобится тестирование, дополнительная охрана, проверка для внуков и т. Д.
2

Я написал модуль перетаскивания под названием 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 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) {
  • 0
    Это не всегда работает в Chrome. Иногда я получаю clientX выше 0, когда мышь находится за пределами поля. Конечно, мои элементы являются position:absolute
  • 0
    Это происходит постоянно или только иногда? Потому что, если мышь движется слишком быстро (например, за пределами окна), вы можете получить неправильные значения.
Показать ещё 1 комментарий
2

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

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

 $yourElement.on('dragleave dragend drop', function(e) {
      if(!$yourElement.has(e.target).length){
           $yourElement.removeClass('is-dragover');
      }
  })
  • 0
    Похоже, самое простое решение для меня, и это решило мою проблему. Спасибо!
1

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

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

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);
  • 0
    Это то, что я тоже делал, но все еще отрывочно.
0

Если вы используете HTML5, вы можете получить родительский клиентRect:

let parentRect = 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

  • 1
    Отличный ответ.
0

Просто попробуйте использовать event.eventPhase. Он будет установлен на 2 (Event.AT_TARGET) только в том случае, если цель ist введена, в противном случае она установлена ​​в 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');

  }
})

Гвидо

0

используйте этот код 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

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

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

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

Так как dragenter для дочернего элемента запускается перед dragleave контейнера, мы установим показ флага как переход на 1 мс... слушатель dragleave проверяет флаг перед тем, как 1 мс будет вверх.

Флаг будет истинным только при переходе к дочерним элементам и не будет истинным при переходе к родительскому элементу (контейнера)

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

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");
                 }
                });
  • 2
    Если пользователь передумает и перетянет файл обратно из браузера, он останется красным.
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
    }
})

Ещё вопросы

Сообщество Overcoder
Наверх
Меню