Visão geral
Eu tenho a seguinte estrutura HTML e anexei os eventos dragenter
e dragleave
ao <div id="dropzone">
elemento
<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>
Problema
Quando arrasto um arquivo sobre o <div id="dropzone">
, o dragenter
evento é disparado conforme o esperado. No entanto, quando movo o mouse sobre um elemento filho, como <div id="drag-n-drop">
, o dragenter
evento é disparado para o <div id="drag-n-drop">
elemento e, em seguida, o dragleave
evento é disparado para o <div id="dropzone">
elemento.
Se eu passar o mouse sobre o <div id="dropzone">
elemento novamente, o dragenter
evento é acionado novamente, o que é legal, mas o dragleave
evento é acionado para o elemento filho que acabou de sair, para que a removeClass
instrução seja executada, o que não é legal.
Esse comportamento é problemático por 2 razões:
Estou apenas anexando
dragenter
&dragleave
ao,<div id="dropzone">
para não entender por que os elementos filhos também têm esses eventos anexados.Ainda estou arrastando o
<div id="dropzone">
elemento enquanto passa o mouse sobre seus filhos, para não quererdragleave
disparar!
jsFiddle
Aqui está um jsFiddle para mexer com: http://jsfiddle.net/yYF3S/2/
Questão
Então ... como posso fazer isso de modo que, ao arrastar um arquivo sobre o <div id="dropzone">
elemento, dragleave
não seja acionado, mesmo se estiver arrastando sobre qualquer elemento filho ... ele deve ser acionado somente quando eu deixar o <div id="dropzone">
elemento .. pairar / arrastar ao redor de qualquer lugar dentro dos limites do elemento não deve acionar o dragleave
evento.
Eu preciso que isso seja compatível com vários navegadores, pelo menos nos navegadores que oferecem suporte ao arrastar e soltar HTML5, portanto, essa resposta não é adequada.
Parece que o Google e o Dropbox descobriram isso, mas o código fonte é minificado / complexo, então não consegui descobrir isso com a implementação.
e.stopPropagation();
Respostas:
Se você não precisar vincular eventos aos elementos filhos, sempre poderá usar a propriedade pointer-events.
fonte
:hover
estilos separados ou clicar em manipuladores de eventos). Caso você queira preservar esses eventos, aqui está outra solução alternativa que eu tenho usado: bensmithett.github.io/dragster.dropzone * {pointer-events: none;}
$('.element').addClass('dragging-over')
para o elemento que você está arrastando e$('.element').removeClass('dragging-over')
quando você arrasta. Então, no seu CSS, você pode ter.element.dragging-over * { pointer-events: none; }
. Isso remove eventos de ponteiro de todos os elementos filhos apenas quando você está arrastando.Finalmente encontrei uma solução com a qual estou feliz. Na verdade, eu encontrei várias maneiras de fazer o que eu quero, mas nenhuma foi tão bem-sucedida quanto a solução atual ... em uma solução, experimentei oscilações frequentes como resultado da adição / remoção de uma borda ao
#dropzone
elemento ... em outra, a borda nunca foi removido se você se afastar do navegador.Enfim, minha melhor solução hacky é esta:
Isso funciona muito bem, mas surgiram problemas no Firefox porque o Firefox estava invocando duas vezes,
dragenter
então meu contador estava desligado. Mas, no entanto, não é uma solução muito elegante.Então me deparei com esta pergunta: Como detectar o evento dragleave no Firefox ao arrastar para fora da janela
Então, peguei a resposta e a apliquei à minha situação:
Isso é limpo e elegante e parece estar funcionando em todos os navegadores que testei até agora (ainda não testei o IE).
fonte
stopPropagation()
epreventDefault()
é o preferido. Eu deixaria cair oreturn false
.Isso é um pouco feio, mas funciona caramba! ...
No manipulador 'dragenter', armazene o event.target (em uma variável dentro do seu fechamento, ou qualquer outra coisa); em seguida, no manipulador 'dragleave', somente o código será acionado se event.target === o que você armazenou.
Se o seu 'dragenter' está disparando quando você não deseja (por exemplo, quando entra após deixar os elementos filhos), na última vez em que dispara antes que o mouse saia do pai, ele está no pai, portanto, o pai sempre estará o 'dragenter' final antes do 'dragleave' pretendido.
fonte
No começo, eu concordei com as pessoas que descartaram a
pointer-events: none
abordagem. Mas então eu me perguntei:No meu caso, eu tenho um monte de coisas acontecendo nas crianças, por exemplo pairar para mostrar botões para ações adicionais, inline-edição, etc ... No entanto, nenhum de que é necessário ou, de fato, mesmo desejado durante um arrasto.
No meu caso, uso algo assim para desativar os eventos do ponteiro seletivamente para todos os nós filhos do contêiner pai:
Use sua abordagem favorita para adicionar / remover a classe
dragging-in-progress
nos manipuladoresdragEnter
/dragLeave
event, como eu fiz ou fiz no mesmodragStart
, et. al.fonte
dragstart
evento e removê-ladragend
.Este parece ser um bug do Chrome.
A única solução alternativa em que pude pensar foi criar um elemento de sobreposição transparente para capturar seus eventos: http://jsfiddle.net/yYF3S/10/
JS :
HTML :
fonte
O problema é que seus elementos dentro das zonas de drop fazem parte do dropzone e, quando você entra nos filhos, deixa o pai. Resolver isso não é fácil. Você pode tentar adicionar eventos aos filhos e adicionar sua classe novamente aos pais.
Seus eventos ainda serão disparados várias vezes, mas ninguém verá.
// Edit: Use o evento dragmove para sobrescrever permanentemente o evento dragleave:
Defina o evento dragleave apenas para o dropzone.
fonte
dragleave
... quando eu deixar uma criança, eu poderia ainda estar dentro do pai#dropzone
, mas isso não irá disparar odragenter
evento novamente :(dragleave
evento é acionado para o elemento filho acabou de sair, então novamente aremoveClass
instrução é executadadragleave
apenas para#dropzone
... se eu pudesse, não teria esse problema.Como benr mencionado nesta resposta , você pode impedir que os nós filhos disparem nos eventos, mas se precisar vincular alguns eventos, faça o seguinte:
E adicione este ao seu código JS:
fonte
Se você estiver usando jQuery, verifique isso: https://github.com/dancork/jquery.event.dragout
É realmente incrível.
Edição: na verdade, eu não acho que é dependente do jQuery, você provavelmente pode apenas usar o código, mesmo sem ele.
fonte
dragleave
mesmo que eu não tenha saído do dropzone pai.dragout
foi a única solução que funcionou para mim.@hristo Eu tenho uma solução muito mais elegante. Verifique se isso é algo que você poderia usar.
Seu esforço não foi desperdiçado, afinal. Consegui usar o seu no começo, mas tive problemas diferentes no FF, Chrome. Depois de passar tantas horas, recebi a sugestão funcionando exatamente como pretendido.
Aqui está como está a implementação. Também aproveitei as dicas visuais para orientar corretamente os usuários sobre a zona de queda.
fonte
var dropZoneHideDelay=70, dropZoneVisible=true;
dropZoneHideDelay, dropZoneVisible
são necessários em dois'on'
eventos de documento na minha implementação.Meus dois centavos: oculte uma camada sobre o seu dropzone e mostre-o quando você arrastar, e direcione o botão de arrastar para ele.
Demonstração: https://jsfiddle.net/t6q4shat/
HTML
CSS
JS
fonte
Então, para mim, a abordagem
pointer-events: none;
não funcionou muito bem ... Então, aqui está minha solução alternativa:Dessa forma, não é possível para
dragleave
o pai (em um filho) oudragover
um elemento filho. espero que isto ajude :)* A classe '.active' que adiciono quando eu
dragenter
oudragleave
. Mas se você estiver trabalhando sem isso, deixe a aula de lado.fonte
Correção rápida super simples para isso, não testada extensivamente, mas funciona no Chrome no momento.
Desculpe o Coffeescript.
Isso corrige o bug de cintilação do Chrome, ou pelo menos a permutação que estava me causando problemas.
fonte
Esta resposta pode ser encontrada aqui:
A arrastar em HTML5 é acionada ao passar o mouse sobre um elemento filho
fonte
Minha versão:
Com este layout:
Eu fiz um despejo de classes de elemento para
e.target
,e.currentTarget
,e.relatedTarget
para ambosdragover
edragleave
eventos.Ele me mostrou que ao deixar o bloco pai (
.dropzone
)e.relatedTarget
não é filho desse bloco, então eu sei que estou fora do dropzone.fonte
Aqui, uma das soluções mais simples ● ︿ ●
Dê uma olhada neste violino <- tente arrastar algum arquivo dentro da caixa
Você pode fazer algo assim:
Por isso você já deve saber o que está acontecendo aqui.
Basta dar uma olhada no violino, você sabe.
fonte
Eu tive um problema semelhante e corrigi-lo desta maneira:
O problema: a função drop (ev) é acionada quando o usuário solta um elemento na "zona de soltar" (elemento ul), mas, infelizmente, também quando o elemento é solto em um de seus filhos (elementos li).
O conserto:
fonte
Eu mesmo estava tentando implementar isso em uma caixa de upload de arquivos em que a cor da caixa mudava quando os usuários arrastavam um arquivo para o espaço.
Encontrei uma solução que é uma boa mistura de Javascript e CSS. Digamos que você tenha uma zona de droppable
div
com id#drop
. Adicione isso ao seu Javascript:Em seguida, adicione isso ao seu CSS, para desativar todos os filhos da classe
.inactive
:Assim, os elementos filhos permanecerão inativos enquanto o usuário arrastar o elemento sobre a caixa.
fonte
Não me senti satisfeito com nenhuma das soluções alternativas apresentadas aqui, porque não quero perder o controle sobre os elementos filhos.
Então, eu usei uma abordagem lógica diferente, traduzindo-a em um plugin jQuery, chamado jquery-draghandler . Absolutamente não manipula o DOM, garantindo altos desempenhos. Seu uso é simples:
Ele lida perfeitamente com o problema sem comprometer nenhuma funcionalidade do DOM.
Download, detalhes e explicações em seu repositório Git .
fonte
frame
seu elemento está na sua borda. Nesse caso, não sugiro essa abordagem.Na verdade, estou gostando do que vejo em https://github.com/lolmaus/jquery.dragbetter/mas queria compartilhar uma possível alternativa. Minha estratégia geral era aplicar um estilo de segundo plano ao dropzone (não a seus filhos) ao arrastá-lo ou a qualquer filho dele (por meio de bolhas). Depois, removo o estilo ao arrastar a zona de soltar. A idéia era ao mudar para uma criança, mesmo que eu remova o estilo do dropzone ao deixá-lo (o dragleave dispara), simplesmente reaplicaria o estilo ao dropzone pai ao arrastar qualquer criança. É claro que o problema é que, ao passar do dropzone para um filho do dropzone, o dragenter é demitido do filho antes do saque, então meus estilos são aplicados fora de ordem. A solução para mim foi usar um timer para forçar o evento dragenter de volta à fila de mensagens, permitindo processá-lo APÓS a saída de arrastar. Usei um fechamento para acessar o evento no retorno de chamada do timer.
Isso parece funcionar no chrome, ou seja, no firefox e funciona independentemente do número de filhos no dropzone. Estou um pouco desconfortável com o tempo limite que garante o reequilíbrio de eventos, mas parece funcionar muito bem no meu caso de uso.
fonte
Eu estava tentando implementar isso sozinho e também não queria o Jquery ou qualquer plug-in.
Eu queria lidar com o upload do arquivo, da seguinte maneira considerada melhor para mim:
Estrutura de Arquivos:
--- / uploads {diretório de uploads}
--- /js/slyupload.js {arquivo javascript.}
--- index.php
--- upload.php
--- styles.css {apenas um pouco de estilo ..}
Código HTML:
Então este é o arquivo Javascript anexado :: 'js / slyupload.js'
CSS:
fonte
Desculpe, é javascript, não jquery, mas para mim é a maneira mais lógica de resolver isso. O navegador deve chamar dropleave (do elemento anterior) antes de dropenter (do novo elemento, como algo não pode entrar em outra coisa antes de deixar a primeira coisa, não entendo por que eles fizeram isso! Então você só precisa atrasar de dropleave assim:
E o dropenter acontecerá após o dropleave, só isso!
fonte
Use o
greedy : true
para a criança como uma função dedroppable
. Em seguida, inicie apenas o evento clicado na primeira camada.fonte