DnD API - Drop Effects
Learning outcomes:
- What are drop effects
- The
dataTransfer.dropEffect
property - The
dataTransfer.effectAllowed
property - Practical-level examples of using these
Introduction
In the last chapter, we covered what are 'dropzones' in the DnD API, in precise detail.
Now, in this chapter, our aim is to cover the idea of drop effects in the API. In particular, we'll see what's the main purpose behind using them, how to use them, how not to use them and much more on this road.
So wasting any more time, let's get to work!
What are drop effects?
Did you even notice the mouse pointer when you drag a draggable item to a dropzone.
By default, when we drag an item and hover it over any element, the browser shows us a given pointer to indicate what would happen when the item is dropped at this instant.
For some draggable items, it's limited to only one pointer, but for some it can be many. However, any element could have one of at most three different kinds of mouse pointers β not more than that.
The simplest example to demonstrate this would be to have a single draggable <div>
, as shown below.
<div draggable="true">Drag and Drop</div>
Try dragging this <div>
.
As soon as you do so, you'll notice that a π icon is shown along with the <div>
as it's dragged. This is the browser's way to tell you that the <body>
element (or any other element in this case) couldn't accept this draggable element's drop.
When π is displayed, it means that the drop
event won't fire at the end of the drag operation.
And similarly, if π isn't displayed, then this would simply mean one thing β drop
would definitely get dispatched.
There are pointers other than BLOCK as well. Each serves to indicate that a certain operation would occur on the dragged element at the end of the drag operation.
In the DnD API, these 'operations' are called drop effects.
Altogether, there are four possible drop effects:
- None β the drop would have no effect.
- Move β the drop would have the effect of moving the dropped item from its original location, usually into the dropzone.
- Copy β the drop would have the effect of copying the dropped item and showing it inside the dropzone.
- Link β the drop would have the effect of creating some relationship between the dragged element and the dropzone.
What does the word 'drop effect' mean?
The term 'drop effect' merely relates to the item displayed to the user when performing a drag operation.
It doesn't have to do anything with animation/transition effectson the drop, as the term might suggest confuse some. The term simply means what effect would the drop have.
Will it move the dropped item, copy it, link with it, or have no effect at all?
Hopefully, the term 'drop effect' would be understood by now.
For elements made draggable and droppable manually, the pointer shown is usually for the one for copy. However, one extremely crucial thing to keep in mind is that the icon merely gives a visual clue as to what might happen if we were to drop the dragged item.
It doesn't do anything itself!
When you first come across drop effects in the DnD API, you might be tempted to think that they could configure the way a drop is made.
For instance, if the drop effect is move, you might think that dropping the dragged item would result in the item being literally copied from its original position into the dropzone.
This is NOT always the case. It might be sometimes β but not always!
Remember that drop effects are just a visual clue to the user as to what would happen on a drop effect β they're nothing more than this.
What this means is that, for instance, if you're shown a pointer for the effect 'move' or 'copy' while dragging an element over a dropzone, don't get tricked into thinking that dropping the element would actually move or copy it into the dropzone, respectively.
Nothing would happen by displaying a specific icon more than the fact that it would give a visual hint to the user for the end operation.
That's it!
To actually accomplish any specific effect for a custom DnD implementation, we have to write the respective code ourselves.
But before that, let's see how to customize the icon shown to the user as an item is dragged over a dropzone.
The dropEffect
property
The dataTransfer
object of the DragEvent
interface holds numerous properties and methods essentially containing information related to the drag.
One of these properties, which of particular interest to us right now, is dropEffect
.
dropEffect
configures a dropzone by specifying what effect would be made as soon as an item is dragged-and-dropped over it.It can have one of the following four values:
'none'
β no effect. Thedrop
event won't fire on the dropzone. The corresponding icon is shown.'move'
β the dragged item would be moved. The corresponding icon is shown.'copy'
β the dragged item would be copied. The corresponding icon is shown.'link'
β the dragged item would be linked. The corresponding icon is shown.
Assignment of any other value is ignored β in such a case, the property remains equal to 'none'
.
Let's see a quick example...
Consider the code below:
<div draggable="true">Drag and Drop</div>
<section></section>
As always, this is the HTML for our drag-and-drop implementation. One element is a draggable (you know which one) and the other is a droppable.
Now since we're just interested in showing a different pointer as <div>
is dragged into <section>
and not anything fancy, we won't be setting up handlers for dragstart
or drop
.
We'll simply use a single dragover
handler (on <section>
) and set dataTransfer.dropEffect
to 'move'
. This would make the respective changes to the pointer as soon as a drag is made over the dropzone:
var sectionEle = document.querySelector('section');
sectionEle.ondragover = function(e) {
e.dataTransfer.dropEffect = 'move';
e.preventDefault(); // do you remember this?
}
Simple, wasn't it?
Let's see the pointer for all drop effects in one single example.
One strange thing in regards to dropEffect
is that by default for a custom dropzone, the property is equal to 'none'
. This should mean that the dropzone accepts nothing. However, despite of dropEffect
being 'none'
, the dropzone accepts everything!
This is definitely some kind of buggy implementation, and unfortunately, we can't do anything about it.
If we set dropEffect
to 'none'
explicitly, only then would the dropzone not accept any drops and behave as desired.
Moving on, keep in mind that dropEffect
works only for dropzones. That is, setting it is sensible only on the dragenter
, dragover
, dragleave
and drop
events β all of which fire on a dropzone.
If you configure dropEffect
on the dragstart
event, it will be useless since the browser would anyways replace it with the default drop effect on dispatching dragover
.
dropEffect
is reset to 'none'
inside the drop
handler. This is actually a bug in these browsers.Let's see a couple of examples of dropEffect
before considering a full-blown example where we use the property to customize the action performed when a draggable element is dropped in one of two dropzones, depending on the dropzone.
Remember that the default feedback icon shown in a browser when a draggable item is brought over a dropzone is for the 'copy' effect, yet dropEffect
returns 'none'
when retrieved.
This is once again a conflicing thing, but web devs can't do anything for it!
Uptil now, we've just seen how the dropEffect
property works. Let's now see how could we use it for a practical purpose.
Consider the HTML markup below:
<div draggable="true">Drag and Drop</div>
<p>Move</p>
<section id="d1"></section>
<p>Copy</p>
<section id="d2"></section>
We have a single draggable <div>
and two <section>
elements, both of which would act as dropzones. #d1
is meant to move the <div>
from its original location into itself while #d2
is meant to copy it and then place it in itself.
Let's set up the logic to accomplish this. First, for the draggable <div>
:
var draggedEle;
var divEle = document.querySelector('div');
divEle.ondragstart = function(e) { draggedEle = e.target; }
divEle.ondragend = function(e) { draggedEle = null; }
As soon as the <div>
is dragged, its reference is stored inside the global variable draggedEle
. This is reset back to null
when the drag operation ends with the dragend
event.
Now over to configuring the first dropzone which is meant to move the dragged <div>
:
var d1Ele = document.querySelector('#d1');
d1Ele.ondragover = function(e) {
e.dataTransfer.dropEffect = 'move';
e.preventDefault();
}
d1Ele.ondrop = function(e) {
if (draggedEle) {
this.appendChild(draggedEle);
}
}
And after this, over to the second dropzone which is meant to copy the dragged <div>
:
var d2Ele = document.querySelector('#d2');
d2Ele.ondragover = function(e) {
e.dataTransfer.dropEffect = 'copy';
e.preventDefault();
}
d2Ele.ondrop = function(e) {
if (draggedEle) {
var node = draggedEle.cloneNode();
this.appendChild(node);
}
}
Altogether, this is the following code:
var draggedEle;
var divEle = document.querySelector('div');
var d1Ele = document.querySelector('#d2');
var d2Ele = document.querySelector('#d2');
// configuring <div>
divEle.ondragstart = function(e) { /* ... */ }
divEle.ondragend = function(e) { /* ... */ }
// configuring the first dropzone
d1Ele.ondragover = function(e) { /* ... */ }
d1Ele.ondrop = function(e) { /* ... */ }
// configuring the second dropzone
d2Ele.ondragover = function(e) { /* ... */ }
d2Ele.ondrop = function(e) { /* ... */ }
The effectAllowed
property
Another property on dataTransfer
related to dropEffect
and the icons displayed while an element is dragged is effectAllowed
.
It is used to configure a draggable element, in contrast to dropEffect
which is used to configure a dropzone.
effectAllowed
property specifies what drop effects are allowed to be made on the draggable element.That is, 'can the element be moved, copied, linked, or both moved and copied, or moved and linked, or copied and linked, or all of them.'
The acceptable values are:
'all'
β all effects are allowed.'copy'
β only the 'copy' effect is allowed.'link'
β only the 'link' effect is allowed.'move'
β only the 'move' effect is allowed.'none'
β no effect is allowed. The element can't be dropped over a dropzone.'copyLink'
β both the 'copy' and 'link' effects are allowed.'copyMove'
β both the 'copy' and 'move' effects are allowed.'linkMove'
β both the 'link' and 'move' effects are allowed.
effetAllowed
, on Internet Explorer the property is converted to all lowercase characters on assignment. So, if you assign 'linkMove'
to effectAllowed
, it would become 'linkmove'
when you retrieve it later on.Setting effectAllowed
inside the handler for any event other than dragstart
has no effect at all β the assignment is simply ignored.
This is because effectAllowed
is meant to configure only draggable elements β not dropzones. Hence, setting it on events fired on a dropzone is useless since the property is not meant to configure dropzones. The property meant to configure dropzones, as we saw above, is dropEffect
.
Consider the code below:
<div id="d1" draggable="true">Move</div>
<div id="d2" draggable="true">Copy</div>
<p>Dropzone:</p>
<section></section>
We have two <div>
s, one that would be moved and that would be copied as soon as dragged-and-dropped into the <section>
dropzone.
Let's fetch and configure both these draggable <div>
s first:
var draggedEle;
var d1Ele = document.querySelector('#d1');
var d2Ele = document.querySelector('#d2');
d1Ele.ondragstart = function(e) {
e.dataTransfer.effectAllowed = 'move';
draggedEle = this;
}
d2Ele.ondragstart = function(e) {
e.dataTransfer.effectAllowed = 'copy';
draggedEle = this;
}
Now over to configuring the <section>
element:
var sectionEle = document.querySelector('section');
sectionEle.ondragover = function(e) {
e.preventDefault();
}
sectionEle.ondrop = function(e) {
if (draggedEle) {
if (e.dataTransfer.effectAllowed === 'move') {
this.appendChild(draggedEle);
}
else if (e.dataTransfer.effectAllowed === 'copy') {
this.innerHTML += draggedEle.outerHTML;
}
}
}
The most interesting thing over here is the ondrop
handler. In there, we first check whether draggedEle
has a truthy value i.e. does it point to an element node. If it does, we further lay out two checks, this time on dataTransfer.effectAllowed
.
If the property is equal to 'move'
, we move the element, or otherwise if it is equal to 'copy'
, we copy it. The logic to move or copy the draggable <div>
is the same as in the last example we saw above.
This gives us another interesting DnD implementation to try out:
Wasn't this simple, as well?
Moving on...
With this, we've successfully completed the tiring and extensive unit on the DnD API. Uh, it sure was a long unit!
We covered a lot of stuff starting with the very basics of setting up a custom drag-and-drop functionality on our webpages, what are 'draggable' elements, when do the events dragstart
and dragend
(and even drag
) fire and how to use them for practical-level purposes and so on.
We also saw what are 'droppable' elements, how to make any given element droppable, when and how do the events dragenter
, dragover
, dragleave
and drop
fire and how to use each of these for practical-level purposes.
Let's also not forget about drag data. In this unit, we also learnt what is the drag data store, how to use it to store data for a drag operation that could be ended anywhere on the OS, not just the active webpage, how to retrieve data from it, and so on and so forth.
To boil it all down, by this point we are damn ready to code any sort of DnD functionality given to us, in minutes. We are well-versed with every single concept of the DnD API and therefore it should not be any difficult to code anything related to DnD.
It's time to test your skills and understanding on DnD.
Are you ready?
Let's take the JavaScript Drag and Drop API β Quiz.
Spread the word
Think that the content was awesome? Share it with your friends!
Join the community
Can't understand something related to the content? Get help from the community.