即使使用 preventDefault 也检测 onmouseup/onmousemove

Detect onmouseup/onmousemove even if preventDefault is used

我问了这个问题:PDF.JS overlay can not be made draggable

我注意到 PDF.js 似乎阻止了 document.onmousemovedocument.onmouseup 事件的触发。

我认为解决方案是使用元素的 onmousemove 和 onmouseup,但是这会带来问题。

例如,如果您将鼠标移动得太快,元素的 onmousemoveonmouseup 事件将停止触发。如果您尝试将其拖出边界,也会发生同样的事情,该元素的事件将停止触发。您可以在下面的代码片段中自己尝试。

// 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>

我的问题是无论如何都要检测 mousemovemouseup,即使 event.preventDefault() 已被解雇。

我最好的猜测是:

document.querySelectorAll('*').forEach(e => e.addEventListener('mousemove', elementDrag);
document.querySelectorAll('*').forEach(e => e.addEventListener('mouseup', closeDragElement);

但是,我担心这会影响性能,尤其是 mousemove

DOM 事件传播分为三个阶段:捕获阶段、目标阶段和冒泡阶段。粗略地说,事件首先向下移动到目标,到达目标然后向上移动。

EventTarget.addEventListener() 有一个可选参数 useCapture(参见 docs):

useCapture Optional

A boolean value indicating whether events of this type will be dispatched to the registered listener before being dispatched to any EventTarget 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 to false.

话虽这么说,您可以尝试类似的方法来监听捕获阶段的事件,而不是冒泡阶段:

document.addEventListener('mousemove', elementDrag, true);
document.addEventListener('mouseup', closeDragElement, true);