I can't tell for sure how all browsers did / do / will behave in such a case, but what I've seen is that when you drag an image from a webpage and drop it to a webpage (the same or an other one), browsers won't create a File (or Blob) from it, but only its markup.
This means that you will still be tied by cross-origin policies in this case, i.e you won't be able to access the data of the image file, and thus won't be able to send it to your server either.
But this won't prevent you from displaying this image on your page, nor to grab its src
.
So what you can do, is a two cases handling:
- Your user drops a File from its file-system: you will have to send it to your server, best through a FormData as multipart. To display it, create a new
<img>
element and set its src
to a blobURI made from the File.
- They drop from an other webpage: The dataTransfer should contain a
text/html
item. If so, try to parse it and look for existing <img>
elements. If there are, you can adopt them in your current document for display. However to save it on your server, you will have to fetch from your server directly (which is not tied by the same-origin policies), and thus you will have to send this URIs to your server.
var dropzone = document.getElementById('dropzone'),
send_btn = document.getElementById('send'),
res = document.getElementById('res'),
imgList = [];
dropzone.ondragover = function ondragover(e) {
e.preventDefault();
dropzone.classList.add('dragover');
};
dropzone.ondrop = function ondrop(e) {
e.preventDefault();
dropzone.classList.remove('dragover');
// try to get images from this dropevent
var imageObjects = retrieveImageData(e.dataTransfer);
if (!imageObjects) return;
imageObjects.forEach(function appendToDoc(imgObj) {
res.appendChild(imgObj.element);
});
// store it
imgList = imgList.concat(imageObjects);
if (imageObjects.length)
send_btn.disabled = false;
};
dropzone.ondragexit = function(ondragexit) {
dropzone.classList.remove('dragover');
};
function retrieveImageData(dT) {
// first try to get Files
var files = getFiles(dT);
if (files.length) {
return files;
}
// if none, try to get HTMLImage or SVGImage
var elems = getHTMLMarkup(dT);
if (elems && elems.length) {
return elems;
}
// we could also try to getData('text/plain') hoping for an url
// but this might not be such a good idea...
console.warn('unable to retrieve any image in dropped data');
}
function getFiles(dT) {
// quite simple: won't traverse folders
var files = [],
imgObj;
if (dT.files && dT.files.length) {
for (var i = 0; i < dT.files.length; i++) {
// only image Files
if (dT.files[i].type.indexOf('image/') === 0) {
imgObj = {
type: 'file',
element: new Image(),
file: dT.files[i]
};
imgObj.element.onerror = onIMGError;
imgObj.element.src = URL.createObjectURL(imgObj.file);
files.push(imgObj);
}
}
}
return files;
}
function getHTMLMarkup(dT) {
var markup = dT.getData('text/html');
if (markup) {
var doc = new DOMParser().parseFromString(markup, 'text/html');
var imgs = doc && doc.querySelectorAll('img,image') || [];
imgs.forEach(toImageObject);
return Array.prototype.map.call(imgs, toImageObject);
}
function toImageObject(element) {
var img;
if (element instanceof SVGImageElement) {
img = new Image();
img.src = element.getAttributeNS('http://www.w3.org/1999/xlink', 'href') ||
element.getAttribute('href');
} else {
img = document.adoptNode(element);
}
img.onerror = onIMGError;
return {
type: 'element',
element: img
};
}
}
// Once we got everything, time to retrieve our objects
send_btn.onclick = function sendData() {
var fD = new FormData();
// send Files data directly
var files = imgList.filter(function isFile(obj) {
return obj.type === 'file';
});
files.forEach(function appendToFD(obj) {
fD.append('files[]', obj.file);
});
// for elems, we will need to grab the data from the server
var elems = imgList.filter(function isElem(obj) {
return obj.type === "element";
});
var urls = elems.map(function grabURL(obj) {
return obj.element.src;
});
if (urls.length)
fD.append('urls', JSON.stringify(urls));
sendFormData(fD);
};
function sendFormData(fD) {
var xhr = new XMLHttpRequest();
xhr.open('POST', 'your_url');
// you would normally send it
//xhr.send(fD);
// but here we will just log the formData's content
var files = fD.getAll('files[]');
console.log('files: ', files);
var urls = fD.get('urls');
console.log('urls', urls);
}
// in case we can't load it
function onIMGError() {
var img = this;
var index = -1;
imgList.forEach(function search(obj, i) {
if (index < 0 && obj && obj.element === img)
index = i;
});
// remove from our list
if (index > -1) {
imgList.splice(index, 1);
if (img.parentNode) img.parentNode.removeChild(img);
}
}
#dropzone {
width: 300px;
height: 150px;
border: 1px solid black;
}
#dropzone.dragover {
background: rgba(0, 0, 0, .5);
}
#res {
border: 1px solid black;
}
<div id="dropzone">drop here</div>
<button id="send" disabled>log saved objects</button>
<div id="res">results:<br></div>
Also note that the default of a contenteditable
container would be to display this <img>
. I didn't wanted to integrate this case in this long enough example, but I'm sure you'll find a way to handle it if needed.