arraste os ficheiros de largada para a entrada normal de ficheiros html

Nestes dias, podemos arrastar e largar ficheiros num contentor especial e carregá-los com o XHR 2. Muitos de cada vez. Com barras de progresso ao vivo, etc. Coisas muito fixes. Exemplo aqui.

Mas às vezes não queremos tanta frieza. O que eu gostaria era arrastar os ficheiros & drop -- muitos de cada vez -- para um ficheiro HTML padrão: <input type=file multiple>.

Isso é possível? Existe alguma forma de 'preencher' a entrada do ficheiro com os nomes de ficheiros correctos (?) da entrega do arquivo? (Filepaths completos não estão disponíveis por razões de segurança do sistema de arquivos.)

porquê?Porque gostaria de apresentar um formulário normal. Para todos os navegadores e todos os dispositivos. O drag & drop é apenas uma melhoria progressiva para melhorar e simplificar o UX. O formulário padrão com entrada de arquivo padrão (+ multiple atributo) estará lá. Eu gostaria de adicionar o aprimoramento HTML5.

Editar
Eu sei que em alguns navegadores Você pode às vezes (quase sempre) largar ficheiros na entrada de ficheiros em si. I conheça o Chrome geralmente faz isso, mas às vezes ele falha e, em seguida, carrega o arquivo na página atual (uma grande falha se você está preenchendo um formulário). Eu quero enganá-lo e torná-lo à prova de navegação.

Author: Rudie, 2011-11-04

11 answers

Fiz uma solução para isto.

$(function () {
    var dropZoneId = "drop-zone";
    var buttonId = "clickHere";
    var mouseOverClass = "mouse-over";

    var dropZone = $("#" + dropZoneId);
    var ooleft = dropZone.offset().left;
    var ooright = dropZone.outerWidth() + ooleft;
    var ootop = dropZone.offset().top;
    var oobottom = dropZone.outerHeight() + ootop;
    var inputFile = dropZone.find("input");
    document.getElementById(dropZoneId).addEventListener("dragover", function (e) {
        e.preventDefault();
        e.stopPropagation();
        dropZone.addClass(mouseOverClass);
        var x = e.pageX;
        var y = e.pageY;

        if (!(x < ooleft || x > ooright || y < ootop || y > oobottom)) {
            inputFile.offset({ top: y - 15, left: x - 100 });
        } else {
            inputFile.offset({ top: -400, left: -400 });
        }

    }, true);

    if (buttonId != "") {
        var clickZone = $("#" + buttonId);

        var oleft = clickZone.offset().left;
        var oright = clickZone.outerWidth() + oleft;
        var otop = clickZone.offset().top;
        var obottom = clickZone.outerHeight() + otop;

        $("#" + buttonId).mousemove(function (e) {
            var x = e.pageX;
            var y = e.pageY;
            if (!(x < oleft || x > oright || y < otop || y > obottom)) {
                inputFile.offset({ top: y - 15, left: x - 160 });
            } else {
                inputFile.offset({ top: -400, left: -400 });
            }
        });
    }

    document.getElementById(dropZoneId).addEventListener("drop", function (e) {
        $("#" + dropZoneId).removeClass(mouseOverClass);
    }, true);

})
#drop-zone {
    /*Sort of important*/
    width: 300px;
    /*Sort of important*/
    height: 200px;
    position:absolute;
    left:50%;
    top:100px;
    margin-left:-150px;
    border: 2px dashed rgba(0,0,0,.3);
    border-radius: 20px;
    font-family: Arial;
    text-align: center;
    position: relative;
    line-height: 180px;
    font-size: 20px;
    color: rgba(0,0,0,.3);
}

    #drop-zone input {
        /*Important*/
        position: absolute;
        /*Important*/
        cursor: pointer;
        left: 0px;
        top: 0px;
        /*Important This is only comment out for demonstration purposes.
        opacity:0; */
    }

    /*Important*/
    #drop-zone.mouse-over {
        border: 2px dashed rgba(0,0,0,.5);
        color: rgba(0,0,0,.5);
    }


/*If you dont want the button*/
#clickHere {
    position: absolute;
    cursor: pointer;
    left: 50%;
    top: 50%;
    margin-left: -50px;
    margin-top: 20px;
    line-height: 26px;
    color: white;
    font-size: 12px;
    width: 100px;
    height: 26px;
    border-radius: 4px;
    background-color: #3b85c3;

}

    #clickHere:hover {
        background-color: #4499DD;

    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="drop-zone">
    Drop files here...
    <div id="clickHere">
        or click here..
        <input type="file" name="file" id="file" />
    </div>
</div>

A funcionalidade de arrastar e largar para este método só funciona com o Chrome, o Firefox e o Safari. (Não sei se funciona com IE10), mas para outros navegadores, o botão "ou clique aqui" funciona bem.

O campo de entrada simplesmente segue o seu rato ao arrastar um ficheiro sobre uma área, e eu adicionei um botão também..

Opacidade de descompressão: 0; a entrada do ficheiro só está visível para que você posso ver o que se passa.

 37
Author: BjarkeCK, 2016-09-01 08:52:36

Os seguintes trabalhos em cromado e FF, mas ainda não encontrei uma solução que cubra IE10+ também:

// dragover and dragenter events need to have 'preventDefault' called
// in order for the 'drop' event to register. 
// See: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations#droptargets
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
  evt.preventDefault();
};

dropContainer.ondrop = function(evt) {
  // pretty simple -- but not for IE :(
  fileInput.files = evt.dataTransfer.files;
  evt.preventDefault();
};
<!DOCTYPE html>
<html>
<body>
<div id="dropContainer" style="border:1px solid black;height:100px;">
   Drop Here
</div>
  Should update here:
  <input type="file" id="fileInput" />
</body>
</html>

Você provavelmente vai querer usar addEventListener ou jQuery (etc.) para registrar seus tratadores de evt-isso é apenas por brevidade.

 28
Author: jlb, 2016-08-16 07:29:11
Esta é a forma de "DTHML" HTML5 de o fazer. Entrada de forma Normal (que é lido apenas como Ricardo Tomasi apontou). Então, se um arquivo é arrastado para dentro, ele é anexado ao formulário. Isto irá requerer modificação na página de Acção para aceitar o ficheiro carregado desta forma.

function readfiles(files) {
  for (var i = 0; i < files.length; i++) {
    document.getElementById('fileDragName').value = files[i].name
    document.getElementById('fileDragSize').value = files[i].size
    document.getElementById('fileDragType').value = files[i].type
    reader = new FileReader();
    reader.onload = function(event) {
      document.getElementById('fileDragData').value = event.target.result;}
    reader.readAsDataURL(files[i]);
  }
}
var holder = document.getElementById('holder');
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (e) {
  this.className = '';
  e.preventDefault();
  readfiles(e.dataTransfer.files);
}
#holder.hover { border: 10px dashed #0c0 !important; }
<form method="post" action="http://example.com/">
  <input type="file"><input id="fileDragName"><input id="fileDragSize"><input id="fileDragType"><input id="fileDragData">
  <div id="holder" style="width:200px; height:200px; border: 10px dashed #ccc"></div>
</form>

É ainda mais importante se conseguir fazer de toda a janela uma zona de largada, ver Como é que detecto um evento de arrastamento HTML5 a entrar e a sair da janela, como o Gmail faz?

 25
Author: William Entriken, 2017-11-28 23:11:40

//----------App.js---------------------//
$(document).ready(function() {
    var holder = document.getElementById('holder');
    holder.ondragover = function () { this.className = 'hover'; return false; };
    holder.ondrop = function (e) {
      this.className = 'hidden';
      e.preventDefault();
      var file = e.dataTransfer.files[0];
      var reader = new FileReader();
      reader.onload = function (event) {
          document.getElementById('image_droped').className='visible'
          $('#image_droped').attr('src', event.target.result);
      }
      reader.readAsDataURL(file);
    };
});
.holder_default {
    width:500px; 
    height:180px; 
    border: 10px dashed #ccc;
}

#holder.hover { 
    width:400px; 
    height:180px; 
    border: 10px dashed #0c0 !important; 
}

.hidden {
    visibility: hidden;
}

.visible {
    visibility: visible;
}
<!DOCTYPE html>

<html>
    <head>
        <title> HTML 5 </title>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
    </head>
    <body>
      <form method="post" action="http://example.com/">
        <div id="holder" style="" id="holder" class="holder_default">
          <img src="" id="image_droped" width="500" height="180" style="width:500px; height:180px; border: 10px dashed #7A97FC;" class=" hidden"/>
        </div>
      </form>
    </body>
</html>
 8
Author: Piccaza, 2016-12-09 08:52:02

Em teoria, você poderia adicionar um elemento sobrepondo o {[[0]}, e então usar o evento drop para capturar os arquivos (usando a API de arquivo) e passá-los para a matriz de entrada files.

Excepto que uma entrada de ficheiro é apenas para leitura . Este é um problema antigo.

Você pode, no entanto, contornar o controlo do formulário completamente e enviar através do XHR (Não tenho a certeza sobre o suporte para isso):

Poderá também usar um elemento na área circundante para cancelar o evento de largada no Chrome e evitar o comportamento por omissão de carregar o ficheiro.

Largar vários ficheiros sobre a entrada já funciona no Safari e no Firefox.

 6
Author: Ricardo Tomasi, 2011-11-14 07:26:35
Sei que alguns truques funcionam no cromado. Quando largares um ficheiro na zona de largada, tens um dataTransfer.objeto de arquivos, que é um tipo de objeto "FileList", que contém todos os arquivos que você arrastou. Enquanto isso, elemento tem propriedade "arquivos", que é o mesmo objeto Tipo "FileList". Então, podes simplesmente asseverar o dataTransfer.os ficheiros objectam à entrada.propriedade dos ficheiros.
 3
Author: Timur Gilauri, 2017-02-17 07:13:15
Excelente trabalho de @BjarkeCK. Eu fiz algumas modificações ao seu trabalho, para usá-lo como método em jquery:
$.fn.dropZone = function() {
  var buttonId = "clickHere";
  var mouseOverClass = "mouse-over";

  var dropZone = this[0];
  var $dropZone = $(dropZone);
  var ooleft = $dropZone.offset().left;
  var ooright = $dropZone.outerWidth() + ooleft;
  var ootop = $dropZone.offset().top;
  var oobottom = $dropZone.outerHeight() + ootop;
  var inputFile = $dropZone.find("input[type='file']");
  dropZone.addEventListener("dragleave", function() {
    this.classList.remove(mouseOverClass);
  });
  dropZone.addEventListener("dragover", function(e) {
    console.dir(e);
    e.preventDefault();
    e.stopPropagation();
    this.classList.add(mouseOverClass);
    var x = e.pageX;
    var y = e.pageY;

    if (!(x < ooleft || x > ooright || y < ootop || y > oobottom)) {
      inputFile.offset({
        top: y - 15,
        left: x - 100
      });
    } else {
      inputFile.offset({
        top: -400,
        left: -400
      });
    }

  }, true);
  dropZone.addEventListener("drop", function(e) {
    this.classList.remove(mouseOverClass);
  }, true);
}

$('#drop-zone').dropZone();

Violino A Trabalhar

 1
Author: Mr_Green, 2016-07-29 06:52:01

Para uma solução CSS apenas:

<div class="file-area">
    <input type="file">
    <div class="file-dummy">
        <span class="default">Click to select a file, or drag it here</span>
        <span class="success">Great, your file is selected</span>
    </div>
</div>

.file-area {
    width: 100%;
    position: relative;
    font-size: 18px;
}
.file-area input[type=file] {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    cursor: pointer;
}
.file-area .file-dummy {
    width: 100%;
    padding: 50px 30px;
    border: 2px dashed #ccc;
    background-color: #fff;
    text-align: center;
    transition: background 0.3s ease-in-out;
}
.file-area .file-dummy .success {
    display: none;
}
.file-area:hover .file-dummy {
    border: 2px dashed #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy {
    border-color: #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy .success {
    display: inline-block;
}
.file-area input[type=file]:valid + .file-dummy .default {
    display: none;
}

Modificado a partir de https://codepen.io/Scribblerockerz/pen/qdWzJw

 1
Author: Jonathan, 2017-07-22 00:49:03
Para quem quiser fazer isto em 2018, tenho uma solução muito melhor e mais simples do que todas as coisas antigas postadas aqui. Você pode fazer uma caixa de drag & drop bonita com apenas vanilla HTML, JavaScript e CSS.

(só funciona em cromado até agora)

Vamos começar pelo HTML.
<div>
<input type="file" name="file" id="file" class="file">
<span id="value"></span>
</div>
Depois vamos ao estilo.
    .file {
        width: 400px;
        height: 50px;
        background: #171717;
        padding: 4px;
        border: 1px dashed #333;
        position: relative;
        cursor: pointer;
    }

    .file::before {
        content: '';
        position: absolute;
        background: #171717;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 100%;
        height: 100%;
    }

    .file::after {
        content: 'Drag & Drop';
        position: absolute;
        color: #808080;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
Depois de fazeres isto, já parece bem. Mas imagino que gostaria de ver que ficheiro enviou, por isso vamos para fazer um JavaScript. Lembras - te daquele intervalo de valor pfp? É onde vamos imprimir o nome do ficheiro.
let file = document.getElementById('file');
file.addEventListener('change', function() {
    if(file && file.value) {
        let val = file.files[0].name;
        document.getElementById('value').innerHTML = "Selected" + val;
    }
});
E é isso.
 1
Author: Michael, 2018-07-08 18:38:27

Alguns anos depois, construí esta biblioteca para fazer largar ficheiros em qualquer elemento HTML.

Podes usá-lo como

const Droppable = require('droppable');

const droppable = new Droppable({
    element: document.querySelector('#my-droppable-element')
})

droppable.onFilesDropped((files) => {
    console.log('Files were dropped:', files);
});

// Clean up when you're done!
droppable.destroy();
 0
Author: Joel Hernandez, 2018-04-08 10:32:50

O que pode fazer é mostrar uma entrada de ficheiros e sobrepô-la com a sua área transparente, tendo o cuidado de usar um nome como file[1]. {Certifique-se que tem o enctype="multipart/form-data" dentro da etiqueta do formulário.}

Então faça com que a área de largada trate dos ficheiros extra, criando dinamicamente mais entradas de ficheiros para os ficheiros 2..numero_of_fices, certifique-se de usar o mesmo nome de base, populando adequadamente o valor-atributo.

Por último (front-end), apresentar o formulário.


Tudo o que é necessário para lidar este método é alterar o seu procedimento para lidar com uma série de arquivos.

 -1
Author: Shark8, 2011-11-11 02:09:40