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