O problema que estou tendo é que o dragleave
evento de um elemento é acionado ao passar o mouse sobre um elemento filho desse elemento. Além disso, dragenter
não é acionado ao passar novamente o elemento pai.
Fiz um violino simplificado: 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>
com o seguinte JavaScript:
$('#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");
}
});
O que ele deve fazer é notificar o usuário, deixando a gota div
vermelha ao arrastar algo para lá. Isso funciona, mas se você arrastar a p
criança, ela dragleave
é acionada e div
não fica mais vermelha. Voltar para a gota div
também não a torna vermelha novamente. É necessário sair completamente da gota div
e arrastá-la novamente para torná-la vermelha.
É possível impedir o dragleave
disparo ao arrastar para um elemento filho?
Atualização de 2017: TL; DR, consulte CSS, pointer-events: none;
conforme descrito na resposta da @ HD abaixo, que funciona em navegadores modernos e no IE11.
dragleave
ainda é acionado nesse caso.Respostas:
Você só precisa manter um contador de referência, incrementá-lo quando obtém um dragenter, diminuir quando obtém um dragleave. Quando o contador estiver em 0 - remova a classe.
Nota: No evento drop, redefina o contador para zero e limpe a classe adicionada.
Você pode executá-lo aqui
fonte
pointer-events: none
as crianças. Quando começo a arrastar, anexo uma classe que tem essa propriedade. Quando arrasto, removo a classe. Funcionou bem no Safari e Chrome, mas não no Firefox.dragenter
eu salvoevent.currentTarget
em uma nova variáveldragEnterTarget
. EnquantodragEnterTarget
estiver definido, ignoro outrosdragenter
eventos, porque são de crianças. Em todos osdragleave
eventos eu verificodragEnterTarget === event.target
. Se isso for falso, o evento será ignorado, pois foi disparado por uma criança. Se isso for verdade, redefinirdragEnterTarget
paraundefined
.Sim.
Esse CSS parece ser suficiente para o Chrome.
Ao usá-lo com o Firefox, o #drop não deve ter nós de texto diretamente (caso contrário, há um problema estranho em que um elemento "deixa para si" ), então sugiro deixá-lo com apenas um elemento (por exemplo, use uma div dentro #drop para colocar tudo dentro)
Aqui está um jsfiddle resolvendo o exemplo da pergunta original (quebrado) .
Também fiz uma versão simplificada do exemplo do @Theodore Brown, mas baseada apenas neste CSS.
Porém, nem todos os navegadores têm esse CSS implementado: http://caniuse.com/pointer-events
Vendo o código-fonte do Facebook, eu pude encontrar isso
pointer-events: none;
várias vezes, no entanto, provavelmente é usado junto com fallbacks de degradação graciosos. Pelo menos, é tão simples e resolve o problema para muitos ambientes.fonte
Aqui, a solução mais simples entre navegadores (seriamente):
jsfiddle <- tente arrastar algum arquivo dentro da caixa
Você pode fazer algo assim:
Em poucas palavras, você cria uma "máscara" dentro da zona de drop-down, com largura e altura herdadas, posição absoluta, que será exibida apenas quando a dragover iniciar.
Então, depois de mostrar essa máscara, você pode fazer o truque anexando os outros eventos de arrastar e soltar nela.
Depois de sair ou deixar cair, você apenas esconde a máscara novamente.
Simples e sem complicações.
(Obs .: conselhos de Greg Pettit - você deve ter certeza de que a máscara passa a caixa inteira, incluindo a borda)
fonte
Já faz algum tempo que essa pergunta é feita e muitas soluções (incluindo hacks feios) são fornecidas.
Consegui corrigir o mesmo problema que tive recentemente graças à resposta nesta resposta e achei que poderia ser útil para alguém que acessa esta página. A idéia é armazenar o
evenet.target
emondrageenter
toda ela é chamada em qualquer um dos elementos pai ou filho. Em seguida,ondragleave
verifique se o destino atual (event.target
) é igual ao objeto em que você armazenouondragenter
.O único caso de correspondência desses dois é quando o arrasto sai da janela do navegador.
A razão pela qual isso funciona bem é quando o mouse sai de um elemento (digamos
el1
) e entra em outro elemento (digamosel2
), primeiroel2.ondragenter
é chamado e depoisel1.ondragleave
. Somente quando o arrasto estiver saindo / entrando na janela do navegador,event.target
estará''
em ambosel2.ondragenter
eel1.ondragleave
.Aqui está minha amostra de trabalho. Eu testei no IE9 +, Chrome, Firefox e Safari.
E aqui está uma página html simples:
Com um estilo adequado, o que eu fiz foi aumentar a div interna (# área de queda de arquivos) sempre que um arquivo é arrastado para a tela, para que o usuário possa soltar os arquivos facilmente no local apropriado.
fonte
==
over===
. Você está comparando as referências de objeto de destino do evento, portanto também===
funciona bem.A maneira "certa" de resolver esse problema é desabilitar os eventos do ponteiro nos elementos filhos do destino de descarte (como na resposta do @ HD). Aqui está um jsFiddle que criei que demonstra essa técnica . Infelizmente, isso não funciona nas versões do Internet Explorer anteriores ao IE11, pois elas não suportam eventos de ponteiro .
Felizmente, eu era capaz de chegar a uma solução que faz o trabalho em versões antigas do IE. Basicamente, envolve identificar e ignorar
dragleave
eventos que ocorrem ao arrastar sobre elementos filho. Como odragenter
evento é disparado nos nós filhos antes dodragleave
evento no pai, ouvintes de eventos separados podem ser adicionados a cada nó filho que adiciona ou remove uma classe "ignore-drag-leave" do destino da gota. Em seguida, odragleave
ouvinte de evento do destino de recebimento pode simplesmente ignorar as chamadas que ocorrem quando essa classe existe. Aqui está um jsFiddle demonstrando essa solução alternativa . Ele foi testado e funciona no Chrome, Firefox e IE8 +.Atualizar:
Criei um jsFiddle demonstrando uma solução combinada usando detecção de recurso, em que eventos de ponteiro são usados se suportados (atualmente Chrome, Firefox e IE11) e o navegador volta a adicionar eventos a nós filhos se o suporte a evento de ponteiro não estiver disponível (IE8 -10).
fonte
dragleave
evento seja disparado no Chrome, Firefox e IE11 +. Também atualizei minha outra solução alternativa para dar suporte ao IE8 e IE9, além do IE10. É intencional que o efeito da zona de lançamento seja adicionado apenas ao arrastar o link "Arraste-me". Outros podem ficar à vontade para alterar esse comportamento, conforme necessário, para dar suporte a seus casos de uso.O problema é que o
dragleave
evento está sendo disparado quando o mouse fica na frente do elemento filho.Eu tentei vários métodos de verificação para ver se o
e.target
elemento é igual aothis
elemento, mas não obtive nenhuma melhoria.A maneira como corrigi esse problema foi um pouco complicado, mas funciona 100%.
fonte
if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
e.x
ee.y
.se você estiver usando HTML5, poderá obter o clientRect do pai:
Em seguida, no parent.dragleave ():
aqui está um jsfiddle
fonte
border-radius
, mover o ponteiro para perto do canto deixaria o elemento, mas esse código ainda pensaria que estamos dentro (deixamos o elemento, mas ainda estamos no retângulo delimitador). Em seguida, odragleave
manipulador de eventos não será chamado.Uma solução muito simples é usar a
pointer-events
propriedade CSS . Basta definir o seu valor paranone
cima dragstart em cada elemento filho. Esses elementos não acionam mais os eventos relacionados ao mouse; portanto, eles não pegam o mouse sobre eles e, portanto, não acionam a saída de arrastar no pai.Não se esqueça de definir essa propriedade de volta ao
auto
terminar o arrasto;)fonte
Esta solução bastante simples está funcionando para mim até agora, supondo que seu evento esteja anexado a cada elemento de arrastar individualmente.
fonte
Solução muito simples:
fonte
Você pode corrigi-lo no Firefox com um pouco de inspiração no código-fonte do jQuery :
Infelizmente, ele não funciona no Chrome porque
relatedTarget
parece não existir emdragleave
eventos, e suponho que você esteja trabalhando no Chrome porque seu exemplo não funcionou no Firefox. Aqui está uma versão com o código acima implementado.fonte
E aqui está uma solução para o Chrome:
fonte
border-radius
como expliquei no meu comentário na resposta do @ azlar acima.Aqui está outra solução usando document.elementFromPoint:
Espero que isso funcione, aqui está um violino .
fonte
event.clientX, event.clientY
, pois eles são relativos à janela de visualização e não à página. Espero que ajude outra alma perdida por aí.Uma solução simples é adicionar a regra css
pointer-events: none
ao componente filho para impedir o acionamento deondragleave
. Consultar exemplo:fonte
dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
Não tenho certeza se este navegador cruzado, mas eu testei no Chrome e resolve o meu problema:
Quero arrastar e soltar um arquivo por toda a página, mas minha saída de arrastar é acionada quando arrasto sobre o elemento filho. Minha correção foi olhar o xey do mouse:
Eu tenho uma div que se sobrepõe a minha página inteira, quando a página carrega eu a oculto.
quando você arrasta o documento, eu o mostro; quando você solta o pai, ele lida com ele; e quando você deixa o pai, verifica x e y.
fonte
Eu escrevi uma pequena biblioteca chamada Dragster para lidar com esse problema exato, funciona em qualquer lugar, exceto silenciosamente, sem fazer nada no IE (que não suporta Construtores de Eventos DOM, mas seria muito fácil escrever algo semelhante usando os eventos personalizados do jQuery)
fonte
Eu estava tendo o mesmo problema e tentei usar a solução pk7s. Funcionou, mas poderia ser feito um pouco melhor sem nenhum elemento extra de dom.
Basicamente, a idéia é a mesma - adicione uma sobreposição invisível extra sobre a área de droppable. Só vamos fazer isso sem nenhum elemento extra de dom. Aqui está a parte em que os pseudoelementos CSS aparecem.
Javascript
CSS
Essa regra posterior criará uma sobreposição totalmente coberta para a área descartável.
Aqui está a solução completa: http://jsfiddle.net/F6GDq/8/
Espero que ajude alguém com o mesmo problema.
fonte
Apenas verifique se o elemento arrastado é um filho, se for, não remova sua classe de estilo 'dragover'. Muito simples e funciona para mim:
fonte
Eu me deparei com o mesmo problema e aqui está a minha solução - que eu acho que é muito mais fácil do que acima. Não tenho certeza se é crossbrowser (pode depender até de ordem borbulhante)
Usarei o jQuery para simplificar, mas a solução deve ser independente da estrutura.
O evento borbulha para o pai de qualquer maneira, conforme indicado:
Anexamos eventos
E é sobre isso. :) funciona porque, embora onEnter no filho seja acionado antes de onLeave no pai, atrasamos um pouco a reversão da ordem, para que a classe seja removida primeiro e depois reaplicada após um milissegundo.
fonte
Eu escrevi um módulo de arrastar e soltar chamado drip-drop que corrige esse comportamento esquisito, entre outros. Se você estiver procurando por um bom módulo de arrastar e soltar de baixo nível que possa ser usado como base para qualquer coisa (upload de arquivo, arrastar e soltar no aplicativo, arrastar de ou para fontes externas), verifique isso saída do módulo:
https://github.com/fresheneesz/drip-drop
É assim que você faria o que está tentando fazer no drip-drop:
Para fazer isso sem uma biblioteca, a técnica do contador é o que eu usei no drip-drop, embora a resposta mais alta perca etapas importantes que farão com que as coisas quebrem por tudo, exceto a primeira gota. Veja como fazê-lo corretamente:
fonte
Uma solução de trabalho alternativa, um pouco mais simples.
fonte
clientX
acima de 0 quando o mouse está fora da caixa. Concedido, meus elementos sãoposition:absolute
Depois de passar tantas horas, recebi a sugestão funcionando exatamente como pretendido. Eu queria fornecer uma dica apenas quando os arquivos eram arrastados e a passagem de documentos, a saída de arrasto estava causando tremulações dolorosas no navegador Chrome.
Foi assim que eu resolvi, também dando dicas adequadas para o usuário.
fonte
Eu tive um problema semelhante - meu código para ocultar o dropzone no evento dragleave for body foi disparado contatantemente quando pairava elementos filho, fazendo o dropzone piscar no Google Chrome.
Consegui resolver isso agendando a função para ocultar o dropzone em vez de chamá-lo imediatamente. Em seguida, se outro arrastar ou sair de arrastar for acionado, a chamada de função agendada será cancelada.
fonte
O evento " dragleave " é acionado quando o ponteiro do mouse sai da área de arraste do contêiner de destino.
O que faz muito sentido, pois, em muitos casos, apenas os pais podem ser descartáveis e não os descendentes. Acho event.stopPropogation () deveria ter lidado com esse caso, mas parece que não funciona.
Acima mencionado, algumas soluções parecem funcionar na maioria dos casos, mas falham no caso dos filhos que não suportam eventos dragenter / dragleave , como iframe .
Uma solução alternativa é verificar o event.relatedTarget e verificar se ele reside dentro do contêiner, depois ignore o evento dragleave como fiz aqui:
Você pode encontrar um violino trabalhando aqui !
fonte
Resolvido ..!
Declare qualquer matriz para ex:
Não precisa se preocupar com elementos filho.
fonte
Eu lutei muito com isso, mesmo depois de ler todas essas respostas, e pensei em compartilhar minha solução com você, porque achei que poderia ser uma das abordagens mais simples, embora um pouco diferente. Meu pensamento era simplesmente omitir
dragleave
completamente o ouvinte de evento e codificar o comportamento da saída de arrastar com cada novo evento dragenter acionado, enquanto certificava-me de que os eventos dragenter não fossem acionados desnecessariamente.No meu exemplo abaixo, eu tenho uma tabela na qual desejo trocar o conteúdo da linha da tabela por meio da API de arrastar e soltar. Em
dragenter
, uma classe CSS deve ser adicionada ao elemento de linha no qual você está arrastando o elemento para destacá-lo edragleave
, em seguida , essa classe deve ser removida.Exemplo:
Tabela HTML muito básica:
E a função dragenter manipulador de eventos, adicionado para cada célula da tabela (com exceção
dragstart
,dragover
,drop
, edragend
manipuladores, que não são específicos para esta questão, por isso não copiadas aqui):Comentários muito básicos deixados no código, de modo que este código seja adequado também para iniciantes. Espero que isso ajude você! Observe que é claro que você precisará adicionar todos os ouvintes de eventos mencionados acima em cada célula da tabela para que isso funcione.
fonte
Aqui está outra abordagem baseada no tempo dos eventos.
O
dragenter
evento despachado do elemento filho pode ser capturado pelo elemento pai e sempre ocorre antes dodragleave
. O tempo entre esses dois eventos é realmente curto, mais curto do que qualquer ação possível do mouse humano. Portanto, a idéia é memorizar o horário em quedragenter
acontece e filtrar osdragleave
eventos que ocorrem "não muito rapidamente" após ...Este pequeno exemplo funciona no Chrome e Firefox:
fonte
pimvdb ..
Por que você não tenta usar drop em vez de arrastar . Funcionou para mim. Espero que isso resolva seu problema.
Verifique o jsFiddle: http://jsfiddle.net/HU6Mk/118/
fonte
Você pode usar um tempo limite com uma
transitioning
bandeira e ouvir o elemento superior.dragenter
/dragleave
de eventos filhos borbulharão até o contêiner.Como
dragenter
no elemento filho dispara antesdragleave
do contêiner, definiremos o sinalizador como transição para 1ms ... odragleave
ouvinte verificará o sinalizador antes que o 1ms esteja ativo.O sinalizador será verdadeiro apenas durante as transições para elementos filho e não será verdadeiro ao fazer a transição para um elemento pai (do contêiner)
jsfiddle: http://jsfiddle.net/ilovett/U7mJj/
fonte
Você precisa remover os eventos do ponteiro para todos os objetos filhos do destino de arrastar.
fonte