To mark something as a drop zone or as a place where you can finish a drag event
so to say, you need to add an event listener and prevent the default
and we do this on our project list here because when we drag an element here, I want to be able to drop
it here into this list.
So we need to go to that list and listen to these events and prevent the default. For that here in the
project list class which is in the end responsible for managing our lists,
I'll add a connect droppable method and trigger that from inside the constructor, so that when we create
a new project list, we in the end set up this droppable event listening here. For that we need access
to the list and we can get access here with this selector here or almost this selector, remove the li
here and instead get access to the unordered list, the ul tag and this gives us access to the list element
itself.
So not to the section with our items but only to a list in there because that is where ultimately the
items should be added.
So now on that list here, on that unordered list in our project list here for the two different types
we have, we can add an event listener and I'll add two event listeners,
one for drag enter where I need the event object and then we do something and one for drag over.
Now again, the difference is when they are triggered regarding nested child elements. You definitely need
the drag over listener and in there, you need the call event prevent default, for drag enter, that is optional.
Nonetheless I'll do both
and now we can just call event prevent default here,
just like this and do it in both event listeners and with that, we would be able to drop our element here
and the drop event would be triggered. If we don't do this, then we can still drop the element there but
no drop event will be triggered.
Now sometimes, you might not want to always just prevent a default here if something is dragged over
it
but maybe you also want to check the type let's say. You can do this with event data transfer and there,
you could check types which is an array that exists in there and have a look at the first element and
here I want to check if that is text/plain and if it is, then I want to prevent the default,
otherwise I won't
and that means that if we drag anything else above this, which is not possible on this page because the
only thing marked as draggable is the list item which will set that data type when we start dragging
but if you had more drag and drop operations on your page, where you for example can also drag some links
or anything like that, well then you might want to add such an extra check to make sure you're only accepting
the right kind of data here to be dragged into. What you can't access here as a side note is the concrete
data that is attached, so you can't access the data that's attached here,
only the type of the data.
So we unfortunately can't read
the ID for example in here, that is not possible,
we'll have to do with the types. We will be able to then get that data in the drop event though and that
is the place where we ultimately update our UI. Before we do that
however, I also want to adjust my styling a little bit, to give the user some visual feedback that you
can drop something here because right now we could drop an item there but we don't really see that it's
possible. Now to give the users some feedback,
I'll go to drag enter here, so when we first enter this unordered list so to say and in there, on the
list, to be precise on the parent element of the list which is the section, I'll add a class with class
list add which should be droppable.
Now this class doesn't exist yet, we have to add it, for this we can go to the app.css file and
there, anywhere where you want, maybe below that card here, you can add a droppable class, written exactly
like in a Javascript file and assign a background color of, well I picked #ffeoec, a slight rose color.
So now this will be added as a background color to the item we dragged our item over
and now if we reload, I get an error, line 155, yes here in connect droppable, we should use this
type here of course because it's in a property, it's not a variable that would be available here
and I also see that here, we should of course change this to just query selector, not query selector
all because I'm only selecting one list here,
the first and only list in here.
So let's save that
and now after reloading, this works and now if we drag this, you see, this now, the background gets marked
and also
if I drag it down there.
Right now this never gets unmarked but that's something we'll add
but at least it works,
so we successfully add our styles which also proves that this event triggers, otherwise that wouldn't
happen. To be precise,
I might want to update the style in this if check because that is the only condition under which we should
be able to drop something there.
So let me reload after changing this and it still works,
which means our if check works.
So that's great,
this is working,
now let's make sure we also change that style again
once an item leaves this box because right now, we change the background once but then we keep the style
which of course is not what I want to do here. To make sure we update this style once we leave the list
with our element,
Ww can add another event listener to the list and that's the drag leave event listener and this will
trigger whenever we leave the list with our element.
Now here we got some tricky behavior though as you will see
but first of all, let's just start with the implementation you would probably expect,
we simply remove the droppable class here whenever this triggers
because that means we left the list with our item, right?
So now if we implement it like this however, we'll see that this doesn't work correctly.
If I reload, it gets marked but you see I'm still in the list and nonetheless
this got removed.
If I re-enter it, briefly is there but basically as soon as my cursor moves over some child item, this
style gets removed,
same for the bottom list,
here it is,
as soon as the cursor is above the other list item, the background is not marked anymore.
It should still be marked though because I'm still in the droppable area,
my cursor just is above a child item and the issue is that the drag leave event also fires when we
basically leave the list here because we enter a child item of the list.
Now to work around that issue, I want to add an if check here and only remove that class if the leave
event triggered because we really left into a non-child item.
Now for that, we can use event related target which I mentioned earlier in this module,
it also exists for drag leave and it will point at the element we moved to.
So when we leave the unordered list here and the cursor is now somewhere else, the somewhere else element
will be the related target.
Now we can check if that related target is inside of the list, in which case I don't want to remove the
style or not
and for that we can use closest again.
We can use closest to select the closest unordered list with this selector up here,
so with the exact same selector and hence we need back ticks here, we can check if the related target basically
has an ancestor which is this list
and if it has such an ancestor, we know we're still in the list.
If we don't find such an ancestor for the related target, then we know we're outside of the list.
So here I want to check if that item I'm selecting here with that,
if that is not equal to the list here,
so to this list because if it's not equal, then we're not inside of the list
and in this case, I want to remove droppable, otherwise we'll not do anything because otherwise we're
still in the list with the item.
Now with that if we reload, we should have a behavior where we can drag this and it stays here, the background
stays, only for leave,
it gets removed the same for the bottom here as well,
so now we can really move that item and now drop it anywhere.
Now we just have to make sure that when we drop it, we also move it.