"перетягування" батьківських елементів запускається при перетягуванні на дочірні елементи


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 ... впевнений, дай мені пару хвилин!
Христо

@Blender ... Я оновив своє запитання скрипкою
Hristo

Відповіді:


171

Якщо вам не потрібно прив’язувати події до дочірніх елементів, ви завжди можете використовувати властивість pointer-events.

.child-elements {
  pointer-events: none;
}

4
Найкраще рішення! Я навіть не думав, що можу вирішити проблему, використовуючи css настільки елегантно. Велике спасибі!
serg66

1
О так. Це воно!
mcmlxxxiii

23
Єдиним недоліком цього підходу є те, що він занурює всі події вказівника на дочірні елементи (наприклад, вони вже не можуть мати окремих :hoverстилів або обробляти події клацання). Якщо ви хочете зберегти ці події, ось інший спосіб, який я використав: bensmithett.github.io/dragster
Ben

2
Поєднайтеся з дочірнім селектором css, щоб охопити всіх дітей вашої краплі: .dropzone * {pointer-events: none;}
Philipp 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тому мій лічильник вимкнувся. Але, тим не менше, це не дуже елегантне рішення.

Тоді я натрапив на це запитання: як виявити події драглов у 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 () prevenDefault () та повернути false. Повернення помилкового повинно бути достатньо. Чи повинно це?
Деджо

Це зайве, але дзвінок stopPropagation() і preventDefault()є кращим. Я б кинув return false.
Peeja

38

Це трохи некрасиво, але це працює чорт! ...

На вашому оброблювальному пристрої "dragenter" збережіть event.target (у змінній всередині вашого закриття чи будь-що інше), а потім у вашому оброблювальному пристрої "dragleave" запустить ваш код лише, якщо event.target === той, який ви зберегли.

Якщо ваш "драженер" стріляє тоді, коли ви цього не хочете (тобто, коли він входить після залишення дочірніх елементів), то останній раз, коли він вистрілить перед тим, як миша покине батька, це на батьківщині, тому батьків завжди буде остаточний «драженер» перед передбачуваним «драглавом».

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

}());

Спробую це пізніше. Якщо що-небудь, це виглядає чистіше, ніж моє рішення, але я не можу бути впевнений, наскільки сумісний він між браузерами, просто дивлячись на нього. Де ви це протестували?
Христо

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/ dragLeaveevent, як я робив або робив те саме в dragStart, et. ін.


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>

дякую за відповідь, Blender. Я вже пробував це, однак. Проблема цього рішення полягає в тому, що елемент накладення "накладає" дочірні елементи, з якими мені потрібно взаємодіяти ... тому наявність накладення вимикає взаємодію, яка мені потрібна з дітьми ... думати про це як про перетягування файлів -для коробки ... ви можете перетягнути або випустити або натиснути на елемент введення, щоб вибрати свої файли.
Христо

ось приклад загадки, що ваше рішення не задовольняє ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo

Я збентежений. Чому вам також потрібно взаємодіяти з дочірніми елементами?
Блендер

У моєму конкретному випадку дочірнім елементом "dropzone" є кнопка вибору файлів ... тому, якщо я не хочу перетягувати файли, я можу їх вибрати за допомогою елемента введення. Але з накладанням ... Я не можу натиснути на елемент введення. мати сенс?
Христо

@Blender У більшості випадків нам потрібно перетягнути файл із робочого столу, а не елемент зі сторінки, тому це щось не працює.
Mārtiņš Briedis

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... якби я міг, у мене не було б цієї проблеми.
Христо

Ні, я мав на увазі не додавати дітям події для перетягування.
Олівер

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

Це справді приголомшливо.

Спеціальна подія, створена для обробки справжнього функціоналу драглейва.

Подія перетягування HTML5 працює як миші. Цей плагін був створений для копіювання функцій стилю миші під час перетягування.

Приклад використання:

$ ('# myelement'). on ('dragout', функція (подія) {// ВАШ КОД});

EDIT: насправді я не думаю, що це залежить від jQuery, ви можете, ймовірно, просто використовувати код навіть без нього.


Оце Так! Перетягування - ЄДНА річ, яка працювала на моєму макеті з великою кількістю дітей, що стріляли, dragleaveнавіть якщо я не покинув батьківську зону.
Марк Кассон

Після боротьби з цим питанням протягом останньої години dragoutбуло єдиним рішенням, яке працювало на мене.
Джейсон Хазел

2

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

вам здається, вам не вистачає визначень для деяких змінних за межами області, я припускаюvar dropZoneHideDelay=70, dropZoneVisible=true;
Тимо Хуовінен

@TimoHuovinen так, правильно. Обидва dropZoneHideDelay, dropZoneVisibleпотрібні протягом двох 'on'подій документа в моїй реалізації.
відвідуванняb

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

Просто і ефективно! Я спускався маршрутом 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. Але якщо ви працюєте без цього, просто залишайте клас подалі.


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


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

У мене була подібна проблема, і я вирішив її так:

Проблема: падіння функції (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));} 
} 

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 .


Як отримати подію перетягування у ваш плагін?
Вуді

Я фактично не перевірив вашу директиву, але думаю, що вона не працює, якщо батьківський елемент (A) насправді не оточує елемент, на який я хочу перевірити (B). Наприклад, якщо між нижньою кромкою B і нижньою кромкою A немає прокладки, то драгентер ніколи не буде запускатися для елемента A, правда?
marcelj

@marcelj Element A може бути самим документом body, тому вам не потрібен навколишній елемент (body виконує цю роботу) з мінімальним накладом. Обмеження, яке я знайшов пізніше при такому підході, полягає в тому, що якщо ви працюєте з frames, а ваш елемент знаходиться на його межі. У такому випадку я не пропоную такий підхід.
Лука Фагіолі

0

Мені насправді подобається те, що я бачу https://github.com/lolmaus/jquery.dragbetter/але хотів поділитися можливою альтернативою. Моя загальна стратегія полягала в застосуванні фонового стилю до dropzone (а не для її дітей) під час драцентування його чи будь-якої дитини з нього (через бульбашку). Потім я видаляю стиль при перетягуванні зони падіння. Ідея полягала в переході до дитини, навіть якщо я видаляю стиль із краплі зону, коли залишаю його (драглейкає вогонь), я просто повторно застосую стиль до батьківського дропзону, щоб драгувати будь-яку дитину. Проблема, звичайно, полягає в тому, що при переході від дроп-зони до дитини, що випадає, драцентер звільняється над дитиною перед драглейфом, тому мої стилі вийшли з ладу. Для мене вирішенням було використання таймера, щоб примусити події драгенера повернутися до черги повідомлень, що дозволило мені обробити її ПІСЛЯ драглейка. Я використовував закриття для доступу до події під час зворотного дзвінка таймера.

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

Це, здається, працює в хромі, тобто firefox і працює незалежно від кількості дітей у зоні випаду. Мені трохи неприємно з приводу тимчасового очікування, що гарантує перерозподіл подій, але, здається, він працює досить добре для мого використання.


0

Я намагався реалізувати це сам, і я не хотів ні 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>

Потім це доданий файл 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 (з нового елемента, оскільки щось не може ввести щось інше, перш ніж покинути найсильнішу річ, я не розумію, чому вони це зробили! Тому просто потрібно затримати de dropleave так:

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

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

І крапельниця станеться після краплинки, ось і все!


-1

Використовуйте greedy : trueдитину як функцію droppable. Потім запустіть лише подію, натиснувши на перший шар.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.