Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a file into an already existing dataTransfer object using Javascript

Assumption: A local HTML/Javascript webpage that has access to file://

At the start of a drag on a draggable HTML element, in the event handler function dragStart(e), how do I add a File object so that it is recognized as a file and ends up in the dataTransfer.files list?

Ex:

function dragStart(e){
    var file = getSomeFileObjFromSomewhere();
    e.originalEvent.dataTransfer.effectAllowed = "all";
    e.originalEvent.dataTransfer.setData("file", file);

    console.log("\nFiles:");
    i = 0;
    var files = e.originalEvent.dataTransfer.files,
    len = files.length;
    for (; i < len; i++) {
        console.log("\nIndex: " + i + "\nFilename: " + files[i].name);
        console.log("Type: " + files[i].type);
        console.log("Size: " + files[i].size + " bytes");
        console.dir(files[i]);
    }
}

Specifically, it needs to work on Chrome/Chromium. And, we can assume that the file exists on the local drive. Basically, I want the same data available then when a file is dragged from Windows Explorer to a HTML page on an element that is droppable.

I know that this exists in Chrome:

e.originalEvent.dataTransfer.setData("DownloadURL", fileType + ":" + name + ":" + filePath);

which downloads the file. But this is not what I want, because I want to assume that this is an existing file and that the original file must be accessed.

like image 455
Kai Avatar asked Jun 30 '14 18:06

Kai


1 Answers

You can use the approach posted by @kol at Simulate drop file event

That is, we have to pass an argument to ondrop, which

  • has a dataTransfer field with a files array subfield, which contains the selected File, and
  • a preventDefault method (a function with no body will do).

adjusted below to attach .addEventListener("drop") to drop element at dragstart event, with File objects passed to a bound function with Function.prototype.bind() which returns the appropriate object described above, with once:true passed at third parameter to .addEventListener(), to call drop event at most once for each dragstart event where File objects are accessed or created.

FileList object is read only, an Array is used to store File object at dataTransfer.files property within a plain javascript object at event handlers.

Note: The FileList interface should be considered "at risk" since the general trend on the Web Platform is to replace such interfaces with the Array platform object in ECMAScript [ECMA-262]. In particular, this means syntax of the sort filelist.item(0) is at risk; most other programmatic use of FileList is unlikely to be affected by the eventual migration to an Array type.

If event.dataTransfer.files at dragstart event contains File objects, iterate FileList and push each File object to files array.

var drag = document.getElementById("drag")
var drop = document.getElementById("drop")

function handleDrop(evt) {
  evt.preventDefault();
  console.log(evt.dataTransfer.files);
}

function getSomeFileObjFromSomewhere() {
  var data = ["abc", "def"];
  var files = [];
  for (var i = 0; i < data.length; i++) {
    files.push(new File([data[i]], data[i] + ".text", {
      type: "text/plain",
      lastModified: new Date().getTime()
    }));
  }
  return files
}

function dataTransferFileObject(files) {
  return {
    preventDefault: function() {},
    dataTransfer: {
      files: Array.isArray(files) ? files : [files]
    }
  }
}

drag.addEventListener("dragstart", function dragStart(e) {

  var files = getSomeFileObjFromSomewhere();
  e.dataTransfer.effectAllowed = "all";

  console.log("\nFiles:");
  
  for (let i = 0; i < files.length; i++) {
    var {name, size, type} = files[i];
    console.log("\nFilename: " + name);
    console.log("Type: " + type);
    console.log("Size: " + size + " bytes");
  }
  // if `e.dataTransfer.files`, push `File` objects dragged
  // to `files` array
  if (e.dataTransfer.files) {
    for (let file of e.dataTransfer.files) {
      files.push(file);
    }
  }
  
  drop.addEventListener("drop"
  , handleDrop.bind(drop, dataTransferFileObject(files))
  , {once: true});

});

drop.addEventListener("dragover", function(evt) {
  evt.preventDefault()
});
div {
  width: 50px;
  height: 50px;
  padding: 10px;
  margin: 10px;
}

div:nth-child(1) {
  border: 2px dotted blue;
}

div:nth-child(2) {
  border: 2px dotted green;
}
<div draggable="true" id="drag">drag</div>
<div droppable="true" id="drop" webkitdropzone="webkitdropzone">drop</div>

plnkr http://plnkr.co/edit/ihQqs4t2zOg2XhIuNwal?p=preview

like image 196
guest271314 Avatar answered Oct 27 '22 16:10

guest271314