„dragleave” elementu macierzystego uruchamia się podczas przeciągania elementów podrzędnych

163

Przegląd

Mam następującą strukturę HTML i dołączyłem zdarzenia dragenteri dragleavedo <div id="dropzone">elementu.

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

Problem

Kiedy przeciągam plik na <div id="dropzone">, dragenterzdarzenie jest uruchamiane zgodnie z oczekiwaniami. Jednak, gdy poruszam myszką na element dziecięcej, takich jak <div id="drag-n-drop">The dragenterzdarzenie jest opalane dla <div id="drag-n-drop">elementu, a następnie dragleavezdarzenie jest opalane dla <div id="dropzone">elementu.

Jeśli <div id="dropzone">ponownie najeżdżam na element, dragenterzdarzenie jest ponownie uruchamiane, co jest fajne, ale potem dragleavezdarzenie jest uruchamiane dla elementu podrzędnego, który właśnie pozostał, więc removeClassinstrukcja jest wykonywana, co nie jest fajne.

To zachowanie jest problematyczne z 2 powodów:

  1. Dołączam tylko dragenter& dragleavedo tego, <div id="dropzone">więc nie rozumiem, dlaczego elementy dziecięce mają również dołączone te zdarzenia.

  2. Ciągle przeciągam po <div id="dropzone">elemencie, najeżdżając na jego elementy podrzędne, więc nie chcę dragleavestrzelać!

jsFiddle

Oto jsFiddle do majstrowania przy: http://jsfiddle.net/yYF3S/2/

Pytanie

Więc ... jak mogę to zrobić tak, że kiedy przeciągam plik po <div id="dropzone">elemencie, dragleavenie uruchamia się, nawet jeśli przeciągam po elementach podrzędnych ... powinien uruchamiać się tylko wtedy, gdy opuszczam <div id="dropzone">element .. . Najechanie kursorem / przeciągnięcie po dowolnym miejscu w granicach elementu nie powinno wywołać dragleavezdarzenia.

Potrzebuję tego, aby był kompatybilny z różnymi przeglądarkami, przynajmniej w przeglądarkach obsługujących przeciąganie i upuszczanie HTML5, więc ta odpowiedź nie jest wystarczająca.

Wygląda na to, że Google i Dropbox to rozgryzły, ale ich kod źródłowy jest zminimalizowany / złożony, więc nie byłem w stanie tego rozgryźć na podstawie ich implementacji.

Hristo
źródło
Zapobiegaj rozprzestrzenianiu się wydarzeń przeze.stopPropagation();
Blender
Myślę, że już ... sprawdź aktualizację
Hristo
Czy możesz opublikować demo gdzieś na jsfiddle.net, a ludzie mogą przy nim majstrować?
Blender
@Blender ... jasne, daj mi kilka minut!
Hristo
@Blender ... Zaktualizowałem moje pytanie skrzypcami
Hristo

Odpowiedzi:

171

Jeśli nie musisz wiązać zdarzeń z elementami podrzędnymi, zawsze możesz użyć właściwości pointer-events.

.child-elements {
  pointer-events: none;
}
Ben Rudolph
źródło
4
Najlepsze rozwiązanie! Nie sądziłem nawet, że uda mi się rozwiązać problem za pomocą css w tak elegancki sposób. Dziękuję Ci bardzo!
serg66
1
O tak. To jest to!
mcmlxxxiii
23
Jedyną wadą tego podejścia jest to, że nukuje wszystkie zdarzenia wskaźnika na elementach potomnych (np. Nie mogą one już mieć oddzielnych :hoverstylów ani funkcji obsługi zdarzeń kliknięcia). Jeśli chcesz zachować te wydarzenia, oto inne obejście, którego używam: bensmithett.github.io/dragster
Ben
2
Połącz z CSS Child Selector, aby dotrzeć do wszystkich dzieci w Twojej strefie zrzutu: .dropzone * {pointer-events: none;}
Philipp F,
7
Używasz już jQuery, więc tylko $('.element').addClass('dragging-over')do elementu, nad którym przeciągasz, i $('.element').removeClass('dragging-over')kiedy przeciągasz. Następnie w swoim CSS możesz mieć .element.dragging-over * { pointer-events: none; }. To usuwa zdarzenia wskaźnika ze wszystkich elementów podrzędnych tylko podczas przeciągania.
Gavin
56

W końcu znalazłem rozwiązanie, z którego jestem zadowolony. Właściwie znalazłem kilka sposobów robienia tego, co chcę, ale żaden nie był tak skuteczny jak obecne rozwiązanie ... w jednym rozwiązaniu doświadczyłem częstego migotania w wyniku dodania / usunięcia obramowania #dropzoneelementu ... w innym obramowanie nigdy nie został usunięty, jeśli najedziesz kursorem poza przeglądarkę.

W każdym razie, moim najlepszym rozwiązaniem jest to:

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

Działa to całkiem nieźle, ale problemy pojawiły się w Firefoksie, ponieważ Firefox wywoływał podwójnie dragenter więc mój licznik był wyłączony. Niemniej jednak jest to niezbyt eleganckie rozwiązanie.

Potem natknąłem się na to pytanie: jak wykryć zdarzenie dragleave w Firefoksie podczas przeciągania poza okno

Więc wziąłem odpowiedź i zastosowałem ją do mojej sytuacji:

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

To jest przejrzyste i eleganckie i wydaje się działać w każdej przeglądarce, którą do tej pory testowałem (jeszcze nie testowałem IE).

Hristo
źródło
Jak dotąd to najlepsze rozwiązanie, jedyny problem polega na tym, że ten kod nie działa po dodaniu pliku, usunięciu go i przeciągnięciu na niego innego pliku. Dopiero po podniesieniu HoverEnd i ponownym podniesieniu HoverStart ten kod znów działa.
MysticEarth
@MysticEarth Czy możesz złożyć jsFiddle do wersji demonstracyjnej, gdy nie działa?
Hristo
1
Dziękuję za Twoją odpowiedź. Było to bardzo pomocne, ponieważ interfejs przeciągania i upuszczania HTML5 oraz nasłuchiwanie zdarzeń mogą być naprawdę uciążliwe. To najlepszy sposób na poradzenie sobie z szalonymi wydarzeniami, zwłaszcza gdy chcesz obsłużyć wiele wystąpień elementów strefy zrzutu.
Nico O
Dlaczego musimy wywoływać stopPropagation () zapobiecDefault () i zwrócić false. Zwrot false powinien wystarczyć. Powinno?
Dejo
Jest zbędny, ale dzwoni stopPropagation()i preventDefault()jest preferowany. Upuściłbym return false.
Peeja
38

To trochę brzydkie, ale działa do cholery! ...

W module obsługi „dragenter” zapisz zdarzenie.target (w zmiennej wewnątrz twojego zamknięcia lub czymkolwiek), a następnie w module obsługi „dragleave” uruchom kod tylko wtedy, gdy event.target === ten, który zapisałeś.

Jeśli twój 'dragenter' odpala, kiedy tego nie chcesz (np. Kiedy wchodzi po opuszczeniu elementów potomnych), to ostatnim razem, gdy odpala, zanim mysz opuści rodzica, jest na rodzicu, więc rodzic zawsze będzie ostatni „dragenter” przed zamierzonym „dragleave”.

(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
źródło
Spróbuję później. Jeśli już, wygląda na czystszy niż moje rozwiązanie, ale nie jestem pewien, czy jest kompatybilny z różnymi przeglądarkami, po prostu patrząc na to. Gdzie to przetestowałeś?
Hristo
1
tylko Chrome i może działać tylko przy założeniu, że między elementami podrzędnymi a kontenerem jest fizyczna przerwa.
hacklikecrack
5
Moja ulubiona odpowiedź tutaj. W ogóle nie czuje się brzydko :)
Matt Way,
1
Działa dla mnie w IE, Chrome, FF
Vicentiu Bacioiu
1
To nie działa, jeśli przeciąganie zaczyna się na dzieciach. Na przykład przeciąganie z czegoś z innego okna w taki sposób, że przeciąganie zaczyna się wewnątrz dziecka.
FINDarkside
29

Na początku zgodziłem się z ludźmi, którzy odrzucili to pointer-events: nonepodejście. Ale wtedy zadałem sobie pytanie:

Czy naprawdę potrzebujesz zdarzeń wskaźnikowych do pracy z elementami podrzędnymi w trakcie przeciągania ?

W moim przypadku wiele rzeczy dzieje się u dzieci, np. Najechanie kursorem, aby wyświetlić przyciski do dodatkowych działań, edycja bezpośrednia itp. Jednak nic z tego nie jest konieczne ani nawet pożądane podczas przeciągania.

W moim przypadku używam czegoś takiego, aby selektywnie wyłączać zdarzenia wskaźnika dla wszystkich węzłów podrzędnych kontenera nadrzędnego:

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

Użyj swojego ulubionego podejścia, aby dodać / usunąć klasę dragging-in-progressw obsłudze zdarzeń dragEnter/ dragLeave, tak jak zrobiłem lub zrobiłem to samo w dragStart, et. glin.

bargar
źródło
1
To rozwiązanie jest zarówno najprostsze, jak i (o ile wiem) w pełni sprawdzone. Po prostu dodaj klasę do dragstartwydarzenia i usuń ją dragend.
cmann
To całkiem fajne rozwiązanie, ale nie idealne. Jeśli przeciąganie rozpocznie się na elemencie podrzędnym (przeciąganie z innego okna), funkcja dragEnter nigdy nie zostanie uruchomiona. Prawdopodobnie możliwe jest również poruszanie myszą na tyle szybko, że dragEnter nie jest uruchamiany, gdy przenosisz się do dziecka, ale nie jest to w 100% pewien.
FINDarkside
12

Wygląda na to, że jest to błąd przeglądarki Chrome.

Jedynym obejściem, jakie mogłem wymyślić, było utworzenie przezroczystego elementu nakładki do przechwytywania wydarzeń: 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>
Mikser
źródło
dzięki za odpowiedź, Blender. Jednak już tego próbowałem. Problem z tym rozwiązaniem polega na tym, że element nakładki „nakłada” elementy podrzędne, z którymi muszę wchodzić w interakcje ... więc posiadanie nakładki wyłącza interakcję, której potrzebuję z dziećmi ... potraktuj to jako plik drag-n -drop box ... możesz przeciągać i upuszczać lub możesz kliknąć element wejściowy, aby wybrać pliki.
Hristo
oto przykład skrzypiec, których twoje rozwiązanie nie spełnia ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo
Jestem zmieszany. Dlaczego musisz również wchodzić w interakcje z elementami podrzędnymi?
Blender
W moim przypadku elementem potomnym „dropzone” jest przycisk wyboru pliku ... więc jeśli nie chcę przeciągać i upuszczać plików, mogę je wybrać za pomocą elementu wejściowego. Ale z nakładką ... nie mogę kliknąć elementu wejściowego. ma sens?
Hristo
@Blender W większości przypadków musimy przeciągnąć plik z pulpitu, a nie element ze strony, więc ten rodzaj nie działa.
Mārtiņš Briedis
5

Problem w tym, że twoje elementy w dropzone są oczywiście częścią dropzone i kiedy wchodzisz do dzieci, opuszczasz rodzica. Rozwiązanie tego nie jest łatwe. Możesz spróbować dodać wydarzenia do dzieci, dodając ponownie klasę do rodzica.

$("#dropzone,#dropzone *").on('dragenter', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

Twoje wydarzenia będą nadal uruchamiane wiele razy, ale nikt nie zobaczy.

// Edycja: użyj zdarzenia dragmove, aby trwale nadpisać zdarzenie dragleave:

$("#dropzone,#dropzone *").on('dragenter dragover', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

Zdefiniuj zdarzenie dragleave tylko dla strefy zrzutu.

Oliver
źródło
2
to jednak nie rozwiąże problemu. Prawdziwy problem polega na tym, że dragleave... kiedy zostawiam dziecko, mogę nadal być z rodzicem, #dropzoneale to nie uruchomi dragenterwydarzenia ponownie :(
Hristo
Czy jesteś pewien, że dragenter nie jest ponownie zwolniony?
Oliver
Ach, racja ... jest ponownie uruchamiany, jednak po uruchomieniu dragleavezdarzenie jest uruchamiane dla elementu podrzędnego, który właśnie pozostał, więc ponownie removeClassinstrukcja jest wykonywana
Hristo
Nie mogę zdefiniować dragleavetylko dla #dropzone... gdybym mógł, nie miałbym tego problemu.
Hristo
Nie, chodziło mi o to, żeby nie dodawać dzieciom wydarzeń związanych z dragleave.
Oliver
4

Jak Benr wspomniał w tej odpowiedzi , możesz zapobiec uruchamianiu przez węzły potomne zdarzeń, ale jeśli chcesz powiązać niektóre zdarzenia, zrób to:

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

I dodaj to do swojego kodu JS:

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

$("#dropzone").on("dragleave", function (event) {
   $("#dropzone").removeClass("dragover");
});
Vahid Ashrafian
źródło
2
U mnie powoduje to ciągłe miganie nakładki.
Andrey Mikhaylov - lolmaus
3

Jeśli używasz jQuery, sprawdź to: https://github.com/dancork/jquery.event.dragout

To naprawdę niesamowite.

Specjalne wydarzenie stworzone w celu obsługi prawdziwej funkcjonalności dragleave.

Zdarzenie dragleave HTML5 działa bardziej jak mouseout. Ta wtyczka została stworzona w celu odtworzenia funkcjonalności stylu mouseleave podczas przeciągania.

Przykład użycia:

$ ('# myelement'). on ('dragout', function (event) {// TWÓJ KOD});

EDYCJA: właściwie nie sądzę, że jest to zależne od jQuery, prawdopodobnie możesz po prostu użyć kodu nawet bez niego.

nkkollaw
źródło
Łał! Dragout to JEDNA rzecz, która działała w moim układzie z dużą ilością elementów podrzędnych, które wystrzeliwują, dragleavemimo że nie opuściłem nadrzędnej strefy zrzutu.
Mark Kasson,
Po walce z tym problemem przez ostatnią godzinę, dragoutbyło to jedyne rozwiązanie, które u mnie zadziałało.
Jason Hazel
2

@hristo Mam dużo bardziej eleganckie rozwiązanie. Sprawdź, czy możesz tego użyć.

Twój wysiłek nie poszedł jednak na marne. Na początku udało mi się użyć twojego, ale miałem inne problemy w FF, Chrome. Po spędzeniu tylu godzin sugestia zadziałała dokładnie tak, jak powinna.

Oto jak wygląda implementacja. Skorzystałem również z wizualnych wskazówek, aby poprawnie poprowadzić użytkowników po strefie zrzutu.

$(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
});
wizyty b
źródło
wydaje się, że brakuje ci definicji niektórych zmiennych spoza zakresu, zakładamvar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen
@TimoHuovinen tak, zgadza się. Oba dropZoneHideDelay, dropZoneVisiblesą potrzebne w dwóch 'on'zdarzeniach dotyczących dokumentów w mojej implementacji.
odwiedziny
2

Moje dwa centy: Ukryj warstwę nad strefą upuszczania, a następnie pokaż ją po przeciągnięciu i wyceluj w dragleave.

Demo: 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
źródło
Proste i skuteczne! Szedłem trasą stopPropagation () i nie podobało mi się stosowanie obsługi zdarzeń do wszystkich dzieci. Wydawało się to niechlujne, ale działa dobrze i jest łatwe do wdrożenia. Dobry pomysł.
Jon Catmull,
1

Więc dla mnie to podejście pointer-events: none;nie zadziałało zbyt dobrze ... Więc oto moje alternatywne rozwiązanie:

    #dropzone {
        position: relative;
    }

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

W ten sposób nie jest to możliwe dla dragleaverodzica (na dziecko) lubdragover element podrzędny. mam nadzieję że to pomoże :)

* Klasa „.active”, którą dodaję, gdy ja dragenterlub dragleave. Ale jeśli pracujesz bez tego, po prostu zostaw klasę.

MMachinegun
źródło
1
Wypróbowałem to w komponencie D&D EmberJS i programowe dodawanie klasy „aktywnej” jest zbyt wolne w około 40% przypadków, gdy mysz wchodzi do nadrzędnego elementu DIV (widok ember). Nie pewny dlaczego. Ale zostawienie tego i działa świetnie, więc dziękuję za to proste rozwiązanie. Przetestowano na komputerze Mac w najnowszych (od dzisiaj) wersjach Safari, Chrome, Firefox.
rmcsharry
1

Super prosta, szybka poprawka, nie testowana szeroko, ale teraz działa w Chrome.

Przepraszam za 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

To naprawia błąd migotania Chrome, a przynajmniej jego permutację, która powodowała problemy.

Dom Vinyard
źródło
1

Moja wersja:

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

W tym układzie:

<div class="dropzone">
  <div class="inner-zone">Inner-zone</div>
</div>

Zrobiłem zrzut klas element e.target, e.currentTarget, e.relatedTargetzarówno dragoveri dragleavewydarzeń.

Pokazało mi, że opuszczając blok rodzica ( .dropzone) e.relatedTargetnie jest dzieckiem tego bloku, więc wiem, że jestem poza strefą zrzutu.

mortalis
źródło
0

Tutaj jedno z najprostszych rozwiązań ● ︿ ●

Spójrz na te skrzypce <- spróbuj przeciągnąć jakiś plik do pudełka

Możesz zrobić coś takiego:

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

Dzięki temu powinieneś już wiedzieć, co się tutaj dzieje.
Po prostu spójrz na skrzypce, wiesz.

Diego T. Yamaguchi
źródło
0

Miałem podobny problem i naprawiłem go w ten sposób:

Problem: funkcja drop (ev) jest uruchamiana, gdy użytkownik upuści element w „drop zone” (element ul), ale niestety także wtedy, gdy element zostanie upuszczony w jednym ze swoich elementów potomnych (element li).

Poprawka:

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
źródło
0

Próbowałem to zaimplementować samodzielnie dla pola przesyłania plików, w którym kolor pudełka zmieniałby się, gdy użytkownicy przeciągali plik w przestrzeń.

Znalazłem rozwiązanie, które jest fajną mieszanką JavaScript i CSS. Powiedzmy, że masz strefę zrzutu divz identyfikatorem #drop. Dodaj to do swojego Javascript:

$('#drop').on('dragenter', function() {
    $(this).addClass('dragover');
    $(this).children().addClass('inactive');
});

$('#drop').on('dragleave', function() {
    $(this).removeClass('dragover');
    $(this).children().removeClass('inactive');
});

Następnie dodaj to do swojego CSS, aby dezaktywować wszystkie dzieci będą klasy .inactive:

#drop *.inactive {
    pointer-events: none;
}

W ten sposób elementy potomne będą nieaktywne tak długo, jak długo użytkownik będzie przeciągał element po ramce.

David A.
źródło
0

Nie czułem się usatysfakcjonowany żadnym z przedstawionych tutaj obejść, ponieważ nie chcę stracić kontroli nad elementami podrzędnymi.

Więc użyłem innego podejścia logicznego, tłumacząc to na wtyczkę jQuery o nazwie jquery-draghandler . Absolutnie nie manipuluje DOM, gwarantując wysoką wydajność. Jego użycie jest proste:

$(document).ready(function() {

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

});

Bezbłędnie rozwiązuje problem, nie naruszając żadnej funkcjonalności DOM.

Pobierz, szczegóły i wyjaśnienia dotyczące repozytorium Git .

Luca Fagioli
źródło
Jak uzyskać zdarzenie drag enter w twojej wtyczce?
Woody
W rzeczywistości nie testowałem twojej dyrektywy, ale myślę, że nie działa, jeśli element nadrzędny (A) w rzeczywistości nie otacza elementu, który chcę sprawdzić (B). Na przykład, jeśli nie ma wypełnienia między dolną krawędzią B a dolną krawędzią A, to dragenter nigdy nie zostanie uruchomiony dla elementu A, prawda?
marcelj
@marcelj Element A może być samym dokumentem, więc tak naprawdę nie potrzebujesz otaczającego elementu (body wykonuje zadanie) z minimalnym wypełnieniem. Ograniczenie, które znalazłem później w tym podejściu, polega na tym, że jeśli pracujesz z frames, a twój element znajduje się na jego granicy. W takim razie nie proponuję takiego podejścia.
Luca Fagioli,
0

Właściwie podoba mi się to, co widzę https://github.com/lolmaus/jquery.dragbetter/ale chciałem podzielić się możliwą alternatywą. Moją ogólną strategią było zastosowanie stylu tła do strefy upuszczenia (a nie jej elementów potomnych) podczas przeciągania do niej lub dowolnego jej elementu podrzędnego (za pomocą bąbelków). Następnie usuwam styl podczas przeciągania strefy zrzutu. Pomysł polegał na tym, że przenosząc się do dziecka, nawet jeśli usunę styl ze strefy upuszczenia, gdy ją opuszczę (ogień dragleave), po prostu ponownie zastosuję styl do strefy upuszczenia rodzica po przeciągnięciu dowolnego dziecka. Problem polega oczywiście na tym, że podczas przechodzenia ze strefy upuszczania do strefy podrzędnej dragenter zostaje wystrzelony na dziecko przed skokiem dragleave, więc moje style zostały zastosowane poza kolejnością. Rozwiązaniem dla mnie było użycie licznika czasu, aby zmusić zdarzenie dragenter z powrotem do kolejki wiadomości, co pozwoliło mi przetworzyć je PO DRagleave. Użyłem zamknięcia, aby uzyskać dostęp do zdarzenia w wywołaniu zwrotnym zegara.

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

Wygląda na to, że działa w Chrome, czyli Firefoxie i działa niezależnie od liczby dzieci w strefie zrzutu. Jestem trochę zaniepokojony limitem czasu gwarantującym ponowne sekwencjonowanie wydarzeń, ale wydaje się, że działa całkiem dobrze w moim przypadku użycia.

mefopolis
źródło
0

Próbowałem to zaimplementować samodzielnie i nie chciałem też Jquery ani żadnej wtyczki.

Chciałem obsłużyć przesyłanie pliku, w następujący sposób uznany za najlepszy dla mnie:

Struktura plików:

--- / uploads {uploads directory}

--- /js/slyupload.js {plik javascript.}

--- index.php

--- upload.php

--- styles.css {tylko trochę stylizacji ...}

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

To jest załączony plik 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;
}

Ande Caleb
źródło
0

Przepraszam, że to javascript, a nie jquery, ale dla mnie jest to najbardziej logiczny sposób rozwiązania tego problemu. Przeglądarka powinna wywołać dropleave (poprzedniego elementu) przed dropenter (nowego elementu, ponieważ coś nie może wprowadzić czegoś innego przed opuszczeniem pierwszej rzeczy, nie rozumiem, dlaczego to zrobili! Więc wystarczy opóźnić) de dropleave w ten sposób:

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

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

A dropenter nastąpi po dropleave, to wszystko!

Entretoize
źródło
-1

Użyj greedy : truedo dziecka jako funkcji droppable. Następnie rozpocznij tylko zdarzenie kliknięte na pierwszej warstwie.

Adam
źródło