Detecting Fake Mouse Events with InputDeviceCapabilities

Finger touching a computer mouse with holographic data interface

Handling cross-device input often leads to subtle bugs when supporting both touch and pointer interactions. When a user taps a touchscreen, mobile and desktop browsers fire touch events first (touchstart, touchend), followed shortly by synthesized compatibility mouse events (mousedown, mouseup, click). If an interface attaches listeners to both touch and mouse events to maintain responsive UI feedback, this browser behavior frequently causes double firing, glitchy toggle states, or duplicated network calls.

Traditionally, developers resort to fragile workarounds such as setting short-lived timestamp flags, calling preventDefault() on touch handlers, or maintaining manual state machines to suppress subsequent mouse events. The InputDeviceCapabilities API addresses this root problem at the browser level by exposing hardware capabilities via event.sourceCapabilities. Rather than relying on guesswork or UA sniffing, the API allows code to inspect firesTouchEvents directly on incoming mouse events, enabling clean suppression of synthesized fallbacks while maintaining solid backwards-compatible behavior across non-supporting browsers.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Input Device Capabilities API Demo</title>
    <link href="style.css" rel="stylesheet" />
    <script src="inputDeviceCapabilities_API.js" defer></script>
</head>
    
<body>
    <!-- HTML: Minimal interactive button and real-time event log container -->
    <div class="demo-wrapper">
        <button id="actionBtn" type="button" class="btn">Trigger Action</button>
        <ul id="eventLog" class="log-list" aria-live="polite"></ul>
    </div>
</body>

</html>
/* CSS: Clean, baseline styles for button interaction and log readout */
.demo-wrapper {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 480px;
    font-family: system-ui, -apple-system, sans-serif;
}

.btn {
    padding: 0.75rem 1.5rem;
    font-size: 1rem;
    font-weight: 600;
    color: #ffffff;
    background-color: #1e293b;
    border: 1px solid #0f172a;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 150ms ease, transform 100ms ease;
}

.btn:active {
    transform: translateY(1px);
    background-color: #0f172a;
}

.log-list {
    margin: 0;
    padding: 0.75rem 1rem;
    background-color: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    list-style: none;
    max-height: 180px;
    overflow-y: auto;
    font-family: monospace;
    font-size: 0.875rem;
}

.log-list li {
    padding: 0.25rem 0;
    border-bottom: 1px solid #edf2f7;
}

.log-list li:last-child {
    border-bottom: none;
}
// FULLY COMMENTED CODE DEMO
// Include clear inline comments explaining WHY key lines exist
// Include error handling or feature detection where appropriate

const actionBtn = document.querySelector('#actionBtn');
const eventLog = document.querySelector('#eventLog');

// Helper to write diagnostic feedback to the log panel
const logMessage = (text) => {
	const item = document.createElement('li');
	item.textContent = `[${new Date().toLocaleTimeString()}] ${text}`;
	eventLog.prepend(item);
};

// Dedicated handler for the primary business logic
const executeAction = (sourceType) => {
	logMessage(`Action executed via: ${sourceType}`);
};

// Touch input: Process immediately on touchstart to eliminate tap latency
actionBtn.addEventListener(
	'touchstart',
	(event) => {
		executeAction('Direct Touch');
	},
	{ passive: true }
);

// Mouse input: Distinguish genuine hardware mouse clicks from synthesized tap events
actionBtn.addEventListener('mousedown', (event) => {
	// Feature detection: Check if the browser supports UIEvent.sourceCapabilities
	if (event.sourceCapabilities) {
		// If the device that fired this mousedown generates touch events,
		// this event is an emulated mouse event triggered by a tap.
		if (event.sourceCapabilities.firesTouchEvents) {
			logMessage('Ignored synthesized mousedown (touch device detected)');
			return;
		}
	}

	// Legitimate mouse click from a physical pointer device
	executeAction('Physical Mouse');
});
more posts: