Movable element - simple and robust. A wrapper web component that can enable customizable element move operations and expose pointer state data.
Since this is a Lit 3 web component, this will work inside any SPA framework. React integration docs. Framework-agnostic web components are the future!
npm i lit-movable
<script type="module">
import {LitMovable} from 'lit-movable';
</script>
<lit-movable>
<div style="background:lightsteelblue">I am movable</div>
</lit-movable>
- posTop: Number - Represents the offsetTop/Left value (reflected). When set, will set the initial style.top value. Updates with move events
- posTop: Number - Represents the offsetLeft value (reflected). When set, will set the initial style.top value. Updates with move events
- targetSelector: String - A selector to select the element that will move. Defaults to the lit-movable (this) element, but useful when for example you want to allow a modal header to respond to pointer events but you want the entire modal to move.
- boundsX: String: boundsX="min,max" Defaults to -Infinity,Infinity. Set to restrict movement along the x axis.
- boundsY: String: boundsY="min,max" Defaults to -Infinity,Infinity. Set to restrict movement along the y axis.
- vertical: String: vertical="min,max" - Will constrain horizontal (x) movement completely and allow vertical (y) movement between the specified values.
- horizontal: String: horizontal="min,max" - Will constrain vertical (y) movement completely and allow horizontal (x) movement between the specified values.
- grid: Number - Snaps movement to nearest grid position (defaults to 1). Initial element position represents the 0,0 position. Movement snapped to the provided value increment
- shiftKey Bool - When enabled, holding the shift key will coerce movement to perpendicular coordinates only.
- disabled: Bool - Disables movement behavior.
- eventsOnly: Bool - (advanced) Only fires movement events, but will not move the element.
Lit-Movable exposes events as either callback properties or the built in custom events.
- onmovestart: called immediately after the pointerdown event on the element
- onmove: called continuously while moving
- onmoveend: called after the pointerup event on the element
- movestart: fires immediately after the pointerdown event on the element
- move: fires continuously while moving
- moveend: fires after the pointerup event on the element
<lit-movable id="myMovable"></lit-movable>
<script>
const movableEl = document.getElementById("myMovable");
/* Bind As property */
movableEl.onmove = (moveState)=>{
const {coords, clickOffset, mouseCoord, moveDist, startCoord, totalDist} = moveState;
//all coordinates expose x,y,top,left
console.log(coords);//Current element pos
console.log(clickOffset);// the position of the pointer relative to the top/left of the element
console.log(mouseCoord); // current mouse pos on page - equivalent of pageX/pageY on a mouse event
console.log(moveDist); //distance moved since previous move operation
console.log(startCoord); //position of element on pointerdown
console.log(totalDist);//distance moved from pointerdown to pointerup
}
/* Custom Event */
movableEl.addEventListener('move', (event) => {
const moveState = event.detail;
console.log({moveState});//same state object as above
});
</script>
Parent moves when title is dragged. Used targetSelector attribute.
<div style="height:200px;width:200px;border:solid 1px blue;" id="dialog">
<lit-movable targetSelector="#dialog">
<div style="background:lightsteelblue;width:100%">I am a draggable title</div>
</lit-movable>
I am not directly grabbable, but I will move if you grab my title.
</div>
Constrain vertical movement. Allow -50 -> 250 horizontal movement. Here are two ways to accomplish the identical behavior.
<!-- Set horizontal movement only -->
<lit-movable horizontal="-50,250">
<div style="background:lightsteelblue">Move me horizontally</div>
</lit-movable>
<!-- OR -->
<!-- Explicit x/y boundaries. Null string constrains an axis -->
<lit-movable boundsX="-50,250" boundsY="null">
<div style="background:lightsteelblue">Move me horizontally</div>
</lit-movable>
Two identical ways to constrain horizontal movement, but enable broad vertical motion.
<!-- Set vertical movement only -->
<lit-movable vertical="-999,9999">
<div style="background:lightsteelblue">Move me vertically</div>
</lit-movable>
<!-- Alternate explicit bounds (x,y) equivalent. Null = no movement enabled -->
<lit-movable boundsY="-999,9999" boundsX="null">
<div style="background:lightsteelblue">Move me horizontally</div>
</lit-movable>
Snaps to a 50px grid with shift key behavior.
<lit-movable grid="50" shiftBehavior="true">
<div style="background:lightsteelblue">my grid is 50 <br>(try holding shift while dragging)</div>
</lit-movable>
Start in middle of a constrained box.
<div style="height:200px;width:200px;border:solid 1px green;position:relative">
<lit-movable posTop="100" posLeft="100" boundsX="-100,100" boundsY="-100,100">
<div style="background:lightsteelblue;width:30px;margin-left:-15px;height:18px;margin-top:-9px">
box
</div>
</lit-movable>
</div>
Uses vite. Will run on node 16+ but will complain about compatibility if you are stuck on node 16 like me. Ignore this. It's fine.
git clone https://github.com/thewebkid/lit-movable.git
cd ./lit-movable
npm i
npm dev