This page details how we use DOM input events, what we do with them, and how you can build things on top of our usage. Generally you will not need to know this information but it can be helpful if you are also binding your own event handlers to the window or to a drag handle.
⚠️ Note: due to a bug in webkit, particular events such asmousemove
will not correctly setevent.defaultPrevented
totrue
whenevent.preventDefault()
is called. You can follow progress on this issue here.
This page assumes a working knowledge of DOM events. For a good introduction to DOM events see:
Without needing going into all the details below, here are the safest event handlers to build on top of react-beautiful-dnd
:
These can be added on the drag handle, anywhere else higher on the tree or to the window directly.
onClick
: theevent.defaultPrevented
property will be set totrue
if occurred as a part of the drag interaction. This is true even if the drag was not finished with a pre-click action such asmouseup
ortouchend
. See sloppy clicks and click prevention.onKeyDown
: theevent.defaultPrevented
property will be set totrue
if it was used as a part of a drag. If you addonKeyDown
to the drag handle you will need to monkey patch theDragHandleProps
onKeyDown
event handler.
You may need to enchance the logic of your event handlers with information from onDragStart
and onDragEnd
to know about whether a drag is occuring while those events fire.
You are welcome to add other event handlers but you may be more reliant on onDragStart
and onDragEnd
information.
When we use an input event as part of a drag and drop interaction we generally call event.preventDefault()
on the event to opt out of standard browser behaviour for the event. We do not stop the propagation of events that we call event.preventDefault()
on so even though we may use a mousemove
event for dragging we will not block that event from being published (propagating) and received by your event handlers.
- we use:
event.preventDefault()
- we do not use:
event.stopPropagation()
Some event handlers we add on the drag handle itself (see DragHandleProps
) and others we add to the window
in the capture phase. What this means is as long as you are applying your events handlers in the bubbling phase (which is the default for event handlers) then behaviour of events will be as described on this page.
In order to know if we have already used the event for the purpose of drag and drop you need to check the event.defaultPrevented
property.
So let's say you want to add a window click
handler. You could do something like this:
window.addEventListener('click', (event: MouseEvent) => {
// event has already been used for drag and drop
if (event.defaultPrevented) {
return;
}
doMyCoolThing();
});
Some user events have a direct impact on a drag: such as a mousemove
when dragging with a mouse or the up arrow ↑ keydown
event while dragging with a keyboard. These direct events will have event.preventDefault()
called on them to prevent their default browser behaviours. Some events indirectly impact a drag such as a resize
event which cancels a drag. For events that indirectly impact a drag we do not call event.preventDefault()
on them. Generally indirect events that impact are drag are events that cancel a drag such as reize
or orientationchange
events.
preventDefault()
is called onmousedown
😞
This is the only known exception to our rule set. It is unfortunate that it is the first one to appear in this guide!
When the user first performs a mousedown
on a drag handle we are not sure if they are intending to click or drag. Ideally we would not call preventDefault()
on this event as we are not sure if it is a part of a drag. However, we need to call preventDefault()
in order to avoid the item obtaining focus as it has a tabIndex
.
preventDefault()
not called onmousemove
The user needs to move a small threshold before we consider the movement to be a drag. In this period of time we do not call preventDefault()
on any mousemove
events as we are not sure if they are dragging or just performing a sloppy click
preventDefault()
not called on the event that caused the pending drag to end (such asmouseup
andkeydown
). Anykeydown
event that is firered while there is a pending drag will be considered an indirect cancelpreventDefault()
is not called on the subsequentclick
event if there is one
preventDefault()
is called onmousemove
eventspreventDefault()
is called on a fewkeydown
events to prevent their standard browser behaviourpreventDefault()
is not called onkeyup
events even if thekeydown
was prevented
preventDefault()
is called on amouseup
if it ended the dragpreventDefault()
is called on a escape esckeydown
if it ended the drag as it directly ended the dragpreventDefault()
is called on the nextclick
event regardless of how the drag ended. See sloppy clicks and click preventionpreventDefault()
is not called on other events such asresize
that indirectly ended a dragpreventDefault()
is not called onkeyup
events even if they caused the drag to end
The logic for touch dragging works in a similar way to mouse dragging
preventDefault()
is not called ontouchstart
.
When a user presses their finger (or other input) on a Draggable
we are not sure if they where intending to tap, force press, scroll the container or drag. Because we do not know what the user is trying to do yet we do not call preventDefault()
on the event.
preventDefault()
is not called on any events
A user can start a drag by holding their finger 👇 on an element for a small period of time 🕑 (long press). If the user moves during this time with touchmove
then we do not call preventDefault()
on the event.
It is possible to cancel a touch drag with over events such as an orientationchange
or a touchcancel
. We do not call preventDefault
on these events.
preventDefault()
is called ontouchmove
events
✌️
preventDefault()
is called ontouchend
preventDefault()
is called ontouchcancel
preventDefault()
is called on an escape esckeydown
as a direct cancel.preventDefault()
is not call on any otherkeydown
as it is an indirect cancel.preventDefault()
is not called on other events such asorientationchange
that can cancel a drag
preventDefault()
is not called ontouchforcechange
if a drag has not started yetpreventDefault()
is not called ontouchforcechange
a drag that has started but no movement has occurred yet. The force press cancels the drag and is an indirect cancel.preventDefault()
is called after ontouchforcechange
a drag has started and atouchmove
has fired. This is defensive as a force presstouchforcechange
should not occur after atouchmove
.
We only use
keydown
events for keyboard dragging sokeyup
events never havepreventDefault()
called on them
preventDefault()
is called onkeydown
Unlike mouse dragging a keyboard drag starts as soon as the user presses the spacebar space. This initial keyboard interaction has event.preventDefault()
called on it.
preventDefault()
is called on akeydown
event if the event is used as part of the drag (such as the up arrow ↑)preventDefault()
is called onkeydown
events were we want to block the stanard browser behaviours (such asenter
for submission)preventDefault()
is not called onkeydown
events that we do not use for the drag
preventDefault()
is called on akeydown
if it is the spacebar space key as it is dropping the itempreventDefault()
is called on akeydown
if it is the escape esc key as it is explicitly cancelling the dragpreventDefault()
is not called on events that indirectly cancel a drag such asresize
ormousedown
.