Giter Club home page Giter Club logo

drag-drop's Introduction

drag-drop ci npm downloads javascript style guide

HTML5 drag & drop for humans

In case you didn't know, the HTML5 drag and drop API is a total disaster! This is an attempt to make the API usable by mere mortals.

live demo

See https://instant.io.

features

  • simple API
  • supports files and directories
  • excellent browser support (Chrome, Firefox, Safari, Edge)
  • adds a drag class to the drop target on hover, for easy styling!
  • optionally, get the file(s) as a Buffer (see buffer)

install

npm install drag-drop

This package works in the browser with browserify. If you do not use a bundler, you can use the standalone script directly in a <script> tag.

usage

const dragDrop = require('drag-drop')

dragDrop('#dropTarget', (files, pos, fileList, directories) => {
  console.log('Here are the dropped files', files) // Array of File objects
  console.log('Dropped at coordinates', pos.x, pos.y)
  console.log('Here is the raw FileList object if you need it:', fileList)
  console.log('Here is the list of directories:', directories)
})

Another handy thing this does is add a drag class to the drop target when the user is dragging a file over the drop target. Useful for styling the drop target to make it obvious that this is a drop target!

complete example

const dragDrop = require('drag-drop')

// You can pass in a DOM node or a selector string!
dragDrop('#dropTarget', (files, pos, fileList, directories) => {
  console.log('Here are the dropped files', files)
  console.log('Dropped at coordinates', pos.x, pos.y)
  console.log('Here is the raw FileList object if you need it:', fileList)
  console.log('Here is the list of directories:', directories)

  // `files` is an Array!
  files.forEach(file => {
    console.log(file.name)
    console.log(file.size)
    console.log(file.type)
    console.log(file.lastModifiedDate)
    console.log(file.fullPath) // not real full path due to browser security restrictions
    console.log(file.path) // in Electron, this contains the actual full path

    // convert the file to a Buffer that we can use!
    const reader = new FileReader()
    reader.addEventListener('load', e => {
      // e.target.result is an ArrayBuffer
      const arr = new Uint8Array(e.target.result)
      const buffer = new Buffer(arr)

      // do something with the buffer!
    })
    reader.addEventListener('error', err => {
      console.error('FileReader error' + err)
    })
    reader.readAsArrayBuffer(file)
  })
})

get files as buffers

If you prefer to access file data as Buffers, then just require drag-drop like this:

const dragDrop = require('drag-drop/buffer')

dragDrop('#dropTarget', files => {
  files.forEach(file => {
    // file is actually a buffer!
    console.log(file.readUInt32LE(0))
    console.log(file.toJSON())
    console.log(file.toString('hex')) // etc...

    // but it still has all the normal file properties!
    console.log(file.name)
    console.log(file.size)
    console.log(file.type)
    console.log(file.lastModifiedDate)
  })
})

detect drag-and-dropped text

If the user highlights text and drags it, we capture that as a separate event. Listen for it like this:

const dragDrop = require('drag-drop')

dragDrop('#dropTarget', {
  onDropText: (text, pos) => {
    console.log('Here is the dropped text:', text)
    console.log('Dropped at coordinates', pos.x, pos.y)
  }
})

detect dragenter, dragover and dragleave events

Instead of passing just an ondrop function as the second argument, instead pass an object with all the events you want to listen for:

const dragDrop = require('drag-drop')

dragDrop('#dropTarget', {
  onDrop: (files, pos, fileList, directories) => {
    console.log('Here are the dropped files', files)
    console.log('Dropped at coordinates', pos.x, pos.y)
    console.log('Here is the raw FileList object if you need it:', fileList)
    console.log('Here is the list of directories:', directories)
  },
  onDropText: (text, pos) => {
    console.log('Here is the dropped text:', text)
    console.log('Dropped at coordinates', pos.x, pos.y)
  },
  onDragEnter: (event) => {},
  onDragOver: (event) => {},
  onDragLeave: (event) => {}
})

You can rely on the onDragEnter and onDragLeave events to fire only for the drop target you specified. Events which bubble up from child nodes are ignored so that you can expect a single onDragEnter and then a single onDragLeave event to fire.

Furthermore, neither onDragEnter, onDragLeave, nor onDragOver will fire for drags which cannot be handled by the registered drop listeners. For example, if you only listen for onDrop (files) but not onDropText (text) and the user is dragging text over the drop target, then none of the listed events will fire.

remove listeners

To stop listening for drag & drop events and remove the event listeners, just use the cleanup function returned by the dragDrop function.

const dragDrop = require('drag-drop')

const cleanup = dragDrop('#dropTarget', files => {
  // ...
})

// ... at some point in the future, stop listening for drag & drop events
cleanup()

support pasting files from the clipboard

To support users pasting files from their clipboard, use the provided processItems() function to process the DataTransferItemList from the browser's native 'paste' event.

document.addEventListener('paste', event => {
  dragDrop.processItems(event.clipboardData.items, (err, files) => {
    // ...
  })
})

a note about file:// urls

Don't run your app from file://. For security reasons, browsers do not allow you to run your app from file://. In fact, many of the powerful storage APIs throw errors if you run the app locally from file://.

Instead, start a local server and visit your site at http://localhost:port.

license

MIT. Copyright (c) Feross Aboukhadijeh.

drag-drop's People

Contributors

borewit avatar codealchemist avatar dependabot[bot] avatar drewbo avatar feross avatar fluorescenthallucinogen avatar greenkeeper[bot] avatar greenkeeperio-bot avatar jameskyburz avatar jhiesey avatar linusu avatar nichoth avatar schnaader avatar t-mullen avatar vially avatar whyrusleeping avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

drag-drop's Issues

Teardown

I guess it's currently not possible to deregister an element as a drop target. This would be very usable when using together with React.js.

drag-drop.min.js missing from latest release

Release 5.0.2 ... https://github.com/feross/drag-drop/tree/v5.0.2 ... features dragdrop.min.js. This works as expected.

Release 6 ... https://github.com/feross/drag-drop ... has nothing. No minified or otherwise working code. I thought maybe index.js was your new release, which seemed strange, so I minified it and tested. But after getting a "module is undefined" error and looking deeper, I realized you probably just forgot to add it to the codebase. So my NPM update left me high and dry :)

Reinstalling with npm i drag-drop@5 --save is my temporary workaround. I'd love to know what you did in v6! You should comment your major releases so we can get excited and cheer you on.

Fix regressions in 3.0.0

Dropping a folder no longer works, because we preferred the code path that uses e.dataTransfer.files instead of e.dataTransfer.items, and I also removed the code for e.dataTransfer.items. Turns out that's how directory support was implemented. My bad for not noticing.

I reverted and found another way to address these issues:

#38
#34

The new API:

dragDrop('#dropTarget', function (files, pos, fileList) {
  console.log('Here are the dropped files', files) // this is an Array
  console.log('Dropped at coordinates', pos.x, pos.y)
  console.log('Here is the raw FileList object if you need it:', fileList)
})
  1. We need to prefer e.dataTransfer.items since it gives us directory support. That object is an Array, so the FileList feature request is affected (#38). But I'll also return e.dataTransfer.items (the FileList) as the third argument. That way, if you need the FileList you can still get access to it.

  2. As for the bug where the file type is sometimes empty in Chrome (#34), that bug will return. However, if you want to, you can use the third argument to get at the FileList which doesn't have this Chrome bug.

Note: that for both the above use cases, the FileList will be useless when a directory is dropped, since e.dataTransfer.files can't show the files within the directory. But that's just a fundamental limitation we can't get around for now.

Hope this satisfies everyone.

Empty type for some file types in Chrome

Thank you for the module! Been using it a while, great help!

Ran into a bug:

  1. Set up drag-drop on a target element;
  2. In Chrome drop a .csv (sample csv file) file on the target
  3. Inspect file object returned from by drag-drop, type will be empty;
  4. Try the same with https://github.com/mikolalysenko/drag-and-drop-files, type will be there for .csv file.

Demo: https://codepen.io/anon/pen/QqppOe.

The reason, I think, is that drag-drop is using e.dataTransfer.items, when it’s available, and then .webkitGetAsEntry() and then .file(callback) method on that. Somewhere in this chain the type gets lost. It remains, however, when calling .getAsFile() instead of .webkitGetAsEntry(): https://codepen.io/anon/pen/LzWWMQ.

Found a Chrome bug report here: https://bugs.chromium.org/p/chromium/issues/detail?id=155455

`example.html` almost works out of the box

diff --git a/example.html b/example.html
index d7e24e7..34c6dd9 100644
--- a/example.html
+++ b/example.html
@@ -12,7 +12,7 @@
   </head>
   <body>
     <h1>Drag something onto this page</h1>
-    <script src="dragdrop.bundle.js"></script>
+    <script src="dragdrop.min.js"></script>
     <script>
       window.remove = DragDrop('body', function (files) {
         var names = files.map(function (file) { return file.name })

Example with POST data?

Hi!

I'm trying to figure out how to send drag-dropped files to PHP.
I suspect a working example would be of use to others as well.

The following code reports success, but PHP receives no POST data.
Any advice on how to get the following to work, or is there a better approach?

(Note: jQuery is used here, but a non-jQuery working example would be fine).

<?php
  if ($_FILES) {
    echo 'ok!';
    exit;
  }
?>
<html>
  <head>
    <title>drag-drop PHP example</title>
    <style>
      body {
        height: 100%;
        margin: 5px;
        padding: 20px;
        font-family: arial;
      }
      body.drag {
        margin: 0;
        border: 5px solid red;
      }
    </style>
    <script src="jquery.min.js"></script>
    <script src="dragdrop.min.js"></script>
  </head>
  <body>
    <h1>Drag something onto this page</h1>

    <script>
    $(function(){

      window.remove = DragDrop('body', {
        onDrop: function (files, pos) {
          $.ajax({
            type: 'POST',
            url: 'index.php',
            data: new FormData(files),
            cache: false,
            contentType: false,
            processData: false,
            success: function(data){
              console.log("success");
              console.log(data);
            },
            error: function(data){
              console.log("error");
              console.log(data);
            }
          });
        }
      });

    });
    </script>

  </body>
</html>

FileList and directories are empty in Chrome

When using Chrome, both the fileList object and the directories array passed to drag-drop's callback function appear to be empty. Firefox is still working fine, I haven't been able to test Safari as I don't have a Mac.
This was previously working; I assume some kind of new security enhancement on Chrome or WebKit is to blame.

To replicate:
HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Drag & Drop</title>
</head>
<body>
  <h1>drag &amp; drop here</h1>
  <div id="input" style="min-height: 10rem; border: 1px solid #000;"></div>
  <script src="https://bundle.run/[email protected]"></script>
  <script src="app.js"></script>
</body>
</html>

app.js

dragDrop('#input', (files, pos, fileList, directories) => {
  console.log('Here are the dropped files', files);
  console.log('Dropped at coordinates', pos.x, pos.y);
  console.log('Here is the raw FileList object if you need it:', fileList);
  console.log('Here is the list of directories:', directories);
});

Note console output on drag & drop:

Here are the dropped files 
(3) [File, File, File]
0: File {fullPath: "/1.jpg", isFile: true, isDirectory: false, name: "1.jpg", lastModified: 1564034283085, …}
1: File {fullPath: "/2.jpg", isFile: true, isDirectory: false, name: "2.jpg", lastModified: 1564034283085, …}
2: File {fullPath: "/3.jpg", isFile: true, isDirectory: false, name: "3.jpg", lastModified: 1564034283085, …}
length: 3
[[Prototype]]: Array(0)

Dropped at coordinates 729 163

Here is the raw FileList object if you need it: 
FileList {length: 0}
length: 0
[[Prototype]]: FileList

Here is the list of directories:
[]
length: 0
[[Prototype]]: Array(0)

Is this something that can be fixed? Or are whatever security change(s) responsible insurmountable?

File name undefined

i have this:
var dragDrop = require('drag-drop');

dragDrop('#target', function (file) {
console.log('Here are the dropped files ' + file.name);
});

When i drop a single file, the console returns: "Here are the dropped files undefined"
How can i fix it? Thanks!

How to get the original path of a dragged file

When I drag/drop /Volumes/Drive12/project3/export_8.csv, the file.fullPath = /export_8.csv.

You mention this is due to browser security restrictions. However the app I'm building does file management, which requires the complete path (moving files from any place to any other).

There's a preview-an-image-before-it-is-uploaded thread on stackoverflow that suggests using URL.createObjectURL() or FileReader.readAsDataURL(), however I don't see how to implement this with drag-drop.

Is there a workaround to retrieve the complete filesystem path of a dragged file?

Large files crash the browser (Chromium)

I'm trying to use drag-drop as seen in your WebTorrent README, to upload a file for seeding. It works just fine for files under ~300MB, but files pushing ~500MB alway cause the page to crash in Chrome. Firefox seems to be able to handle the larger files. Is this some kind of limitation with Chrome? Should I be using buffers to mitigate this? If so, how do I actually use buffers with drag-drop? I don't use browserify, and I'm not sure how to do the equivalent of require('drag-drop/buffer'), do I just the buffer.js file?

FullPath always on root directory

I have been trying to use this library and the fullPath always shows (/<name of file>), from what I see from the code this seems to be by design:

files.forEach(function (file) {
    file.fullPath = '/' + file.name
})

drag-drop/index.js

Lines 130 to 132 in ff16569

files.forEach(function (file) {
file.fullPath = '/' + file.name
})

In my case I need to start a process external that requires the full path to a file <Path to file>/<name of file>.

is this something possible? is the fullpath not as intended? or is it possible to add another property with this fullPath

thanks in advance for your help.

drag-drop/buffer drop "fullPath" property

drag-drop's result have file.fullPath.

var dragDrop = require('drag-drop')
dragDrop('#dropTarget', function (files) {
  files.forEach(function (file) {
    console.log(file.fullPath)
  })
})

But, drag-drop/buffer's result not have fullPath

var dragDrop = require('drag-drop/buffer')
dragDrop('#dropTarget', function (files) {
  files.forEach(function (file) {
    console.log(file.fullPath) // undefined
  })
}
  • drag-drop/buffer.js

    Lines 13 to 16 in 250dd93

    buffer.name = file.name
    buffer.size = file.size
    buffer.type = file.type
    buffer.lastModifiedDate = file.lastModifiedDate

Is this expected design?

nw.js: document is not defined

I'm a bit new to all this so it's probably an issue with my (lack of) understanding of nw.js or JS in general but any pointers are appreciated!

I tried require()ing in the normal way and upon running my app I am told:

"Uncaught ReferenceError: document is not defined".

I have also tried linking to the dragdrop.bundle.js as the src of a script element but I am a little unsure what the line "This exports a DragDrop function on window" means and so I'm not sure I have tried to use it properly. The error I am getting reads:

"Uncaught TypeError: Cannot read property 'addEventListener' of null".

I would really like to use your module rather than the clunky offerings from HTML5 (which do seem to be working for me)! Thanks for any help you can offer.

Support for IE

This module doesn't seem to be transpiled for compatibility with Internet Explorer as it includes template literals.

Any chance this module can support IE?

say i want the directory?

instead of the entire file list.

Try dragging a folder on, it will expand the directory into a file list. It'd be nice if I could tell it to not recurse through the folder and instead just give me the directory path that they dropped

Can't apply drag & drop to class

Hey,
I want to create multiple dropzones. They have in common, that the all have the same class, so i though i can use as a selector in my dragdrop function the class ".dropzone"

window.remove = DragDrop('body', {
        onDrop: function (files, pos) {
          var names = files.map(function (file) { return file.name })
          window.alert('onDrop fired! ' + files.length + ' files: ' + names.join(', '))
          window.files = files
          console.log('onDrop', files)
          console.log('Dropped at coordinates', pos.x, pos.y)
        }
      })

But I'm getting this error:

Uncaught TypeError: Cannot read property 'addEventListener' of null

When i change the class back to body, it works.

What can I do to prevent this ?
To mention it, I'm using your min.js file

Standalone script bundler no longer works

Seems bundle.run (found in your README.md) has been broken (or breaking) for some time. Is there an alternative? Or better yet, could you supply standalone version in your npm builds so those of us who simply use npm for installing/updating packages can keep coding the way we want?

How about onDragEnter handler?

Thanks for package!

It would be great if we could do something like this:

dragDrop('.container', {
  onDrop(files, pos) {
    console.log('Here are the dropped files', files)
    console.log('Dropped at coordinates', pos.x, pos.y)
  },

  // ---

  onDragEnter() {
    // ...
  }

  // ---

  onDragLeave(e) {
    console.log('dragend', e);
  }
});

What do you think?

Continue reading entries broken

It looks like the recursion, to read more entries is called with missing argument reader.

I am getting the following error:

Uncaught TypeError: Cannot read property 'readEntries' of undefined
    at readEntries (C:\Users\Borewit\code\github\drag-drop\index.js:176)
    at C:\Users\Borewit\code\github\drag-drop\index.js:179

original issue: webtorrent-desktop#1771

drag-drop/index.js

Lines 175 to 184 in 89fd6bd

function readEntries (reader) {
reader.readEntries(entries_ => {
if (entries_.length > 0) {
entries = entries.concat(Array.from(entries_))
readEntries() // continue reading entries until `readEntries` returns no more
} else {
doneEntries()
}
})
}

I think like 179 should be:

readEntries(reader)

PR: #53 #51

Can't drag & drop with nw and frame disabled

It seems that I can't use drag-drop when I disable frame in package.json. Even if i set the draggable id as -webkit-app-region: drag;
My package.json

{
    "name": "Test",
    "main": "app/index.html",
    "window": {
        "width": 380,
        "height": 380,
        "frame": false,
        "resizable": false,
        "toolbar": false
    },
    "dependencies": {
        "drag-drop": "^2.13.2",
        "file-type": "^4.1.0",
        "font-awesome": "^4.7.0",
        "read-chunk": "^2.0.0"
    }

With the same code if i turn frame to true everything works as expected.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.