Mouse Events — Complete Guide
In this tutorial, you will learn about Mouse Events. We cover key concepts, practical examples, and best practices to help you master this topic.
Mouse events like click, dblclick, mousedown, mouseup, mousemove, and contextmenu enable JavaScript to respond to pointer interactions on DOM elements.
What You'll Learn
- The difference between click, mousedown, and mouseup
- How to track mouse position with clientX, clientY, pageX, pageY
- How to detect which mouse button was pressed
- How to implement drag-and-drop with mouse events
- How to handle right-click with contextmenu
Why It Matters
Mouse interactions are the foundation of many UI patterns: drag-and-drop, drawing canvases, resizing panels, selecting text, and custom context menus. Understanding mouse events is essential for building interactive applications.
Real-World Use
- A drawing application tracks mousedown and mousemove for freehand drawing
- A file manager uses drag-and-drop for organizing files
- A custom right-click menu replaces the browser default
- A slider component tracks mouse position for value changes
flowchart LR A[Mouse Interaction] --> B[mousedown] A --> C[mousemove] A --> D[mouseup] B --> E[click] A --> F[dblclick] A --> G[contextmenu] B --> H[Start drag] H --> C[Update position] C --> D[End drag]
Mouse Event Types
Different mouse events fire at different moments of the interaction.
const target = document.querySelector('.mouse-demo');
const log = document.querySelector('.event-log');
function logEvent(event) {
const msg = `${event.type} at (${event.clientX}, ${event.clientY})`;
console.log(msg);
log.textContent = msg;
}
target.addEventListener('mousedown', logEvent);
target.addEventListener('mousemove', logEvent);
target.addEventListener('mouseup', logEvent);
target.addEventListener('click', logEvent);
target.addEventListener('dblclick', logEvent);
target.addEventListener('contextmenu', logEvent);
// Sequence for a single click:
// mousedown -> mouseup -> click
// Sequence for a double click:
// mousedown -> mouseup -> click -> mousedown -> mouseup -> click -> dblclick
// Right-click:
// mousedown (button: 2) -> contextmenu -> mouseup (button: 2)
Expected output: Clicking shows mousedown, mouseup, and click in order. Double-clicking shows the sequence twice followed by dblclick. Moving the mouse shows mousemove events.
Mouse Button Detection
The event.button property identifies which button was pressed.
const area = document.querySelector('.interaction-area');
area.addEventListener('mousedown', function(event) {
switch (event.button) {
case 0:
console.log('Left button (primary)');
break;
case 1:
console.log('Middle button (wheel)');
break;
case 2:
console.log('Right button (secondary)');
break;
default:
console.log('Unknown button:', event.button);
}
// event.buttons indicates which buttons are currently pressed
// (bitmask: 1=left, 2=right, 4=middle, 8=back, 16=forward)
console.log('Currently pressed buttons:', event.buttons);
});
// Prevent default context menu for custom handling
area.addEventListener('contextmenu', function(event) {
event.preventDefault();
console.log('Default context menu prevented');
showCustomMenu(event.clientX, event.clientY);
});
Expected output: Left-click reports button 0. Right-click reports button 2 and shows the custom menu. The default browser context menu is suppressed.
Mouse Position
Mouse coordinates can be relative to different reference points.
const container = document.querySelector('.mouse-position');
container.addEventListener('mousemove', function(event) {
// clientX/Y: relative to viewport (scroll position ignored)
// pageX/Y: relative to document (includes scroll)
// screenX/Y: relative to physical screen
// offsetX/Y: relative to the event target
console.log({
clientX: event.clientX,
clientY: event.clientY,
pageX: event.pageX,
pageY: event.pageY,
offsetX: event.offsetX,
offsetY: event.offsetY,
screenX: event.screenX,
screenY: event.screenY
});
// Update a tooltip position
const tooltip = document.querySelector('.position-tooltip');
tooltip.style.left = `${event.clientX + 10}px`;
tooltip.style.top = `${event.clientY + 10}px`;
tooltip.textContent = `${event.offsetX}, ${event.offsetY}`;
});
// Throttle mousemove for performance (fires at high frequency)
let lastMove = 0;
container.addEventListener('mousemove', function(event) {
const now = Date.now();
if (now - lastMove < 16) return; // ~60fps throttle
lastMove = now;
// Update position
});
Expected output: Moving the mouse over the container logs coordinates from all four reference systems. The tooltip follows the cursor showing offset coordinates.
Implementing Drag-and-Drop
Custom drag-and-drop with mouse events gives full control over the interaction.
const draggable = document.querySelector('.draggable-box');
const dropZone = document.querySelector('.drop-zone');
let isDragging = false;
let offsetX, offsetY;
draggable.addEventListener('mousedown', function(event) {
isDragging = true;
// Calculate offset between mouse and element top-left
const rect = this.getBoundingClientRect();
offsetX = event.clientX - rect.left;
offsetY = event.clientY - rect.top;
this.style.position = 'fixed';
this.style.zIndex = 1000;
this.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', function(event) {
if (!isDragging) return;
draggable.style.left = `${event.clientX - offsetX}px`;
draggable.style.top = `${event.clientY - offsetY}px`;
// Detect if over drop zone
const dropRect = dropZone.getBoundingClientRect();
const isOverDrop = event.clientX >= dropRect.left &&
event.clientX <= dropRect.right &&
event.clientY >= dropRect.top &&
event.clientY <= dropRect.bottom;
dropZone.classList.toggle('highlight', isOverDrop);
});
document.addEventListener('mouseup', function(event) {
if (!isDragging) return;
isDragging = false;
// Check if dropped on target
const dropRect = dropZone.getBoundingClientRect();
const isOverDrop = event.clientX >= dropRect.left &&
event.clientX <= dropRect.right &&
event.clientY >= dropRect.top &&
event.clientY <= dropRect.bottom;
if (isOverDrop) {
console.log('Dropped on target!');
draggable.style.position = 'static';
dropZone.appendChild(draggable);
} else {
console.log('Dropped outside target');
// Return to original position
draggable.style.position = 'static';
draggable.style.left = '';
draggable.style.top = '';
}
draggable.style.cursor = 'grab';
dropZone.classList.remove('highlight');
});
Expected output: The box follows the cursor when dragged. The drop zone highlights when the box is over it. Releasing outside returns the box to its original position.
Drawing with Mouse Events
Capture mouse events on a canvas element for drawing applications.
const canvas = document.getElementById('draw-canvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;
ctx.lineWidth = 2;
ctx.lineCap = 'round';
ctx.strokeStyle = '#333';
canvas.addEventListener('mousedown', function(event) {
isDrawing = true;
ctx.beginPath();
ctx.moveTo(event.offsetX, event.offsetY);
console.log('Drawing started');
});
canvas.addEventListener('mousemove', function(event) {
if (!isDrawing) return;
ctx.lineTo(event.offsetX, event.offsetY);
ctx.stroke();
});
canvas.addEventListener('mouseup', function(event) {
isDrawing = false;
ctx.closePath();
console.log('Drawing ended');
});
canvas.addEventListener('mouseleave', function(event) {
isDrawing = false;
ctx.closePath();
});
// Color picker
document.querySelector('#color-picker').addEventListener('input', function() {
ctx.strokeStyle = this.value;
});
document.querySelector('#brush-size').addEventListener('input', function() {
ctx.lineWidth = parseInt(this.value);
});
Expected output: Drawing on the canvas with the mouse creates freehand lines. Changing the color or brush size affects subsequent strokes. Lifting the mouse or leaving the canvas stops drawing.
Common Mistakes
- Attaching mousemove to the target element instead of document — For drag operations, attach mousemove and mouseup to
documentso the interaction continues even if the mouse leaves the target element. - Forgetting to prevent default on contextmenu — If you do not call event.preventDefault() on the contextmenu event, the browser shows its default menu in addition to your custom one.
- Not handling mouseleave — When the mouse leaves the element during a drag or draw operation, release the interaction state. Otherwise, it stays stuck until the next mouse event.
- Ignoring event.button for right-click detection — The click event fires for any button. Check event.button === 0 for primary click detection.
- Not throttling mousemove — Mousemove fires at a very high rate (60+ times per second on high refresh rate displays). Throttle to 16ms intervals or use requestAnimationFrame.
Practice Questions
- What is the order of events for a single left click? mousedown -> mouseup -> click.
- How do you get the mouse position relative to the element? Use event.offsetX and event.offsetY, which are relative to the target element's padding edge.
- Why should mousemove be attached to document during drag? So the drag continues even when the mouse moves outside the dragged element. Attaching to document ensures mouseup also fires regardless of position.
- Challenge: Build a resizable split panel. Two divs separated by a 5px wide divider bar. Mousedown on the bar starts resize, mousemove updates the widths, mouseup stops. The total width should remain constant.
FAQ
Mini Project
Build a simple paint application. Use a canvas element for drawing. Add controls for brush color (color input), brush size (range slider), and an eraser mode toggle. Show the current coordinates below the canvas. Add a clear button that wipes the canvas. Use mousedown, mousemove, mouseup, and mouseleave events for the drawing logic.
What's Next
Continue with Lesson 19: Touch Events to learn about touch-specific events for mobile and tablet interactions.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro