useLongPress

Enable precise control of long-press interactions for both touch and mouse events with useLongPress.

Inspired on:

Visual example

site title
domain.tld
Press and hold me

Is pressed done: no

Code base

type LongPressEvent = TouchEvent | MouseEvent;

type LongPressOptions = {
	threshold?: number;
	onStart?: (event: LongPressEvent) => void;
	onFinish?: (event: LongPressEvent) => void;
	onCancel?: (event: LongPressEvent) => void;
};

export function useLongPress(
	node: HTMLElement,
	callback: (event: LongPressEvent) => void,
	options: LongPressOptions = {}
) {
	const { threshold = 400, onStart, onFinish, onCancel } = options;

	let timerId: ReturnType<typeof setTimeout> | null = null;
	let isLongPress = false;
	let isPressed = false;

	function isTouch(event: any): event is TouchEvent {
		return typeof TouchEvent !== 'undefined'
			? event instanceof TouchEvent
			: 'touches' in event;
	}

	function isMouse(event: any): event is MouseEvent {
		return typeof MouseEvent !== 'undefined'
			? event instanceof MouseEvent
			: 'button' in event;
	}

	function start(event: LongPressEvent) {
		if (!isTouch(event) && !isMouse(event)) return;

		onStart?.(event);
		isPressed = true;

		timerId = setTimeout(() => {
			callback(event);
			isLongPress = true;
		}, threshold);
	}

	function cancel(event: LongPressEvent) {
		if (!isTouch(event) && !isMouse(event)) return;

		if (isLongPress) {
			onFinish?.(event);
		} else if (isPressed) {
			onCancel?.(event);
		}

		isLongPress = false;
		isPressed = false;

		if (timerId !== null) {
			clearTimeout(timerId);
			timerId = null;
		}
	}

	node.addEventListener('mousedown', start);
	node.addEventListener('mouseup', cancel);
	node.addEventListener('mouseleave', cancel);
	node.addEventListener('touchstart', start);
	node.addEventListener('touchend', cancel);

	return {
		destroy() {
			node.removeEventListener('mousedown', start);
			node.removeEventListener('mouseup', cancel);
			node.removeEventListener('mouseleave', cancel);
			node.removeEventListener('touchstart', start);
			node.removeEventListener('touchend', cancel);
		}
	};
}

Code example

<!-- javascript -->
<script lang="ts">
	import { writable } from 'svelte/store';

	import { useLongPress } from "@dimaslz/svelteuse";

	const longPressDone = writable<boolean>(false);

	function handleLongPress(event) {
		console.log('Long press fired', event);
		longPressDone.update(() => true);
	}
</script>

<!-- html -->
<div>
	<div
		use:useLongPress={handleLongPress}
		style="padding: 2rem; border: 1px solid;"
		class={$longPressDone ? 'bg-green-900': ''}
	>
		Press and hold me
	</div>
	<p>Is pressed done: {$longPressDone ? 'yes' : 'no'}</p>
</div>