Problem, który mam, polega na tym, że dragleave
zdarzenie elementu jest uruchamiane podczas najechania na element potomny tego elementu. Ponadto dragenter
nie jest uruchamiany po ponownym najechaniu na element nadrzędny z powrotem.
Zrobiłem uproszczone skrzypce: 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>
z następującym JavaScriptem:
$('#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");
}
});
Powinien to zrobić, powiadamiając użytkownika, czyniąc kroplę div
czerwoną podczas przeciągania czegoś tam. To działa, ale jeśli przeciągniesz na p
dziecko, dragleave
zostanie ono zwolnione i div
nie będzie już czerwone. Przejście z powrotem do kropli div
również nie powoduje, że znów jest czerwone. Konieczne jest całkowite opuszczenie upuszczenia div
i przeciągnięcie z powrotem do niego, aby był czerwony.
Czy można zapobiec dragleave
uruchamianiu podczas przeciągania do elementu potomnego?
Aktualizacja 2017: TL; DR, pointer-events: none;
wyszukaj CSS zgodnie z opisem w odpowiedzi @ HD poniżej, która działa w nowoczesnych przeglądarkach i IE11.
dragleave
że w tym przypadku jest nadal uruchamiany.Odpowiedzi:
Musisz tylko zachować licznik referencyjny, zwiększyć go, gdy pojawi się dragenter, zmniejszyć, gdy dostaniesz dragleave. Gdy licznik ma wartość 0 - usuń klasę.
Uwaga: W przypadku upuszczenia zresetuj licznik do zera i wyczyść dodaną klasę.
Możesz go uruchomić tutaj
źródło
pointer-events: none
dziećmi. Kiedy zaczynam przeciągać, dołączam klasę, która ma tę właściwość, a kiedy przeciąganie się kończy, usuwam klasę. Działał ładnie w Safari i Chrome, ale nie w Firefox.dragenter
zapisujęevent.currentTarget
w nowej zmiennejdragEnterTarget
. DopókidragEnterTarget
jest ustawiony, ignoruję dalszedragenter
wydarzenia, ponieważ pochodzą one od dzieci. We wszystkichdragleave
przypadkach sprawdzamdragEnterTarget === event.target
. Jeśli to nieprawda, zdarzenie zostanie zignorowane, ponieważ zostało wystrzelone przez dziecko. Jeśli to prawda, resetujędragEnterTarget
doundefined
.Tak.
Ten CSS wydaje się wystarczający dla Chrome.
Podczas używania go z Firefoksem, #drop nie powinien mieć bezpośrednio węzłów tekstowych (w innym przypadku występuje dziwny problem polegający na tym, że element „zostaw to sobie” ), więc sugeruję pozostawić go tylko z jednym elementem (np. Użyj div wewnątrz #drop, aby umieścić wszystko w środku)
Oto jsfiddle rozwiązujący oryginalny przykład (zepsuty) przykład .
Stworzyłem również uproszczoną wersję , opracowaną na podstawie przykładu @Theodore Brown, ale opartą tylko na tym CSS.
Jednak nie wszystkie przeglądarki mają zaimplementowany ten CSS: http://caniuse.com/pointer-events
Widząc kod źródłowy Facebooka, mogłem go znaleźć
pointer-events: none;
kilka razy, jednak prawdopodobnie jest on używany razem z płynnymi awariami degradacji. Przynajmniej jest to takie proste i rozwiązuje problem w wielu środowiskach.źródło
Oto najprostsze rozwiązanie dla wielu przeglądarek (poważnie):
jsfiddle <- spróbuj przeciągnąć jakiś plik do pudełka
Możesz zrobić coś takiego:
W kilku słowach tworzysz „maskę” w strefie zrzutu, z odziedziczoną szerokością i wysokością, pozycją bezwzględną, która pokaże się po rozpoczęciu przeciągania.
Po pokazaniu tej maski możesz wykonać tę sztuczkę, dołączając do niej inne zdarzenia dragleave i drop.
Po wyjściu lub upuszczeniu po prostu ponownie ukryjesz maskę.
Prosty i bez komplikacji.
(Obs .: Porada Greg Pettit - Musisz upewnić się, że maska unosi całe pole, w tym ramkę)
źródło
Minęło sporo czasu, od kiedy pytanie zostało zadane i zapewniono wiele rozwiązań (w tym brzydkie hacki).
Udało mi się naprawić ten sam problem, który miałem ostatnio dzięki odpowiedzi w tej odpowiedzi i pomyślałem, że może być pomocny dla kogoś, kto przejdzie na tę stronę. Cała idea jest przechowywanie
evenet.target
wondrageenter
każdym razem to się nazywa na którykolwiek z elementów rodzica lub dziecka. Następnieondragleave
sprawdź, czy bieżący cel (event.target
) jest równy obiektowi, w którym zapisałeśondragenter
.Jedynym przypadkiem, w którym te dwa są dopasowane, jest opuszczenie okna przeglądarki przez przeciągnięcie.
Powodem, dla którego działa to dobrze, jest to, że mysz opuszcza element (powiedzmy
el1
) i wchodzi do innego elementu (powiedzmyel2
), najpierwel2.ondragenter
wywoływane jest, a następnieel1.ondragleave
. Tylko wtedy, gdy przeciąganie opuszcza / wchodzi do okna przeglądarki,event.target
będzie''
w obuel2.ondragenter
iel1.ondragleave
.Oto moja działająca próbka. Przetestowałem to na IE9 +, Chrome, Firefox i Safari.
A oto prosta strona HTML:
Przy odpowiedniej stylizacji zrobiłem, aby wewnętrzny div (# file-drop-area) był znacznie większy za każdym razem, gdy plik jest przeciągany na ekran, aby użytkownik mógł łatwo upuścić pliki w odpowiednie miejsce.
źródło
==
over===
. Porównujesz odwołania do obiektu docelowego zdarzenia, więc też===
działa dobrze.„Właściwym” sposobem rozwiązania tego problemu jest wyłączenie zdarzeń wskaźnika na elementach potomnych celu upuszczenia (jak w odpowiedzi @ HD). Oto jsFiddle, który stworzyłem, który demonstruje tę technikę . Niestety nie działa to w wersjach programu Internet Explorer wcześniejszych niż IE11, ponieważ nie obsługiwały one zdarzeń wskaźnika .
Na szczęście udało mi się wymyślić obejście który robi pracy w starszych wersjach IE. Zasadniczo polega na identyfikowaniu i ignorowaniu
dragleave
zdarzeń, które występują podczas przeciągania po elementach potomnych. Ponieważdragenter
zdarzenie jest uruchamiane w węzłach podrzędnych przeddragleave
zdarzeniem w obiekcie nadrzędnym, do każdego węzła podrzędnego można dodać osobne detektory zdarzeń, które dodają lub usuwają klasę „ignoruj-przeciągnij-zostaw” z miejsca docelowego upuszczania. Następniedragleave
detektor zdarzeń celu upuszczania może po prostu zignorować wywołania, które występują, gdy ta klasa istnieje. Oto jsFiddle demonstrujący to obejście . Jest testowany i działa w Chrome, Firefox i IE8 +.Aktualizacja:
Stworzyłem jsFiddle demonstrujący połączone rozwiązanie wykorzystujące wykrywanie funkcji, w którym zdarzenia wskaźnika są używane, jeśli są obsługiwane (obecnie Chrome, Firefox i IE11), a przeglądarka wraca do dodawania zdarzeń do węzłów potomnych, jeśli obsługa zdarzeń wskaźnika nie jest dostępna (IE8 -10).
źródło
dragleave
zdarzenia w Chrome, Firefox i IE11 +. Zaktualizowałem również inne moje obejście, aby obsługiwało IE8 i IE9, oprócz IE10. Celowe jest, aby efekt dropzone był dodawany tylko podczas przeciągania linku „Przeciągnij mnie”. Inni mogą swobodnie zmieniać to zachowanie w razie potrzeby w celu obsługi swoich przypadków użycia.Problem polega na tym, że
dragleave
zdarzenie jest uruchamiane, gdy mysz znajdzie się przed elementem potomnym.Próbowałem różnych metod sprawdzania, czy
e.target
element jest taki sam jakthis
element, ale nie mogłem uzyskać żadnej poprawy.Sposób, w jaki rozwiązałem ten problem, był trochę włamaniem, ale działa w 100%.
źródło
if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
e.x
ie.y
.jeśli używasz HTML5, możesz uzyskać klienta nadrzędnego:
Następnie w parent.dragleave ():
tutaj jest jsfiddle
źródło
border-radius
przesuwanie wskaźnika blisko narożnika faktycznie opuszczałoby element, ale ten kod nadal myślałby, że jesteśmy w środku (opuściliśmy element, ale nadal znajdujemy się w prostokącie ograniczającym). Wtedydragleave
moduł obsługi zdarzeń nie zostanie w ogóle wywołany.Bardzo prostym rozwiązaniem jest użycie
pointer-events
właściwości CSS . Po prostu ustaw jego wartośćnone
na dragstart na każdym elemencie potomnym. Te elementy nie będą już wywoływać zdarzeń związanych z myszą, więc nie złapią myszy nad nimi, a tym samym nie wywołają dragleave na rodzicu.Nie zapomnij ustawić tej właściwości z powrotem na
auto
zakończenie przeciągania;)źródło
To dość proste rozwiązanie do tej pory działa dla mnie, zakładając, że Twoje zdarzenie jest dołączane do każdego elementu przeciągania indywidualnie.
źródło
Bardzo proste rozwiązanie:
źródło
Możesz to naprawić w Firefoksie, inspirując się kodem źródłowym jQuery :
Niestety nie działa w Chrome, ponieważ
relatedTarget
wydaje się, że nie istnieje w przypadkudragleave
wydarzeń, i zakładam, że pracujesz w Chrome, ponieważ twój przykład nie działał w Firefox. Oto wersja z powyższym kodem.źródło
I oto jest rozwiązanie dla Chrome:
źródło
border-radius
jak wyjaśniłem w moim komentarzu do odpowiedzi @ azlar powyżej.Oto inne rozwiązanie za pomocą document.elementFromPoint:
Mam nadzieję, że to zadziała , oto skrzypce .
źródło
event.clientX, event.clientY
zamiast tego, ponieważ są one związane z oknem ekranu, a nie ze stroną. Mam nadzieję, że pomoże to innej zagubionej duszy.Prostym rozwiązaniem jest dodanie reguły css
pointer-events: none
do komponentu potomnego, aby zapobiec wyzwalaniuondragleave
. Zobacz przykład:źródło
dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
Nie jestem pewien, czy ta przeglądarka działa, ale przetestowałem w Chrome i to rozwiązuje mój problem:
Chcę przeciągnąć i upuścić plik na całej stronie, ale mój dragleave jest uruchamiany, gdy przeciągam element podrzędny. Moją poprawką było spojrzenie na xiy myszy:
Mam div, który nakłada się na całą moją stronę, kiedy strona się ładuje, ukrywam ją.
kiedy przeciągasz dokument, pokazuję go, a kiedy upuszczasz nadrzędnego, on go obsługuje, a kiedy opuszczasz nadrzędnego, sprawdzam xiy.
źródło
Napisałem małą bibliotekę o nazwie Dragster, aby poradzić sobie z tym dokładnie problemem, działa wszędzie oprócz cichej pracy w IE (która nie obsługuje konstruktorów zdarzeń DOM, ale napisanie czegoś podobnego przy użyciu niestandardowych zdarzeń jQuery byłoby dość łatwe)
źródło
Miałem ten sam problem i próbowałem użyć rozwiązania pk7s. Działało, ale można to zrobić trochę lepiej bez dodatkowych elementów dom.
Zasadniczo pomysł jest taki sam - dodaj dodatkową niewidoczną nakładkę na obszar upuszczalny. Zróbmy to tylko bez dodatkowych elementów dom. Oto część, w której pojawiają się pseudoelementy CSS.
JavaScript
CSS
Ta reguła po utworzy w pełni zakrytą nakładkę na obszar, który można upuścić.
Oto pełne rozwiązanie: http://jsfiddle.net/F6GDq/8/
Mam nadzieję, że pomoże każdemu z tym samym problemem.
źródło
Po prostu sprawdź, czy przeciągnięty element jest dzieckiem, jeśli tak, to nie usuwaj klasy stylu „przeciągnij”. Całkiem proste i działa dla mnie:
źródło
Natknąłem się na ten sam problem i oto moje rozwiązanie - które moim zdaniem jest znacznie łatwiejsze niż powyżej. Nie jestem pewien, czy to crossbrowser (może zależeć nawet od kolejności propagacji)
Użyję jQuery dla uproszczenia, ale rozwiązanie powinno być niezależne od frameworka.
Wydarzenie przechodzi do nadrzędnego w obu kierunkach, dlatego podano:
Dołączamy wydarzenia
I o to chodzi. :) działa, ponieważ chociaż onEnter na potomkach odpala przed onLeave na rodzicach, opóźniamy je nieco odwracając kolejność, więc klasa jest najpierw usuwana, a następnie ponownie stosowana po milisekundie.
źródło
Napisałem moduł przeciągnij i upuść o nazwie kroplówka, który naprawia to dziwne zachowanie, między innymi. Jeśli szukasz dobrego niskopoziomowego modułu przeciągania i upuszczania, którego możesz użyć jako podstawy do wszystkiego (przesyłanie plików, przeciąganie i upuszczanie w aplikacji, przeciąganie zi do źródeł zewnętrznych), powinieneś to sprawdzić wyjście modułu:
https://github.com/fresheneesz/drip-drop
Oto jak zrobiłbyś to, co próbujesz zrobić kroplówką:
Aby to zrobić bez biblioteki, zastosowałem technikę liczenia w kroplówce, ponieważ najwyżej oceniana odpowiedź pomija ważne kroki, które spowodują, że wszystko się zepsuje, z wyjątkiem pierwszej kropli. Oto jak to zrobić poprawnie:
źródło
Alternatywne rozwiązanie robocze, trochę prostsze.
źródło
clientX
powyżej zera, gdy mysz jest poza polem. Oczywiście moje elementy toposition:absolute
Po spędzeniu tylu godzin otrzymałem sugestię, która działa dokładnie tak, jak powinna. Chciałem dać sygnał tylko wtedy, gdy pliki zostały przeciągnięte, a przeciąganie dokumentów, dragleave powodował bolesne migotanie w przeglądarce Chrome.
Tak to rozwiązałem, dodając odpowiednie wskazówki dla użytkownika.
źródło
Miałem podobny problem - mój kod służący do ukrywania dropzone na zdarzeniu dragleave dla ciała został wystrzelony w sposób ciągły po najechaniu na elementy potomne powodujące migotanie dropzone w Google Chrome.
Udało mi się to rozwiązać, planując funkcję ukrywania dropzone zamiast wywoływania go od razu. Następnie, jeśli zostanie wyrzucony inny dragover lub dragleave, zaplanowane wywołanie funkcji zostanie anulowane.
źródło
Zdarzenie „ dragleave ” jest uruchamiane, gdy wskaźnik myszy wychodzi z obszaru przeciągania kontenera docelowego.
To ma sens, ponieważ w wielu przypadkach tylko rodzic może być upuszczalny, a nie potomkowie. Myślę, że event.stopPropogation () powinien był obsłużyć ten przypadek, ale wygląda na to, że to nie działa.
Wyżej wspomniane niektóre rozwiązania wydają się działać w większości przypadków, ale zawodzą w przypadku tych dzieci, które nie obsługują zdarzeń dragenter / dragleave , takich jak iframe .
1 obejście polega na sprawdzeniu event.relatedTarget i sprawdzeniu, czy znajduje się on w kontenerze, a następnie zignoruj zdarzenie dragleave , tak jak to zrobiłem tutaj:
Można znaleźć skrzypce pracuje tutaj !
źródło
Rozwiązany ..!
Zadeklaruj dowolną tablicę np.
Nie musisz martwić się o elementy potomne.
źródło
Miałem z tym DUŻO problemów, nawet po przeczytaniu wszystkich tych odpowiedzi, i pomyślałem, że mogę podzielić się z tobą moim rozwiązaniem, ponieważ uznałem, że może to być jedno z prostszych podejść, choć nieco inne.
dragleave
Miałem na myśli po prostu całkowite pominięcie nasłuchiwania zdarzeń i kodowanie zachowania dragleave przy każdym nowym zdarzeniu dragenter, jednocześnie upewniając się, że zdarzenia dragenter nie będą uruchamiane niepotrzebnie.W poniższym przykładzie mam tabelę, w której chcę móc wymieniać zawartość wiersza tabeli za pomocą interfejsu API przeciągnij i upuść. Włącz
dragenter
, klasa CSS zostanie dodana do elementu wiersza, do którego obecnie przeciągasz swój element, aby go podświetlić, a następniedragleave
ta klasa zostanie usunięta.Przykład:
Bardzo podstawowa tabela HTML:
A funkcja obsługi zdarzenia dragenter, dodane do każdej komórki tabeli (na bok
dragstart
,dragover
,drop
idragend
teleskopowe, które nie są specyficzne dla tej kwestii, więc nie skopiowane tutaj):Pozostały bardzo podstawowe komentarze w kodzie, dzięki czemu ten kod jest odpowiedni również dla początkujących. Mam nadzieję, że to ci pomoże! Pamiętaj, że musisz oczywiście dodać wszystkie detektory zdarzeń, o których wspomniałem powyżej, do każdej komórki tabeli, aby to zadziałało.
źródło
Oto inne podejście oparte na czasie wydarzeń.
dragenter
Zdarzenie wysyłane z elementu dziecka mogą być przechwytywane przez elementu nadrzędnego i zawsze występuje przeddragleave
. Czas pomiędzy tymi dwoma zdarzeniami jest naprawdę krótki, krótszy niż jakakolwiek możliwa akcja myszy ludzkiej. Chodzi o to, aby zapamiętać czas wystąpienia zdarzeniadragenter
i filtrowaćdragleave
zdarzenia, które występują „niezbyt szybko” po ...Ten krótki przykład działa w Chrome i Firefox:
źródło
pimvdb ..
Dlaczego nie spróbujesz użyć kropli zamiast dragleave . To zadziałało dla mnie. mam nadzieję, że to rozwiąże twój problem.
Sprawdź jsFiddle: http://jsfiddle.net/HU6Mk/118/
źródło
Możesz użyć limitu czasu z
transitioning
flagą i nasłuchiwać na górnym elemencie.dragenter
/dragleave
z wydarzeń podrzędnych zostaną przeniesione do kontenera.Ponieważ
dragenter
na elemencie potomnym wystrzeliwanym przeddragleave
kontenerem ustawimy wyświetlanie flagi jako przejście na 1 ms ...dragleave
nasłuchujący sprawdzi flagę przed upływem 1 ms.Flaga będzie prawdziwa tylko podczas przejścia do elementów potomnych i nie będzie prawdziwa podczas przejścia do elementu nadrzędnego (kontenera)
jsfiddle: http://jsfiddle.net/ilovett/U7mJj/
źródło
Musisz usunąć zdarzenia wskaźnika dla wszystkich obiektów podrzędnych celu przeciągania.
źródło