«перетаскивание» родительского элемента срабатывает при перетаскивании дочерних элементов

163

обзор

У меня есть следующий HTML - структура , и я приложил dragenterи 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инструкция, что не круто.

Такое поведение проблематично по 2 причинам:

  1. Я только прикрепление dragenterи dragleaveк <div id="dropzone">так что я не понимаю , почему дети элементы имеют эти события присоединенные также.

  2. Я все еще перетаскиваю <div id="dropzone">элемент, нависая над его дочерними элементами, поэтому я не хочу dragleaveстрелять!

jsFiddle

Вот jsFiddle, с которым можно повозиться: http://jsfiddle.net/yYF3S/2/

Вопрос

Итак ... как сделать так, чтобы при перетаскивании файла поверх <div id="dropzone">элемента dragleaveон не срабатывал, даже если перетаскивал какие-либо дочерние элементы ... он должен срабатывать только при выходе из <div id="dropzone">элемента зависание / перетаскивание в любом месте в пределах границ элемента не должно вызывать dragleaveсобытие.

Мне нужно, чтобы он был совместим с разными браузерами, по крайней мере, в браузерах, которые поддерживают перетаскивание HTML5, поэтому этот ответ не подходит.

Кажется, что Google и Dropbox выяснили это, но их исходный код минимизирован / сложен, поэтому я не смог понять это из их реализации.

Христо
источник
Предотвращение распространения событий черезe.stopPropagation();
Blender
Я думаю, что я уже ... проверить обновление
Христо
Можете ли вы опубликовать демо где-нибудь на jsfiddle.net, люди могут возиться с этим?
Блендер
@ Блендер ... конечно, дай мне пару минут!
Христо
@Blender ... Я обновил свой вопрос с скрипкой
Христо

Ответы:

171

Если вам не нужно привязывать события к дочерним элементам, вы всегда можете использовать свойство pointer-events.

.child-elements {
  pointer-events: none;
}
Бен Рудольф
источник
4
Лучшее решение! Я даже не думал, что смогу решить проблему с помощью CSS таким элегантным способом. Большое спасибо!
serg66
1
О да. Это оно!
mcmlxxxiii
23
Единственным недостатком этого подхода является то, что он уничтожает все события указателя на дочерних элементах (например, они больше не могут иметь отдельные :hoverстили или обработчики событий щелчка). Если вы хотите сохранить эти события, вот еще один обходной путь, который я использовал: bensmithett.github.io/dragster
Бен
2
Объедините с css child selector, чтобы охватить всех детей в вашей зоне: .dropzone * {pointer-events: none;}
Филипп F
7
Вы уже используете jQuery, так что просто $('.element').addClass('dragging-over')к элементу, который вы перетаскиваете, и $('.element').removeClass('dragging-over')когда вы перетаскиваете. Тогда в вашем CSS вы можете иметь .element.dragging-over * { pointer-events: none; }. Это удаляет события указателя из всех дочерних элементов только при перетаскивании.
Гэвин
56

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

Тогда я наткнулся на этот вопрос: как обнаружить событие dragleave в Firefox при перетаскивании за пределы окна

Поэтому я взял ответ и применил его к моей ситуации:

$.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).

Христо
источник
Пока это лучшее решение. Единственная проблема в том, что этот код не работает, когда вы добавляете файл, удаляете его и перетаскиваете поверх него другой файл. Только после поднятия HoverEnd и повторного поднятия HoverStart этот код снова работает.
MysticEarth
@MysticEarth Можете ли вы собрать jsFiddle для демонстрации, когда он не работает?
Христо
1
Спасибо за ваш ответ. Это было действительно полезно, так как интерфейс перетаскивания HTML5 и прослушиватели событий могут быть очень болезненными. Это лучший способ обработки сумасшедших событий, особенно если вы хотите обрабатывать несколько экземпляров элементов зоны перетаскивания.
Нико О
Зачем нам нужно вызывать stopPropagation () предотвращать дефолт () и возвращать false. Вернуть false должно быть достаточно. Это должно?
Dejo
Это избыточно, но звонит stopPropagation()и preventDefault()является предпочтительным. Я бы бросил return false.
Пиджа
38

Это немного некрасиво, но это работает, черт возьми! ...

В вашем обработчике «dragenter» сохраните event.target (в переменной внутри вашего замыкания или где-то еще), затем в вашем обработчике «dragleave» запустите ваш код, только если event.target === тот, который вы сохранили.

Если ваш '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");
        }
    });

}());
hacklikecrack
источник
Я попробую это позже. Во всяком случае, это выглядит чище, чем мое решение, но я не уверен, насколько он совместим с разными браузерами, просто взглянув на него. Где ты это проверял?
Христо
1
только Chrome и может работать только при условии, что между дочерними элементами и контейнером существует физический разрыв.
hacklikecrack
5
Мой любимый ответ здесь. Совсем не страшно :)
Мэтт Уэй
1
Работает для меня в IE, Chrome, FF
Vicentiu Bacioiu
1
Это не работает, если перетаскивание начинается на детей. Например, перетаскивание из чего-либо из другого окна таким образом, что перетаскивание начинается внутри дочернего элемента.
FINDarkside
29

Сначала я согласился с людьми, отказывающимися от pointer-events: noneподхода. Но потом я спросил себя:

Вам действительно нужны события указателя для работы с дочерними элементами во время перетаскивания ?

В моем случае, у меня много чего происходит, например, при наведении курсора на кнопки для дополнительных действий, встроенного редактирования и т. Д. Однако во время перетаскивания ничего из этого не требуется или даже не требуется .

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

  div.drag-target-parent-container.dragging-in-progress * {
    pointer-events: none;
  }

Используйте свой любимый подход , чтобы добавить / удалить класс dragging-in-progressв dragEnter/ dragLeaveобработчиков событий, как я сделал или сделать то же самое dragStart, и др. и др.

bargar
источник
1
Это решение является как наиболее простым, так и (насколько я могу судить) полным доказательством. Просто добавьте класс в dragstartсобытие и удалите его dragend.
cmann
Это довольно хорошее решение, но не идеальное. Если перетаскивание начинается на дочернем элементе (перетаскивание из другого окна), dragEnter никогда не запускается. Возможно, также возможно перемещать мышь достаточно быстро, чтобы dragEnter не срабатывал при переходе к ребенку, но не уверен на 100% в этом.
FINDarkside
12

Это похоже на ошибку 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>
смеситель
источник
спасибо за ответ, Блендер. Я уже попробовал это однако. Проблема с этим решением состоит в том, что оверлейный элемент «перекрывает» дочерние элементы, с которыми мне нужно взаимодействовать ... поэтому наложение отключает взаимодействие, которое мне нужно с дочерними ... воспринимайте это как файл drag-n -Открытое окно ... вы можете перетащить или вы можете нажать элемент ввода, чтобы выбрать ваши файлы.
Христо
Вот пример скрипки, которую ваше решение не удовлетворяет ... jsfiddle.net/UnsungHero97/yYF3S/11
Христо
Я запутался. Зачем вам нужно взаимодействовать и с дочерними элементами?
Блендер
В моем конкретном случае дочерним элементом «dropzone» является кнопка выбора файла ... поэтому, если я не хочу перетаскивать файлы, я могу выбрать их, используя элемент input. Но с наложением ... Я не могу щелкнуть элемент ввода. имеет смысл?
Христо
@Blender В большинстве случаев нам нужно перетащить файл с рабочего стола, а не элемент со страницы, так что это вроде не работает.
Мартиньш Бридис,
5

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

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

Определите событие Dragleave только для dropzone.

Оливер
источник
2
это не решит проблему, хотя. Реальная проблема в том, что dragleave... когда я оставляю ребенка, я все еще могу быть в пределах родителя, #dropzoneно это не вызовет dragenterсобытие снова :(
Христо
Вы уверены, что драгентер снова не выстрелил?
Оливер
Ах, да ... он запускается снова, однако после его запуска происходит dragleaveсобытие для дочернего элемента, который только что вышел, поэтому снова выполняется removeClassинструкция
Христо,
Я не могу определить dragleaveтолько для #dropzone... если бы я мог, у меня не было бы этой проблемы.
Христо
Нет, я имел в виду не добавлять события для dragleave для детей.
Оливер
4

Как упоминалось в этом ответе , вы можете запретить дочерним узлам запускать события, но если вам нужно привязать некоторые события, сделайте следующее:

#dropzone.dragover *{
   pointer-events: none;
}

И добавьте это в свой код JS:

$("#dropzone").on("dragover", function (event) {
   $("#dropzone").addClass("dragover");
});

$("#dropzone").on("dragleave", function (event) {
   $("#dropzone").removeClass("dragover");
});
Вахид Ашрафян
источник
2
Для меня это приводит к тому, что оверлей мигает постоянно.
Андрей Михайлов - lolmaus
3

Если вы используете jQuery, проверьте это: https://github.com/dancork/jquery.event.dragout

Это действительно потрясающе.

Специальное событие, созданное для обработки истинной функциональности Dragleave.

HTML5 событие dragleave работает больше как мышление. Этот плагин был создан для копирования функциональности стиля mouseleave во время перетаскивания.

Пример использования:

$ ('# myelement'). on ('dragout', function (event) {// ВАШ КОД});

РЕДАКТИРОВАТЬ: на самом деле, я не думаю, что это зависит от jQuery, вы можете просто использовать код даже без него.

nkkollaw
источник
Вот Это Да! Dragout - это ЕДИНСТВЕННАЯ вещь, которая работала для моего макета с большим количеством дочерних элементов, dragleaveдаже если я не покинул родительскую зону сброса.
Марк Кассон
После борьбы с этой проблемой в течение последнего часа, dragoutбыло единственным решением, которое работало для меня.
Джейсон Хейзел
2

@ Христо У меня есть более элегантное решение. Проверьте, если это то, что вы могли бы использовать.

Ваши усилия не были потрачены впустую в конце концов. Сначала мне удалось использовать ваш, но у меня были разные проблемы в 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
});
visitsb
источник
я полагаю, что вам не хватает определений для некоторых переменных вне области видимостиvar dropZoneHideDelay=70, dropZoneVisible=true;
Тимо Ховинен
@TimoHuovinen да, правильно. Оба dropZoneHideDelay, dropZoneVisibleнеобходимы для двух 'on'событий документа в моей реализации.
посещения
2

Мои два цента: скройте слой над зоной сброса, затем покажите его при перетаскивании и наведите курсор на него.

Демо: 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;
});
jeremie.b
источник
Просто и эффективно! Я шел по маршруту stopPropagation (), и мне не нравилось применять обработчик событий ко всем детям. Это казалось грязным, но это работает хорошо и просто для реализации. Отличная идея.
Джон
1

Так что для меня подход pointer-events: none;не сработал ... Итак, вот мое альтернативное решение:

    #dropzone {
        position: relative;
    }

    #dropzone(.active)::after {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        content: '';
    }

Таким образом, это невозможно для dragleaveродительского (дочернего) или dragoverдочернего элемента. надеюсь это поможет :)

* .Active-класс, который я добавляю, когда я dragenterили dragleave. Но если вы работаете без этого, просто не посещайте занятия.

MMachinegun
источник
1
Я пробовал это в компоненте D & D EmberJS, и добавление класса 'active' программным путем происходит слишком медленно около 40% времени, когда мышь входит в родительский div (ember-view). Не уверен почему. Но если оставить все как есть, то это прекрасно работает, поэтому спасибо за это простое решение. Протестировано на Mac на последних версиях (на сегодняшний день) Safari, Chrome, Firefox.
rmcsharry
1

Супер простое быстрое решение для этого, не проверено, но работает в 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 или, по крайней мере, ее перестановку, которая вызывала у меня проблемы.

Дом Виньярд
источник
1

Моя версия:

$(".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не является дочерним по отношению к этому блоку, поэтому я знаю, что я вне зоны сброса.

Mortalis
источник
0

Здесь одно из самых простых решений ● ︿ ●

Посмотрите на эту скрипку <- попробуйте перетащить какой-нибудь файл внутрь коробки

Вы можете сделать что-то вроде этого:

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

По этому вы уже должны знать, что здесь происходит.
Знаете, просто посмотрите на скрипку.

Диего Т. Ямагути
источник
0

У меня была похожая проблема, и я исправил ее следующим образом:

Проблема: функция drop (ev) запускается, когда пользователь удаляет элемент в «зоне удаления» (элемент ul), но, к сожалению, также когда элемент сбрасывается в одном из его дочерних элементов (элементов 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));} 
} 
Pao
источник
0

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

Я нашел решение, которое представляет собой хорошее сочетание Javascript и CSS. Скажем, у вас есть зона сброса divс идентификатором #drop. Добавьте это к вашему Javascript:

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

Таким образом, дочерние элементы будут неактивны до тех пор, пока пользователь перетаскивает элемент над полем.

Дэвид А
источник
0

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

Поэтому я использовал другой логический подход, переводя его в плагин jQuery, который называется jquery-draghandler . Он абсолютно не манипулирует DOM, гарантируя высокие характеристики. Его использование просто:

$(document).ready(function() {

    $(selector).draghandler({
        onDragEnter: function() {
            // $(this).doSomething();
        },
        onDragLeave: function() {
            // $(this).doSomethingElse();
        }
    });

});

Он без проблем справляется с проблемой, не ставя под угрозу функциональность DOM.

Скачать, подробности и объяснения в своем репозитории Git .

Люка Фагиоли
источник
Как получить событие Drag Enter в вашем плагине?
Вуди
Я на самом деле не проверял вашу директиву, но думаю, что она не работает, если родительский элемент (A) фактически не окружает элемент, для которого я хочу проверить (B). Например, если между нижним краем B и нижним краем A нет отступов, то для элемента A никогда не сработает dragenter, верно?
marcelj
@marcelj Элемент A может быть самим документом body, поэтому вам не нужен окружающий элемент (body выполняет свою работу) с минимальным заполнением. Ограничение, которое я обнаружил позже с этим подходом, состоит в том, что если вы работаете с frames, и ваш элемент находится на границе этого. В таком случае я не предлагаю такой подход.
Лука Фаджиоли
0

Мне действительно нравится то, что я вижу на https://github.com/lolmaus/jquery.dragbetter/но хотел поделиться возможной альтернативой. Моя общая стратегия заключалась в том, чтобы применить стиль фона к зоне сброса (не к ее дочерним элементам) при перетаскивании ее или любого ее дочернего элемента (посредством всплытия). Затем я удаляю стиль, когда перетаскиваю зону сброса. Идея заключалась в том, что при переходе к ребенку, даже если я удаляю стиль из дропзоны, когда покидаю его (огни драконьей волны), я просто повторно применяю стиль к родительской дропзоне при перетаскивании любого потомка. Проблема, конечно, заключается в том, что при переходе из промежуточной зоны в дочернюю из зоны перетаскивания драгентер срабатывает перед дочерним элементом, поэтому мои стили были применены не по порядку. Решением для меня было использование таймера, чтобы принудительно вернуть событие dragenter обратно в очередь сообщений, что позволило мне обработать его ПОСЛЕ завершения перетаскивания. Я использовал замыкание для доступа к событию по обратному вызову таймера.

$('.dropzone').on('dragenter', function(event) {
  (function (event) {
    setTimeout(function () {
      $(event.target).closest('.dropzone').addClass('highlight');
    }, 0);
  }) (event.originalEvent); 
});

Похоже, что это работает в Chrome, то есть Firefox и работает независимо от количества детей в dropzone. Мне немного не по себе из-за тайм-аута, гарантирующего повторное упорядочение событий, но, похоже, он хорошо работает для моего варианта использования.

mefopolis
источник
0

Я пытался реализовать это сам, и я не хотел Jquery или любой плагин либо.

Я хотел обработать загрузку файла следующим образом:

Структура файлов:

--- / загружает {загружает каталог}

--- /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>

Тогда это прикрепленный файл Javascript :: '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;
}

Анд Калеб
источник
0

Извините, это javascript, а не jquery, но для меня это самый логичный способ решить эту проблему. Браузер должен вызывать dropleave (предыдущего элемента) перед dropenter (или новым элементом, поскольку что-то не может ввести что-то еще, прежде чем покинуть первое, я не понимаю, почему они это сделали! Так что вам просто нужно отложить де дроплэйв так:

function mydropleave(e)
{
    e.preventDefault();
    e.stopPropagation();

    setTimeout(function(e){ //the things you want to do },1);
}

И капельница случится после капли, вот и все!

Entretoize
источник
-1

Используйте greedy : trueдля ребенка как функцию droppable. Затем запускается только событие, щелкнувшее по первому слою.

Адам
источник