4

I have a contenteditable div, and I want the user to be able to drop an image file from their computer into the div. This works as expected in FF, but in chrome, rather than drop the file into the div, it navigates away from the page and opens the file in the browser. I feel like I must be missing something basic here, since facebook, gmail, etc., have file drag and drop that works in Chrome.

I am simply using

<div contenteditable='true'></div>

and here is a fiddle http://jsfiddle.net/Jt9LU/

Anything that I need to add in CSS, JS, jQuery, or the HTML markup would be appreciated, since it really seems like it must be something simple.

Tried in Chrome 34 and Chrome Canary 36

chiliNUT
  • 18,989
  • 14
  • 66
  • 106
  • 1
    FYI - this also does NOT work in IE9 – FastTrack May 08 '14 at 17:42
  • 1
    Possible duplicate of http://stackoverflow.com/questions/10654262/drop-image-into-contenteditable-in-chrome-to-the-cursor – Rob M. May 08 '14 at 17:42
  • @RobM. it sounds like all of the pieces are there, except the OP says "Thanks to the HTML5 you can catch the drop event, and catch the image with it." How would I go about doing that? – chiliNUT May 08 '14 at 17:48
  • 2
    Here is a great tutorial on this topic: http://www.html5rocks.com/en/tutorials/file/dndfiles/ you can also look up HTML5 drag and drop files as there are tutorials galore. – Rob M. May 08 '14 at 17:50

1 Answers1

12

Big thanks to RobM for pointing me in the right direction. Using the other SO answer you provided, and the tutorial link you provided, here is a solution working for me in FF and chrome

(See fiddle: http://jsfiddle.net/MWe8U/)

HTML

Content Editable Div:
        <div id='d' class='demo' contenteditable='true'>
        </div>

CSS

.demo{
    height:400px;
    border:1px solid black;
    overflow-y:scroll;
}

JS

        $(document).ready(function() {
            var handleDrag = function(e) {
                //kill any default behavior
                e.stopPropagation();
                e.preventDefault();
            };
            var handleDrop = function(e) {
                //kill any default behavior
                e.stopPropagation();
                e.preventDefault();
                //console.log(e);
                //get x and y coordinates of the dropped item
                x = e.clientX;
                y = e.clientY;
                //drops are treated as multiple files. Only dealing with single files right now, so assume its the first object you're interested in
                var file = e.dataTransfer.files[0];
                //don't try to mess with non-image files
                if (file.type.match('image.*')) {
                    //then we have an image,

                    //we have a file handle, need to read it with file reader!
                    var reader = new FileReader();

                    // Closure to capture the file information.
                    reader.onload = (function(theFile) {
                        //get the data uri
                        var dataURI = theFile.target.result;
                        //make a new image element with the dataURI as the source
                        var img = document.createElement("img");
                        img.src = dataURI;

                        //Insert the image at the carat

                        // Try the standards-based way first. This works in FF
                        if (document.caretPositionFromPoint) {
                            var pos = document.caretPositionFromPoint(x, y);
                            range = document.createRange();
                            range.setStart(pos.offsetNode, pos.offset);
                            range.collapse();
                            range.insertNode(img);
                        }
                        // Next, the WebKit way. This works in Chrome.
                        else if (document.caretRangeFromPoint) {
                            range = document.caretRangeFromPoint(x, y);
                            range.insertNode(img);
                        }
                        else
                        {
                            //not supporting IE right now.
                            console.log('could not find carat');
                        }


                    });
                    //this reads in the file, and the onload event triggers, which adds the image to the div at the carat
                    reader.readAsDataURL(file);
                }
            };

            var dropZone = document.getElementById('d');
            dropZone.addEventListener('dragover', handleDrag, false);
            dropZone.addEventListener('drop', handleDrop, false);
        });
chiliNUT
  • 18,989
  • 14
  • 66
  • 106
  • 1
    Be careful with this implementation because this way the images are embeded as base64 strings in the HTML, creating bigger pages and preventing the caching and parallel loading, so your pages might get worse performance. – AlfonsoML May 08 '14 at 22:24
  • This is for a rich text editor wherein the data is submitted to the server, and the server scrapes out the base64 urls and converts them to image files, and replaces the `src` attribute with a path to the new image file, so its not a problem. – chiliNUT May 08 '14 at 22:59
  • 1
    Great!, many people might just copy this code without realizing about your backend and they might end up with a horrible CMS – AlfonsoML May 09 '14 at 14:23
  • My goal was to do nothing more than replicate the default behavior in FF which is xactly what this does – chiliNUT May 09 '14 at 17:23