1

So I have implemented a very simple drag and drop file upload widget. Basically my widget is a vertical panel with a couple of labels and a button inside. The user can either drag file into vertical panel or click button and browse for file.

My problem is that when I drag a file into the vertical panel it fires the DragLeaveEvent every time I drag the item over the space that the labels or button occupies. I want it to know that the item is in the vertical panel even when it is on top of the label or button. Im sure I am missing something simple. I provide the drag functionality by adding these dom handlers to the vertical panel:

addDomHandler(new DragEnterHandler() {
            @Override
            public void onDragEnter(DragEnterEvent event) {
                System.out.println("drag enter");
                highlight(true);
            }
        }, DragEnterEvent.getType());

addDomHandler(new DragLeaveHandler() {
            @Override
            public void onDragLeave(DragLeaveEvent event) {
                System.out.println("drag leave");
                highlight(false);
            }
        }, DragLeaveEvent.getType());

addDomHandler(new DragOverHandler() {
            @Override
            public void onDragOver(DragOverEvent event) {
            }
        }, DragOverEvent.getType());

addDomHandler(new DropHandler() {
            @Override
            public void onDrop(DropEvent event) {
                System.out.println("drop");
                // stop default behaviour
                event.preventDefault();
                event.stopPropagation();

                // starts the fetching, reading and callbacks
                if (fileUploadHandler != null) {
                    handleFiles(event.getDataTransfer(), fileUploadHandler);
                }
                highlight(false);
            }
        }, DropEvent.getType());    
Will
  • 292
  • 4
  • 16

3 Answers3

0

Check that the event target is a child (or grand child) of your panel, or in this case maybe rather whether the event target is exactly your panel's element:

if (verticalPanel.getElement().isOrHasChild(Node.as(event.getNativeEvent().getEventTarget()))) {
   // within the panel (possibly on a child)
}

if (verticalPanel.getElement() == Node.as(event.getNativeEvent().getEventTarget())) {
   // targetting exactly the panel (e.g. leaving the panel, not one of its children)
}
Thomas Broyer
  • 64,353
  • 7
  • 91
  • 164
  • I see how this shows me what elements are children, but what I want to accomplish is dragging a file anywhere in the vertical panel and being able to drop it, however I am unable to do so because the drag handlers consider the labels and stuff not in the panel. I want the file to be considered inside the vertical panel no matter if it crosses over a label or button – Will Jul 18 '13 at 16:41
  • I suppose that you receive `DragEnter`, `DragOver` and `DragLeave` for each and every child element (because of event bubbling), so you'll want to only process those events that directly target the panel; i.e. ignore events that target your label and button. – Thomas Broyer Jul 19 '13 at 09:34
  • Yes, but the problem remains that it sends a drag leave event when I am still inside the panel but go over a label because it thinks it is leaving the panel and entering the label even thought the label is inside the panel – Will Jul 19 '13 at 13:58
  • Oh, I'm only understanding right now: when the mouse enters the label, you receive a `dragleave` for the panel. This is strange, and in violation of [the spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dnd) (and common sense). The spec is a bit cryptic but it says that unless the label is a _dropzone_ by itself, you shouldn't have `dragleave` events on children when you move over the panel. Could you reproduce it in "pure JS" on sites like jsfiddle.net? (that'd help determine whether it's a browser bug, a GWT bug or a bug somewhere in your code) – Thomas Broyer Jul 19 '13 at 14:14
0

Through lots of research I have come to the only solution I could find. I set highlight to true in the dragover handler instead of drag enter.

    panel.addDomHandler(new DragEnterHandler() {
        @Override
        public void onDragEnter(DragEnterEvent event) {
        }
    }, DragEnterEvent.getType());

    panel.addDomHandler(new DragLeaveHandler() {
        @Override
        public void onDragLeave(DragLeaveEvent event) {
            highlight(false);
        }
    }, DragLeaveEvent.getType());

    panel.addDomHandler(new DragOverHandler() {
        @Override
        public void onDragOver(DragOverEvent event) {
            highlight(true);
        }
    }, DragOverEvent.getType());

    panel.addDomHandler(new DropHandler() {
        @Override
        public void onDrop(DropEvent event) {
            // stop default behaviour
            event.preventDefault();
            event.stopPropagation();

            // starts the fetching, reading and callbacks
            handleFiles(event.getDataTransfer());
            highlight(false);
        }
    }, DropEvent.getType());
Will
  • 292
  • 4
  • 16
0

I copy pasted your code, but also added a:

RootPanel.get().addHandler(dropHandler, DropEvent.getType());

My drophandler looks like this:

DropHandler dropHandler = new DropHandler() {

        @Override
        public void onDrop(DropEvent event) {
            handleFiles(event.getDataTransfer(), new FileUploadHandler() {
                @Override
                public TYPE specifyFileType() {
                    return TYPE.BINARY;
                }

                @Override
                public void handleFileContent(String fileName, String fileContent) {
                    // do stuff with filename and content
                }

                @Override
                public boolean checkFileName(String fileName) {
                    return true;
                }
            });
            event.preventDefault();
            event.stopPropagation();
        }
    };

and the file-upload interface:

public interface FileUploadHandler {

    static public enum TYPE {
         TEXT, BINARY, DATAURL
    };

    // check the filename and extension and return true if you are happy with
    // proceeding
    // returnning false will prevent the file from being read
    boolean checkFileName(String fileName);

    // tell the method to use to read this file
    TYPE specifyFileType();

    // do your stuff here, eg upload to a server
    void handleFileContent(String fileName, String fileContent);
}

and the handle files func: (note you will have to change classpath to the FileUploadHandler-interface)

// native method to make use of the HTML5 file API functionality
private final native void handleFiles(JavaScriptObject dataTransfer, FileUploadHandler fileUploadHandler) /*-{
    var files = dataTransfer.files;
    var i;
    var file;
    var reader = new FileReader();
    for (i = 0; i < files.length; i++) {
        file = files[i];
        if (fileUploadHandler.@<classpath_to>.FileUploadHandler::checkFileName(Ljava/lang/String;)(file.name)) {
            var type = fileUploadHandler.@<classpath_to>.FileUploadHandler::specifyFileType()();
            reader.onload = function(e) {
                fileUploadHandler.@<classpath_to>.FileUploadHandler::handleFileContent(Ljava/lang/String;Ljava/lang/String;)(file.name, e.target.result);
            }
            if (type == "TEXT") {
                reader.readAsText(file);
            } else if (type == "BINARY") {
                reader.readAsBinaryString(file);
            } else if (type == "DATAURL") {
                reader.readAsDataURL(file);
                // not supported
            } else if (type == "ARRAYBUFFER") {
                reader.readAsArrayBuffer(file);
            } else {
            }
        }
    }
}-*/;
dac2009
  • 3,521
  • 1
  • 22
  • 22