trackDrag
Sometimes you want to track a mouse-drag or touch-drag operation without messing around.
This is how the resizeable columns in <tosi-table> work.
Just call trackDrag(event, (dx, dy, event) => { ... }) and you'll get updates on corresponding events until
you return true from the event-handler (or, in the case of touch events, the last touch ends).
For mouse events, a "tracker" element is thrown up in front of everything for the event.
<p>
Try dragging the squares…<br>
(You can drag them separately with multi-touch!)
</p>
<div class="draggable" style="top: 20px; left: 40px; background: #f008"></div>
<div class="draggable" style="left: 40%; bottom: 30%; background: #0f08"></div>
<div class="draggable" style="bottom: 30px; right: 10px; background: #00f8"></div>
.preview {
touch-action: none;
}
.draggable {
content: ' ';
position: absolute;
width: 50px;
height: 50px;
cursor: move;
}
.preview p {
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
}
import { trackDrag } from 'tosijs-ui'
function dragItem(event) {
const draggable = event.target
if (draggable.classList.contains('draggable')) {
const x = draggable.offsetLeft
const y = draggable.offsetTop
trackDrag(event, (dx, dy, event) => {
draggable.style.left = (x + dx) + 'px'
draggable.style.top = (y + dy) + 'px'
draggable.style.bottom = 'auto'
draggable.style.right = 'auto'
return event.type === 'mouseup'
})
}
}
preview.addEventListener('mousedown', dragItem )
preview.addEventListener('touchstart', dragItem, { passive: true } )
For touch events, dx and dy are based on tracking event.changedTouches[0] which
is almost certainly what you want.
To handle multi-touch gestures you will need to track the touches yourself.
Do not register your mousedown listener as passive
Note the example above: mousedown is added without options, touchstart with
{ passive: true }. That asymmetry is load-bearing, not an oversight.
trackDrag calls preventDefault() on the mousedown that starts a drag, and a passive
listener makes that a silent no-op — no error, no warning, it simply does nothing. Without it,
Firefox treats the first drag on a freshly-loaded page as a text selection: what you grabbed
stays put, text highlights instead, and it only behaves after you have clicked somewhere to
settle Firefox's selection state. Landing on a page and immediately dragging something is an
entirely ordinary thing to do, so in practice the first attempt fails.
This bit every draggable in this library — <tosi-sizer>, <tosi-float>,
<tosi-editable-rect> and <tosi-table>'s column resize — from v0.5.1 until 1.12.2. Three of
them had passive mousedown listeners, so fixing trackDrag alone changed nothing for them;
<tosi-table>, whose listener was never passive, was fixed by the same change. If you use
trackDrag in your own component and dragging misbehaves on Firefox, this is the first thing
to check.
Nothing is lost by dropping passive on mousedown. Its practical value is silencing the
console warning about handlers that delay scrolling, and that warning only ever applied to
touch and wheel — keep it on touchstart, where trackDrag handles the equivalent on
touchmove.
bringToFront
bringToFront(element: HTMLElement, selector = 'body *') gives the element the highest
z-index of any element matching the selector (which is passed to findHighestZ).
findHighestZ
findHighestZ(selector = 'body *'): number returns the the highest z-index of any element
matching selector.