VueJS - Drag 'n Drop
Drag and Drop Scripting API has been released along with HTML 5. In this article you will learn how to combine this recent feature with VueJS and create generic and reusable components to provide dragging and dropping feature to your next front-end application.
In this article you're going to learn how to implement a few reusable components in order to add drag and drop capabilities to your next VueJS Project.
The whole sample code available in this article is based on VueJS 3.
It's important to mention that you may find several third-party libraries that implement drag and drop features. That's fine and you will probably save time by using them.
The goal here is just to practice a little bit of VueJS, see how HTML 5 Drag and Drop API works and also create your own reusable and lightweight components without the need of any external dependency.
If you still don't know how to create a VueJS project from scratch, I recommend you to take a look at this article through which I explain how I structure my own VueJS projects from scratch.
Create a new VueJS Project and let's get hands dirty!
We're going to start by creating a simple component that will allow other elements to be dragged into it.
We're going to call it
DroppableItem and it will look like this:
Let's dive deeper into each part of this implementation.
template is very simple. It is made of a unique
span element with a
slot inside it.
We're going to add some event listeners to this very root element, which are:
@dragover: triggered when dragging an element over it;
@dragleave: triggered when dragging an element out of it;
@drop: triggered when dropping an element into it;
Even though it's not a good practice, we're not defining the prop types in this example just to keep it simple.
Notice that we wrap the
onDragOver event within a
handleDragOver method. We do this to implement the
preventDefault() method and make the component capable of having something dragged over it.
We are also making use of a
slot to allow this component to receive HTML content and "assume the form" of any element that is put inside it.
That's pretty much what's needed to create our
Now, let's create the component that will allow us to drag elements around the interface.
This is how it will look like:
Let's dive deeper into this implementation. Starting with the
draggable- This attribute informs the browser that this is a draggable element.
Initially, we need to set the
draggable attribute as
true to enable the Drag and Drop API for the
span element that is around our
slot. It's important to mention that, in this case, even though we're working with VueJS, we have to set the value "true" explicitly, otherwise it won't work as expected.
@dragstart - This is the default HTML event listened by VueJS. It is triggered when the user clicks, holds and drags the element.
Now let's take a look at the component's
We defined a method named
onDragStart that will be called when the user starts to drag the component.
In this method, we pass the
transferData prop value to the
dataTransfer property of the
According to MDN Web Docs:
The DataTransfer object is used to hold the data that is being dragged during a drag and drop operation.
We need to serialize the value before setting it to
This will allow us to retrieve it when the element has been dropped.
So far, so good!
This is all we need to build generic and reusable wrapper components to drag and drop elements around our application.
Now, to make use of them, we need to define the content of their default slots.
Let's suppose we want to create draggable circles that can be dragged into a square area.
Assuming they will be implemented in the
App component, here is how it would look like:
In this example, we can already drag each one of the balls, but nothing happens when we do it.
In order to make this implementation really work, we need to improve the code to make it more dynamic.
We are going to add:
availableBalls- a computed property that will represent the balls available to be dragged. As the user drags a ball into the square, it will no longer be available to be dragged again.
selectedBalls- a reactive variable that will represent all of the balls that were dragged into the droppable square.
isDroppableItemActive- a reactive variable that will represent the state of the droppable square. We will use it to change the background color of the square when an element is being dragged over it.
onDragOver- a method that will be called when a ball is dragged over the square. It will be responsible for setting the
isDroppableItemActivevariable and changing its background color.
onDragLeave- a method that will be called when a ball is dragged out of the square. It will be responsible for resetting the
isDroppableItemActivevariable and its background color.
onDrop- a method that will be called when a ball is dropped into the square. It will reset its background color and update the
Notice that we use the
dataTransfer.getData() of Drag and Drop API to retrieve the data of that item that was dragged.
As it is a serialized value, we need to use
JSON.parse to "unserialize" it and turn it into a valid object.
We are going to use Lodash FP's
differenceBy method just for the sake of simplicity but you can implement your own filtering.
This is how our
App component will look like after the improvements:
And this is the visual result:
You can find a more complete and fully-working example in this repo.
I hope you liked!
Please, share and comment.
Cover image by E-learning Heroes