10

I'm trying to implement a file dropper on a <div> as a Svelte component. I've tried every combination of preventDefault but the browser still loads the dropped file instead of passing it to the component.

<script>
    function handleDrop(event) {
        event.preventDefault();
        console.log("onDrop");
    }

    function handleDragover(event) {
        console.log("dragOver");
    }
</script>

<style>
    .dropzone {
        display: block;
        width: 100vw;
        height: 300px;
        background-color: #555;
    }
</style>

<div class="dropzone" on:drop|preventDefault={handleDrop} 
    on:dragover|once|preventDefault={handleDragover}></div>

I've tried with and without event.preventDefault(); in handler functions. Also tried with on:dragenter event and different combinations of modifiers, i.e. with stopPropagation. The browser still opens the dropped file. What am I doing wrong? Thanks!

(UPDATE) FIX: Okay, the culprit was the |once modifier. Once removed from the on:dragover in <div> everything works great, except that dragover event fires continuously while dragging across the div. event.preventDefault(); inside handler functions is not needed as the |preventDefault modifier works correctly. Here is the code (omitting <style> for brevity):

<script>
    function handleDrop(event) {
        console.log("onDrop");
    }
    function handleDragover(event) {
        console.log("onDragOver");
    }
</script>

<div class="dropzone" on:drop|preventDefault={handleDrop} 
    on:dragover|preventDefault={handleDragover}></div>

Not submitting this as an answer yet, because I would like to find out why I can't use |once modifier for dragover event, which would be useful for my app. Thanks!

CC BY-SA 4.0

2 Answers 2

18

Problem:

This is a common gotcha rooted in HTML drag-and-drop (not Svelte's fault), where the last dragover event must be canceled in order to cancel drop. Looking at Svelte's once directive, it's just a closure that runs your handler one time. However, dragover will fire multiple times before being dropped, so the immediately preceding dragover is not prevented.

Solution:

Just include the directive without a handler:

<div 
   on:dragover|preventDefault
   on:drop|preventDefault={handler} 
>
CC BY-SA 4.0
0
<style>
    .dropzone {
        display: block;
        width: 100vw;
        height: 300px;
        background-color: #555;
    }
</style>

<div class="dropzone" on:drop={event => handleDrop(event)}
    on:dragover={handleDragover}>
</div>

<script>
    export function handleDragover (ev) {
        ev.preventDefault();
        console.log("dragOver");
    }

    export function handleDrop (ev) {
        ev.preventDefault();
        console.log("onDrop");
    }
</script>

Look here: https://svelte.dev/repl/3721cbc9490a4c51b07068944a36a40d?version=3.4.2

https://v2.svelte.dev/repl?version=2.9.10&gist=8a9b145a738530b20d0c3ba138512289

CC BY-SA 4.0
2
  • This still doesn't work on my end. Moreover, export function now creates warnings in the console: <FileChooser> was created without expected prop 'handleDrop'
    – Eximorp
    Commented Jun 22, 2019 at 12:53
  • According to this tutorial I should be able to just add on:drop={handleDrop} and in the function I would receive the event object anyway: function handleDrop(event). And that works with the dragover event: while dragging across the div I can see the event object in the console if I do console.log(event).
    – Eximorp
    Commented Jun 22, 2019 at 12:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.