web-dev-qa-db-fra.com

glisser les fichiers de dépôt dans l'entrée de fichier HTML standard

Ces jours-ci, nous pouvons glisser-déposer des fichiers dans un conteneur spécial et les télécharger avec XHR 2. Plusieurs à la fois. Avec des barres de progression en direct, etc. Très cool. Exemple ici.

Mais parfois, nous ne voulons pas autant de sang-froid. Ce que j'aimerais, c’est de glisser-déposer des fichiers - plusieurs à la fois - dans une entrée de fichier HTML standard: <input type=file multiple>.

Est-ce possible? Existe-t-il un moyen de "remplir" l'entrée de fichier avec les bons noms de fichier (?) À partir du fichier déposé? (Les chemins de fichiers complets ne sont pas disponibles pour des raisons de sécurité du système de fichiers.)

Pourquoi? Parce que j'aimerais soumettre un formulaire normal. Pour tous les navigateurs et tous les appareils. Le glisser-déposer est simplement une amélioration progressive pour améliorer et simplifier l'UX. Le formulaire standard avec entrée de fichier standard (attribut + multiple) sera présent. J'aimerais ajouter l'amélioration HTML5.

modifier
Je sais que dans certains navigateurs, vous pouvez parfois (presque toujours) déposer des fichiers dans l'entrée de fichier elle-même. Je sais que Chrome fait généralement cela, mais parfois il échoue et charge ensuite le fichier dans la page actuelle (un gros échec si vous remplissez un formulaire). Je veux le tromper et le browserproof.

127
Rudie

J'ai fait une solution pour cela.

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

La fonctionnalité glisser-déposer de cette méthode ne fonctionne qu'avec Chrome, Firefox et Safari . (Je ne sais pas si cela fonctionne avec IE10), mais pour les autres navigateurs, le bouton "Ou cliquez ici" fonctionne correctement.

Le champ de saisie suit simplement votre souris lorsque vous faites glisser un fichier sur une zone et j'ai également ajouté un bouton.

Opacité sans commentaires: 0; l'entrée de fichier n'est visible que pour que vous puissiez voir ce qui se passe.

44
BjarkeCK

Ce qui suit fonctionne sous Chrome et FF, mais je n'ai pas encore trouvé de solution couvrant également IE10 +:

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

Vous voudrez probablement utiliser addEventListener ou jQuery (etc.) pour enregistrer vos gestionnaires evt - ceci est juste pour des raisons de brièveté.

37
jlb

C’est la méthode HTML5 "DTHML" pour le faire. Entrée de forme normale (qui IS est uniquement en lecture, comme l'a souligné Ricardo Tomasi). Ensuite, si un fichier est déplacé, il est attaché au formulaire. Cela nécessitera une modification de la page d'action pour accepter le fichier téléchargé de cette manière.

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>

C'est encore plus patron si vous pouvez faire de la fenêtre entière une zone de dépôt, voir Comment détecter un événement de glissement HTML5 entrant et sortant de la fenêtre, comme le fait Gmail?

26
William Entriken

//----------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
Dipak

En théorie, vous pouvez ajouter un élément recouvrant le <input/>, puis utiliser son événement drop pour capturer les fichiers (à l'aide de l'API de fichier) et les transmettre au tableau files.

Sauf que l'entrée d'un fichier est en lecture seule. C'est un vieux problème.

Vous pouvez toutefois contourner complètement le contrôle du formulaire et le télécharger via XHR (vous n'êtes pas certain du support pour cela):

Vous pouvez également utiliser un élément situé dans la zone environnante pour annuler l'événement de suppression dans Chrome et empêcher le comportement par défaut du chargement du fichier.

La suppression de plusieurs fichiers sur l'entrée fonctionne déjà dans Safari et Firefox.

6
Ricardo Tomasi

Je sais que certaines astuces fonctionnent sous Chrome. 

Lorsque vous déposez un fichier dans la zone de dépôt, vous obtenez un objet dataTransfer.files, de type "FileList", qui contient tous les fichiers que vous avez fait glisser. Pendant ce temps, l'élément a la propriété "fichiers", c'est le même objet de type "FileList".

Vous pouvez donc associer simplement l'objet dataTransfer.files à la propriété input.files.

3
Timur Gilauri

Pour une solution CSS uniquement:

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

Modifié à partir de https://codepen.io/Scribblerockerz/pen/qdWzJw

3
Jonathan

Excellent travail de @BjarkeCK. J'ai apporté quelques modifications à son travail, pour l'utiliser comme méthode dans 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();

Travailler Fiddle

1
Mr_Green

Quelques années plus tard, j'ai construit cette bibliothèque pour déposer des fichiers dans un élément HTML.

Vous pouvez l'utiliser comme

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();
1
Joel Hernandez

Pour tous ceux qui envisagent de le faire en 2018, j'ai une solution bien meilleure et plus simple que tous les anciens éléments publiés ici. Vous pouvez créer une jolie boîte drag & drop avec juste Vanilla HTML, JavaScript et CSS.

(Fonctionne uniquement dans Chrome jusqu'à présent)

Commençons par le HTML.

<div>
<input type="file" name="file" id="file" class="file">
<span id="value"></span>
</div>

Ensuite, nous passerons au style.

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

Une fois que vous avez fait cela, cela semble déjà bien. Mais j'imagine que vous aimeriez voir le fichier que vous avez chargé en réalité, alors nous allons faire du JavaScript. Rappelez-vous cet intervalle de valeur pfp? C'est là que nous allons imprimer le nom du fichier.

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

Et c'est tout.

1
Michael

C'est ce que je suis sorti avec. 

Utilisation de JQuery et HTML. Cela l'ajoutera aux fichiers d'insertion. 

var dropzone = $('#dropzone')


dropzone.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    e.preventDefault();
    e.stopPropagation();
  })

dropzone.on('dragover dragenter', function() {
    $(this).addClass('is-dragover');
  })
dropzone.on('dragleave dragend drop', function() {
    $(this).removeClass('is-dragover');
  })  
  
dropzone.on('drop',function(e) {
	var files = e.originalEvent.dataTransfer.files;
	// Now select your file upload field 
	// $('input_field_file').prop('files',files)
  });
input {	margin: 15px 10px !important;}

.dropzone {
	padding: 50px;
	border: 2px dashed #060;
}

.dropzone.is-dragover {
  background-color: #e6ecef;
}

.dragover {
	bg-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="" draggable='true' style='padding: 20px'>
	<div id='dropzone' class='dropzone'>
		Drop Your File Here
	</div>
	</div>

0
Lionel Yeo