I'm a java guy trying my hand in javascript and need some help. I came across an amazing tutorial on image uploads here Mozilla Tutorial and need some help figuring it out. I am currently working on the drag and drop image upload feature. Every time I drag an image onto my area the mouse turns green so it's activated. But then when I let go it should send me an alert that says one image was found. However it always just alerts 0. So the size of the array is 0. Any ideas? Thanks for taking a look. What I've tried with no success...
Copying and pasting the code from the tutorial into my javascript file exactly
Moving the code to add the listeners outside of a function and into a window onload
Every browser I have
...
function toggleStrideMedia()
{
if(getDisplay("strideMediaWrapper") == "" || getDisplay("strideMediaWrapper") == "none")
{
show("strideMediaWrapper");
getElement("strideMediaDropZone").addEventListener("dragenter", dragenter, false);
getElement("strideMediaDropZone").addEventListener("dragover", dragover, false);
getElement("strideMediaDropZone").addEventListener("drop", drop, false);
}
else
{
hide("strideMediaWrapper");
}
}
function dragenter(e)
{
e.stopPropagation();
e.preventDefault();
}
function dragover(e)
{
e.stopPropagation();
e.preventDefault();
}
function drop(e)
{
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
// THIS SHOULD BE GIVING ME A ONE BUT IT ALWAYS GIVES ME A ZERO INSTEAD
alert(files.length);
handleFiles(files);
}
.
UPDATE - Fiddle Results
UPDATE
The actual problem turned out to be that if you try to drag images directly from one web browser tab to this web based drag and drop interface, the event will fire but no files will be dropped. The asker noted this issue on OSX and I was able to replicate the same behavior in Windows 7.
Without seeing your HTML, it's hard to tell what you were having difficulty with. If the ondragover/ondragenter piece wasn't set up correctly then dropping won't work, but you wouldn't see an alert at all, you'd just see the browser render the image from the local filesystem. That also means that you're almost certainly successfully adding the drop event to the correct element.
Try this Fiddle and see if it works for you: http://jsfiddle.net/qey9G/4/
HTML
<div>
<div id="dropzone" style="margin:30px; width:500px; height:300px;
border:1px dotted grey;">
Drag & drop your file here...
</div>
</div>
JavaScript
var dropzone = document.getElementById("dropzone");
dropzone.ondragover = dropzone.ondragenter = function(event) {
event.stopPropagation();
event.preventDefault();
}
dropzone.ondrop= function drop(e)
{
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
alert(files.length);
}
Related
I'm writing a small notetaking webapp which uses CKEditor as a HTML editor. I've been able to integrate it well, with one exception - drag and drop functionality. It mostly works - but there are two quirks I haven't been able to solve despite trying a whole lot of different approaches.
My requirements are simple - I drag any file into CKEditor from outside the app, upon which it should insert a simple anchor tag in the editable text with a local link to the file. Currently, I am implementing drag and drop in my own CKEditor plugin called filemanager:
CKEDITOR.plugins.add( 'filemanager', {
init: function( editor ) {
editor.on('contentDom', function(contentDom) {
//Runs upon dropping a file into the editor
contentDom.editor.document.on('drop', function(e) {
//prevents default behavior as long as it's an actual file drag and drop (if it's dragging text etc. inside the editor, keep default behavior)
if(e.data.$.dataTransfer.files.length)
{
e.data.preventDefault();
}
//Goes through all the dropped files and insert HTML with links
for (var i = 0; i < e.data.$.dataTransfer.files.length; ++i) {
CopyData(e.data.$.dataTransfer.files[i].path);
}
});
});
//Creates the link in the editor
function CopyData(path, range)
{
//Link HTML
var fileHTML;
/* Code to parse the path into HTML to be inserted */
editor.insertHtml(fileHTML);
}
}
});
I have omitted most of the CopyData function as the formatting isn't relevant, but it generates a div such as this:
fileHTML = ""+fileName+"";
This does work perfectly fine, so the basic implementation of the feature seems to work. However, here are my two issues:
Dropping a file into the editor but outside the actual text area (such as below the final line of text in the editor) does not disable default behavior. How can I run a preventDefault() for this area of the editor? Been trying events in all sort of places without results.
The HTML isn't inserted at the location of the mouse cursor, even though CKEditor previews a caret there. Rather, it is inserted where the caret currently is in the document, independently of where you drop. This disrupts core functionality of the feature - is there a way to insert HTML at the mouse position? I would assume so since CKEditor actually previews a caret where the mouse is, but I looked through the documentation without results.
Does anyone have ideas of how to solve the two above issues? If it helps, I am using nw.js so the webapp will always run in Chromium. Grateful for any responses!
I eventually managed to solve these issues.
To prevent default behavior outside the DOM elements, I added this code to my CKEditor plugin:
var iframeWin = window.document.getElementsByTagName('iframe')[0].contentWindow;
iframeWin.addEventListener("dragover",function(e){
e = e || iframeWin.event;
if(e.dataTransfer.files.length)
{
e.preventDefault();
}
},false);
iframeWin.addEventListener("drop",function(e){
e = e || iframeWin.event;
if(e.dataTransfer.files.length)
{
e.preventDefault();
}
},false);
This correctly identified the iframe I needed to disable default behavior in. The if(e.dataTransfer.files.lenght) condition makes sure this only applies to files dragged into the app, and not dragging things within the editor.
Inserting the HTML at the mouse cursor was trickier! This cannot be credited to me - I found a function doing most of the work on the CKEditor bug tracker, and edited it to work in this scenario:
function moveSelectionToDropPosition( editor, dropEvt )
{
var $evt = dropEvt.data.$,
$range,
range = editor.createRange();
// Make testing possible.
if ( dropEvt.data.testRange ) {
dropEvt.data.testRange.select();
return;
}
// Webkits.
if ( document.caretRangeFromPoint ) {
$range = editor.document.$.caretRangeFromPoint( $evt.clientX, $evt.clientY );
range.setStart( CKEDITOR.dom.node( $range.startContainer ), $range.startOffset );
range.collapse( true );
}
// FF.
else if ( $evt.rangeParent ) {
range.setStart( CKEDITOR.dom.node( $evt.rangeParent ), $evt.rangeOffset );
range.collapse( true );
}
// IEs.
else if ( document.body.createTextRange ) {
$range = editor.document.getBody().$.createTextRange();
$range.moveToPoint( $evt.clientX, $evt.clientY );
var id = 'cke-temp-' + ( new Date() ).getTime();
$range.pasteHTML( '<span id="' + id + '">\u200b</span>' );
var span = editor.document.getById( id );
range.moveToPosition( span, CKEDITOR.POSITION_BEFORE_START );
span.remove();
}
range.select();
}
In the drop event I then simply added
moveSelectionToDropPosition(editor, e);
And it all worked as intended!
Hopefully this may help anyone else having similar issues in the future :)
I am using HTML5 native drag and drop for dropping images into a div. Here is an example: http://jsbin.com/ipojuk/1/
The problem is, once dropped the same image should not be dropped again. How to prevent this from happening? For this purpose i have set a data-inplan attribute in image, and i was planning to set it to true once an image is dropped, but i can't find an easy way to get a reference to the original image in function dragDrop. I thought about using an id, but these images are created at runtime, and generating id's will be hard for me.
This will allow you to drop only one image per type (checking the src)
function dragDrop(e) {
if (e.stopPropagation) {
e.stopPropagation(); // Stops some browsers from redirecting
}
var $item = $(e.dataTransfer.getData('text/html'));
var exists = false;
$(".divRendezvous img").each(function()
{
if($(this).attr("src")==$item.attr("src"))
{
exists = true;
return;
}
});
if(!exists)
$item.appendTo(e.target);
return false;
}
jsBin: http://jsbin.com/ipojuk/12/edit
In Javascript, I know how to set up a drag & drop target that accepts file uploads from the user's computer. How can I set up a drop target that accepts images that are dragged from another website? All I need to know is the URL of the image that they've dragged.
I know this is possible, since Google Docs accepts image drops from other websites. Any idea how they're doing it?
UPDATE:
It looks like there are differences between Chrome on Windows and MacOS. On Windows dataTransfer.getData('Text'); works but not on MacOS. dataTransfer.getData('URL'); should work on both.
OLD answer:
You could define a drop zone:
<div id="dropbox">DropZone => you could drop any image from any page here</div>
and then handle the dragenter, dragexit, dragover and drop events:
var dropbox = document.getElementById('dropbox');
dropbox.addEventListener('dragenter', noopHandler, false);
dropbox.addEventListener('dragexit', noopHandler, false);
dropbox.addEventListener('dragover', noopHandler, false);
dropbox.addEventListener('drop', drop, false);
function noopHandler(evt) {
evt.stopPropagation();
evt.preventDefault();
}
function drop(evt) {
evt.stopPropagation();
evt.preventDefault();
var imageUrl = evt.dataTransfer.getData('Text');
alert(imageUrl);
}
It is inside the drop event handler that we are reading the image data from the dataTransfer object as Text. If we dropped an image from some other webpage this text will represent the url of the image.
And here's a live demo.
function drop(evt) {
evt.stopPropagation();
evt.preventDefault();
var imageUrl = evt.dataTransfer.getData('URL'); // instead of 'Text'
alert(imageUrl);
}
Seems to work in Firefox, Safari, and Chrome on Mac. Also works in Firefox, IE, and Chrome in Windows.
Updated fiddle
Although you are able to accept the drag and drop of an image from another website, you are unable to do any processing of it (e.g. converting it to a base64 string using the canvas) (as of 21st August 2014) because of various cross-origin policy issues.
var dt = event.dataTransfer;
var url = dt.getData('url');
if (!url) {
url = dt.getData('text/plain');
if (!url) {
url = dt.getData('text/uri-list');
if (!url) {
// We have tried all that we can to get this url but we can't. Abort mission
return;
}
}
}
Even Google can't get around this - If you use gmail, you can drag and drop an image from another location in to the email body, but all this does is create an <img/> element with its src set to url (from the code above).
However, I've created a plugin that allows you to fake it cross-origin drag and drop. It requires a PHP backend.
Read the article I wrote on it here https://coderwall.com/p/doco6w/html5-cross-origin-drag-and-drop
Here's my solution to the problem: Dropzone js - Drag n Drop file from same page
Please do keep in mind that ability to drag an image from another domain depends on their CORS setup.
Some browsers use text/plain some use text/html as well
This code should pull any text or image source url on the latest Chrome, FF on Mac and PC.
Safari doesn't seem to give the URL so if someone knows how to get it let me know.
I'm still working on IE.
function getAnyText(theevent) {
//see if we have anything in the text or img src tag
var insert_text;
var location = theevent.target;
var etext;
var ehtml;
try {
etext = theevent.dataTransfer.getData("text/plain");
} catch (_error) {}
try {
ehtml = theevent.dataTransfer.getData("text/html");
} catch (_error) {}
if (etext) {
insert_text = etext;
} else if (ehtml) {
object = $('<div/>').html(ehtml).contents();
if (object) {
insert_text = object.closest('img').prop('src');
}
}
if (insert_text) {
insertText(insert_text,location);
}
}
As the other answers correctly state: It normally depends on the CORS-Settings of the server if drag & drop from another browser window is possible (Access-Control-Allow-Origin has to be set).
However I've just found out by chance that it's possible to drap & drop any images from a Firefox (current version 68.0.1) to a Chrome window (current version 76.0.3809) and process it in Javascript, regardless if CORS headers are set or not.
See working example (based on jsfiddle of Darin Dimitrov) which accepts and directly shows images from:
drag and drop from local computer (e.g. from file explorer)
drag and drop from other website, if CORS headers are set, e.g. an image from https://imgur.com/
drag and drop any images from Firefox window to Chrome window:
open jsfiddle demo in Chrome
open e.g. google image search in Firefox and search for any image
click on the image for bigger preview
drag & drop the preview image to the jsfiddle demo in Chrome
However this seems to be kind of a bug of Firefox and therefore I would not rely on this behaviour in an productive application.
I'm working on a Spotify app and built a dropzone inside my application so users can drag music onto it. It works with songs, albums, and playlists. Whenever they drag this content over, their cursor shows a green plus sign and everything works.
Whenever a user selects just a few songs from an existing playlist, though, the dropzone refuses to accept them. No green plus sign is shown. Nothing is transferred to the dropzone.
Any thoughts?
var dropzone = document.querySelector('#dropzone');
dropzone.addEventListener('dragover', handleDragOver, false);
dropzone.addEventListener('drop', handleDrop, false);
function handleDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
return false;
}
function handleDrop(e) {
var droppedURI = e.dataTransfer.getData('Text');
// rest of code here.
}
From my attempts and research (googling) it seems it's not possible at all to drop more than one link into a dropzone. I don't get the point in that so I guess it's bug. However it is possible to drop multiple links onto the app's icon, and at least in my case that's good enough.
How I did it:
models.application.observe(models.EVENT.LINKSCHANGED, function(){
models.application.links.forEach(function(link){
// Use your already existing logic and do whatever your want to do with the links
// For example (assuming you already have a playlist object):
if( (link.split('/')[3] || link.split(':')[1]) === 'track' ){
playlist.add(link);
}
});
});
Hope this helps!
Recently I've been looking into adding a simple HTML5 drag and drop to my already simple php file upload script.
I have read lots of tutorials and other solutions but I can't seem to understand the whole sending the file to server part.
From what I understand, XMLHttpRequest will send the data, but it will do it without reloading the page. This, I do not want. Currently the script I'm using will take the POST data and produce the file upload output, eg. server download link, thumbnail if image etc.
How can I have the drag and drop submit the POST data and either access the upload output or reload the page?
Edit:
I'm using the following for the drag and drop:
<div id="drop_zone">Drag and drop a file here</div>
<script>
function handleFileSelect(evt) {
evt.stopPropagation();
evt.preventDefault();
var files = evt.dataTransfer.files; // FileList object.
uploadFile(files[0]); //<-- This is where most examples will use XMLHttpRequest to construct form and send data
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
document.getElementById('drop_zone').style.backgroundColor = "#faffa3";
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
function handleDragLeave(evt) {
document.getElementById('drop_zone').style.backgroundColor = "";
}
// Setup the dnd listeners.
var dropZone = document.getElementById('drop_zone');
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', handleFileSelect, false);
dropZone.addEventListener('dragleave', handleDragLeave, false);
</script>
If you use JQuery or some other event-capable javascript library you should be able to catch the drag event and submit your form.
http://docs.jquery.com/UI/API/1.8/Draggable
http://api.jquery.com/on/
But no XMLHttpRequest involved...
After more reading and searching around I've come to the conclusion that what I originally wanted cannot be done.
The solution I am going with is to simply have a large transparent/hidden <input type="file"> covering the drop zone. The dropped filename will be read from the input with javascript and displayed in the drop zone. This of course will rely on the browser to support the drag and drop files into html file inputs.
From there my upload script will pretty much operate as if the user had used the visible file selector.
Apologies for not being too clear on what I wanted. I wasn't so sure myself. And thanks for the replies/comments.