I asked this question: PDF.JS overlay can not be made draggable.
I noticed that PDF.js seemed to be blocking the document.onmousemove
and document.onmouseup
events from being fired.
I thought the solution would be to use the element's onmousemove and onmouseup however that comes with its problems.
For example, if you move your mouse too fast the element's onmousemove
and onmouseup
events will stop firing. Same things goes if you try and drag it out of bounds, the element's events will stop firing. You can try for yourself in the snippet below.
// Modified from https://www.w3schools.com/howto/howto_js_draggable.asp
function dragElement(elmnt, _bounds) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
elmnt.onmousedown = dragMouseDown;
let bounds = [..._bounds];
bounds[2] -= +elmnt.style.width.slice(0, -2);
bounds[3] -= +elmnt.style.height.slice(0, -2);
function dragMouseDown(e)
{
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.onmouseup = closeDragElement; // Originally document.onmouseup
elmnt.onmousemove = elementDrag; // Originally document.onmousemove
}
function elementDrag(e)
{
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
let yC = elmnt.offsetTop - pos2, xC = elmnt.offsetLeft - pos1;
if (xC < bounds[0]) xC = bounds[0];
else if (xC > bounds[2]) xC = bounds[2];
if (yC < bounds[1]) yC = bounds[1];
else if (yC > bounds[3]) yC = bounds[3];
elmnt.style.top = yC + "px";
elmnt.style.left = xC + "px";
}
function closeDragElement()
{
elmnt.onmouseup = null; // Originally document.onmouseup
elmnt.onmousemove = null; // Originally document.onmousemove
}
}
dragElement(toDrag, [0, 0, 200, 200]);
<div style="
position: absolute;
left: 0;
top: 0;
width: 200px;
height: 200px;
border: 1px solid black
" onmousedown="event.preventDefault()" onmouseup="event.preventDefault()">
<h1 style="text-align:center">My PDF</h1>
<div style="
position: absolute;
left: 150px;
top: 150px;
width: 25px;
height: 25px;
border: 1px solid black;
">
</div>
<div id=toDrag style="
cursor: move;
position: absolute;
left: 10px;
top: 25px;
width: 25px;
height: 25px;
border: 1px solid black;
background-color: #cac;
border-radius: 25px
">
</div>
</div>
My question is there anyway of detecting mousemove
and mouseup
even if event.preventDefault()
has been fired.
My best guess would be something like:
document.querySelectorAll('*').forEach(e => e.addEventListener('mousemove', elementDrag);
document.querySelectorAll('*').forEach(e => e.addEventListener('mouseup', closeDragElement);
However, I worry that this could impact performance especially on mousemove
.
DOM event propagation works in three phases: the capture phase, the target phase and the bubble phase. Roughly, the event first works its way down to the target, reaches the target and then works its way back up.
EventTarget.addEventListener() has an optional parameter useCapture
(see docs):
useCapture
OptionalA boolean value indicating whether events of this type will be dispatched to the registered
listener
before being dispatched to anyEventTarget
beneath it in the DOM tree. Events that are bubbling upward through the tree will not trigger a listener designated to use capture. Event bubbling and capturing are two ways of propagating events that occur in an element that is nested within another element, when both elements have registered a handle for that event. The event propagation mode determines the order in which elements receive the event. See DOM Level 3 Events and JavaScript Event order for a detailed explanation. If not specified,useCapture
defaults tofalse
.
That being said, you could try something like that to listen event on capture phase, instead of bubble phase:
document.addEventListener('mousemove', elementDrag, true);
document.addEventListener('mouseup', closeDragElement, true);