'dragleave' do elemento pai é acionado ao arrastar sobre os elementos filhos


163

Visão geral

Eu tenho a seguinte estrutura HTML e anexei os eventos dragentere dragleaveao <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 dragenterevento é disparado conforme o esperado. No entanto, quando movo o mouse sobre um elemento filho, como <div id="drag-n-drop">, o dragenterevento é disparado para o <div id="drag-n-drop">elemento e, em seguida, o dragleaveevento é disparado para o <div id="dropzone">elemento.

Se eu passar o mouse sobre o <div id="dropzone">elemento novamente, o dragenterevento é acionado novamente, o que é legal, mas o dragleaveevento é acionado para o elemento filho que acabou de sair, para que a removeClassinstrução seja executada, o que não é legal.

Esse comportamento é problemático por 2 razões:

  1. Estou apenas anexando dragenter& dragleaveao, <div id="dropzone">para não entender por que os elementos filhos também têm esses eventos anexados.

  2. Ainda estou arrastando o <div id="dropzone">elemento enquanto passa o mouse sobre seus filhos, para não querer dragleavedisparar!

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, dragleavenã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 dragleaveevento.

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.


Impedir a propagação de eventos viae.stopPropagation();
Blender

Acho que já am ... confira a atualização
Hristo

Você pode postar uma demo em algum lugar do jsfiddle.net ?
Blender

@ Blender ... coisa certa, me dê alguns minutos!
Hristo #

@Blender ... Eu atualizei a minha pergunta com um violino
Hristo

Respostas:


171

Se você não precisar vincular eventos aos elementos filhos, sempre poderá usar a propriedade pointer-events.

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

4
Melhor solução! Eu nem pensei que poderia resolver o problema usando o css de maneira tão elegante. Muito obrigado!
precisa saber é

1
Oh sim. É isso!
mcmlxxxiii

23
A única desvantagem dessa abordagem é que ela destrói todos os eventos de ponteiro em elementos filho (por exemplo, eles não podem mais ter :hoverestilos 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
Ben

2
Combine com o seletor de filhos do css para alcançar todos os filhos do seu dropzone: .dropzone * {pointer-events: none;}
Philipp F

7
Você já está usando jQuery, portanto, apenas $('.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.
Gavin

56

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 #dropzoneelemento ... em outra, a borda nunca foi removido se você se afastar do navegador.

Enfim, minha melhor solução hacky é esta:

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

Isso funciona muito bem, mas surgiram problemas no Firefox porque o Firefox estava invocando duas vezes, dragenterentã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:

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

Isso é limpo e elegante e parece estar funcionando em todos os navegadores que testei até agora (ainda não testei o IE).


Até agora, esta é a melhor solução. O único problema é que esse código não funciona quando você adiciona um arquivo, remove-o e arrasta outro arquivo sobre ele. Somente após aumentar o HoverEnd e aumentar o HoverStart novamente esse código funcionará novamente.
MysticEarth

@MysticEarth, você pode montar um jsFiddle para fazer uma demonstração quando ele não funcionar?
Hristo

1
Obrigado pela sua resposta. Isso foi realmente útil, pois a interface de arrastar e soltar do HTML5 e os ouvintes de eventos podem ser realmente dolorosos. Essa é a melhor maneira de lidar com eventos malucos, especialmente quando você deseja lidar com várias instâncias de elementos da zona de descarte.
Nico O

Por que precisamos chamar stopPropagation () preventDefault () e retornar false. Retorno falso deve ser suficiente. Deveria ?
Dejo

É redundante, mas chama stopPropagation()e preventDefault()é o preferido. Eu deixaria cair o return false.
Peeja

38

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.

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

}());

Vou tentar isso mais tarde. Se alguma coisa, parece mais limpa que a minha solução, mas não tenho certeza de quão compatível é entre navegadores apenas olhando para ela. Onde você testou isso?
Hristo

1
somente Chrome e pode funcionar apenas com a suposição de que existe uma lacuna física entre os elementos filhos e o contêiner.
precisa saber é o seguinte

5
Minha resposta favorita aqui. Não se sente feio em todos os :)
Matt Way

1
Funciona para mim no IE, Chrome, FF
Vicentiu Bacioiu

1
Isso não funciona se o arrasto começar nos filhos. Por exemplo, arrastar de algo de outra janela de forma que o arraste comece dentro do filho.
precisa saber é o seguinte

29

No começo, eu concordei com as pessoas que descartaram a pointer-events: noneabordagem. Mas então eu me perguntei:

Você realmente precisa de eventos de ponteiro para trabalhar nos elementos filhos enquanto o arrasto está em andamento ?

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:

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

Use sua abordagem favorita para adicionar / remover a classe dragging-in-progressnos manipuladores dragEnter/ dragLeaveevent, como eu fiz ou fiz no mesmo dragStart, et. al.


1
Essa solução é a mais simples e é (até onde eu sei) a prova completa. Basta adicionar a classe no dragstartevento e removê-la dragend.
Cmann 15/05

Esta é uma solução bastante agradável, mas não perfeita. Se o arrasto iniciar no elemento filho (arrastar de outra janela), o dragEnter nunca será acionado. Provavelmente, também é possível mover o mouse rápido o suficiente para que o dragEnter não seja acionado quando você se move para o filho, mas não tem 100% de certeza disso.
precisa saber é o seguinte

12

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 :

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

obrigado pela resposta, Blender. Eu já tentei isso no entanto. O problema com esta solução é que o elemento de sobreposição "sobrepõe" os elementos filhos, com os quais eu preciso interagir ... portanto, ter uma sobreposição desativa a interação que eu preciso com os filhos ... pense nisso como um arquivo arrastar-n -drop box ... você pode arrastar e soltar ou clicar no elemento de entrada para selecionar seus arquivos.
precisa

aqui está um exemplo violino que sua solução não satisfaz ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo

Estou confuso. Por que você precisa interagir com os elementos filhos também?
Blender

No meu caso particular, um elemento filho do "dropzone" é um botão de seleção de arquivo ... então, se eu não quiser arrastar e soltar arquivos, posso selecioná-los usando o elemento de entrada. Mas com a sobreposição ... Não consigo clicar no elemento de entrada. faz sentido?
Hristo

@Blender Na maioria dos casos, precisamos arrastar um arquivo da área de trabalho, não um elemento da página, portanto isso meio que não funciona.
Mari Briedis

5

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.

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

    // add a class to #dropzone

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

Seus eventos ainda serão disparados várias vezes, mas ninguém verá.

// Edit: Use o evento dragmove para sobrescrever permanentemente o evento dragleave:

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

    // add a class to #dropzone

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

Defina o evento dragleave apenas para o dropzone.


2
isso não resolverá o problema. A verdadeira questão é dragleave... quando eu deixar uma criança, eu poderia ainda estar dentro do pai #dropzone, mas isso não irá disparar o dragenterevento novamente :(
Hristo

Tem certeza de que o dragenter não é acionado novamente?
Oliver

Ah certo ... que é acionado novamente, no entanto, depois de ser demitido, o dragleaveevento é acionado para o elemento filho acabou de sair, então novamente a removeClassinstrução é executada
Hristo

Não posso definir dragleaveapenas para #dropzone... se eu pudesse, não teria esse problema.
Hristo #

Não, eu quis dizer não adicionar eventos para dragleave às crianças.
Oliver

4

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:

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

E adicione este ao seu código JS:

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

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

2
Para mim, isso resulta na sobreposição piscando constantemente.
Andrey Mikhaylov - lolmaus

3

Se você estiver usando jQuery, verifique isso: https://github.com/dancork/jquery.event.dragout

É realmente incrível.

Evento especial criado para lidar com a verdadeira funcionalidade de arrastar.

O evento HTML5 dragleave funciona mais como mouseout. Este plug-in foi criado para replicar a funcionalidade de estilo de ratoeira enquanto arrasta.

Exemplo de uso:

$ ('# myelement'). on ('dragout', function (evento) {// SEU CÓDIGO});

Edição: na verdade, eu não acho que é dependente do jQuery, você provavelmente pode apenas usar o código, mesmo sem ele.


Uau! Dragout é a ÚNICA coisa que funcionou no meu layout, com muitos elementos filhos disparando, dragleavemesmo que eu não tenha saído do dropzone pai.
MarkKasson

Depois de lutar com esse problema pela última hora, dragoutfoi a única solução que funcionou para mim.
Jason Hazel

2

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

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

você parece estar faltando as definições para alguns fora de variáveis de escopo, presumovar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen

@TimoHuovinen sim, correto. Ambos dropZoneHideDelay, dropZoneVisiblesão necessários em dois 'on'eventos de documento na minha implementação.
visitsb

2

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

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

Simples e eficaz! Eu estava descendo a rota stopPropagation () e não gostava de aplicar o manipulador de eventos a todas as crianças. Parecia confuso, mas isso funciona bem e é simples de implementar. Boa ideia.
21416 Jon Jonmullo

1

Então, para mim, a abordagem pointer-events: none;não funcionou muito bem ... Então, aqui está minha solução alternativa:

    #dropzone {
        position: relative;
    }

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

Dessa forma, não é possível para dragleaveo pai (em um filho) ou dragoverum elemento filho. espero que isto ajude :)

* A classe '.active' que adiciono quando eu dragenterou dragleave. Mas se você estiver trabalhando sem isso, deixe a aula de lado.


1
Eu tentei isso em um componente EmberJS D&D e adicionar a classe 'ativa' programaticamente é muito lento cerca de 40% do tempo em que o mouse entra na div pai (visão da brasa). Não sei por que. Mas deixando isso de fora e funciona muito bem, então obrigado por esta solução simples. Testado em um Mac nas versões mais recentes (a partir de hoje) do Safari, Chrome, Firefox.
rmcsharry

1

Correção rápida super simples para isso, não testada extensivamente, mas funciona no Chrome no momento.

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

Isso corrige o bug de cintilação do Chrome, ou pelo menos a permutação que estava me causando problemas.



1

Minha versão:

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

Com este layout:

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

Eu fiz um despejo de classes de elemento para e.target, e.currentTarget, e.relatedTargetpara ambos dragovere dragleaveeventos.

Ele me mostrou que ao deixar o bloco pai ( .dropzone) e.relatedTargetnão é filho desse bloco, então eu sei que estou fora do dropzone.


0

Aqui, uma das soluções mais simples ● ︿ ●

Dê uma olhada neste violino <- tente arrastar algum arquivo dentro da caixa

Você pode fazer algo assim:

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

Por isso você já deve saber o que está acontecendo aqui.
Basta dar uma olhada no violino, você sabe.


0

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:

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

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 divcom id #drop. Adicione isso ao seu Javascript:

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

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

Em seguida, adicione isso ao seu CSS, para desativar todos os filhos da classe .inactive:

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

Assim, os elementos filhos permanecerão inativos enquanto o usuário arrastar o elemento sobre a caixa.


0

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:

$(document).ready(function() {

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

});

Ele lida perfeitamente com o problema sem comprometer nenhuma funcionalidade do DOM.

Download, detalhes e explicações em seu repositório Git .


Como obtenho o evento drag enter no seu plug-in?
Woody

Na verdade, eu não testei sua diretiva, mas acho que não funcionará se o elemento pai (A) não envolver o elemento que eu quero verificar (B). Por exemplo, se não houver preenchimento entre a borda inferior de B e a borda inferior de A, o dragenter nunca será acionado para o elemento A, certo?
marcelj

@marcelj O elemento A pode ser o próprio documento do corpo, então você realmente não precisa de um elemento circundante (o corpo faz o trabalho) com um preenchimento mínimo. A limitação que encontrei posteriormente com essa abordagem é que, se você trabalha com se frameseu elemento está na sua borda. Nesse caso, não sugiro essa abordagem.
Luca Fagioli

0

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.

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

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.


0

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:

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

Então este é o arquivo Javascript anexado :: '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

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:

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

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

E o dropenter acontecerá após o dropleave, só isso!


-1

Use o greedy : truepara a criança como uma função de droppable. Em seguida, inicie apenas o evento clicado na primeira camada.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.