I am writing a 'drag-n-drop' library and want to create certain events for the user to hook on to. For example, before dropping an element onto another one, I create an event 'beforeDrop' with some data in the details field, and would like the user to be able to prevent the drop depending on the data - this would be done in an event handler created by the user. But if the user doesn't provide such a handler, the drop will never happen - so the library needs a default handler, which it will execute if the client doesn't respond in a sensible timeframe.
I am doing it as detailed below, which works (so far!) but it seems messy (particularly choosing a timeout value) and I am wondering if there is a better way?
In the Draggable
library:
document.addEventListener('beforeDrop', (e) => {
setTimeout(function() {
if (e.defaultPrevented) {
Draggable.log('Draggable: client prevented drop');
Draggable.dropAllowed = false;
}
else {
Draggable.log('Draggable: drop allowed');
Draggable.dropAllowed = true;
}
}, 50);
})
and then, when I want to test for a drop:
this.addEvent('beforeDrop',this.el,droppedOnElement.el);
let clientResponse = new Promise(function(resolve) {
setTimeout(function() {resolve();}, 100);
});
await clientResponse;
if (Draggable.dropAllowed) {
// actually do the drop......
}
addevent()
creates and dispatches a cancelable CustomEvent
:
addEvent(name,from, to) {
const event = new CustomEvent(name, { cancelable: true, detail: {from:from.dataset, to:to.dataset}});
document.dispatchEvent(event);
}
The client's listener is like this:
document.addEventListener('beforeDrop', (e) => {
if (drop(e.detail.from, e.detail.to) === false) {
// stop the item being dropped by the library
e.preventDefault();
}
}, false);
the library needs a default handler, which it will execute if the client doesn't respond in a sensible timeframe
No. The "default handler" is not an event listener itself, and it does not need a delay. It is code that is running after the event has been dispatched, and all the event listeners have been executed. The "sensible timeframe" for the client is "during the event dispatch (handling)".
Notice that the dispatchEvent
method is synchronous. The event listeners are called and return before the dispatchEvent()
call returns. And in fact the method has another benefit: it returns a boolean value whether the default handling should occur or whether it was prevented (essentially the same as !event.defaultPrevented
).
So you should use
const event = new CustomEvent('beforeDrop', {
cancelable: true,
detail: {from: this.el.dataset, to: droppedOnElement.dataset},
});
const dropAllowed = document.dispatchEvent(event);
// ^^^
if (dropAllowed) {
Draggable.log('Draggable: drop allowed');
// actually do the drop...
} else {
Draggable.log('Draggable: client prevented drop');
}