Mouse events
Almost every interface you build reacts to a pointer moving and clicking. This chapter takes the mouse events you’ve already met and fills in the parts that trip people up: exactly which events fire and in what order, how to tell the left button from the right, which modifier keys were held down, and the two coordinate systems every mouse event carries.
One thing to keep in mind before the details. These events don’t only come from an actual mouse. Phones, tablets, and trackpads generate them too. When you tap a touchscreen, the browser fires a matching set of mouse events so that pages written for a mouse keep working. So “mouse event” is really about a pointing action, not a physical mouse.
Mouse event types
You’ve seen several of these already. Here’s the full cast for this chapter.
mousedown / mouseup — a mouse button is pressed down over an element, then released over it.mouseover / mouseout — the pointer moves onto an element, then leaves it.mousemove — fires for every move of the pointer while it’s over an element. A single drag across a box can fire this dozens of times.click — fires after a mousedown and then a mouseup land on the same element, and only for the left button.dblclick — fires after two clicks on the same element inside a short window. You’ll rarely reach for it these days.contextmenu — fires when the right button is pressed. But a keyboard menu key (or a two-finger tap) opens the context menu too and also fires this event, so treat it as “the menu is being requested,” not strictly “right mouse button.”There are more mouse-related events (the mouseenter/mouseleave pair, drag events, and so on) that later chapters cover. The list above is enough to reason about a normal click.
Events order
A single user action often triggers a sequence of events, not one. Press and release the left button, and three events fire in a row.
When one action produces several events, the browser fires them in a fixed order. For a left click that order is always mousedown → mouseup → click.
A double click layers on top of this. Each of the two clicks fires its own full mousedown → mouseup → click sequence, and only after the second one does dblclick fire. So a double click is a fairly busy stream of events.
To watch this live, wire one handler to several events on a single target and print each one into a log as it fires. Click the box below with the left button and you’ll see the mousedown → mouseup → click trio arrive. Try the right button for mousedown → mouseup → contextmenu. Double-click to watch two full sequences land before dblclick. The log also prints each event’s button value (explained next) and drops a divider whenever more than a second passes between events.
Mouse button
Every click-related event carries a button property telling you which physical button was involved.
You usually don’t need it for click or contextmenu: click only ever fires for the left button, and contextmenu is tied to the right button (or the menu key). The property earns its keep on mousedown and mouseup, which fire for any button. There, event.button is how you separate a right-button press from a left-button press.
The possible values:
| Button state | event.button |
|---|---|
| Left button (primary) | 0 |
| Middle button (auxiliary) | 1 |
| Right button (secondary) | 2 |
| X1 button (back) | 3 |
| X2 button (forward) | 4 |
Most mice only have left and right buttons, so in practice you’ll see 0 or 2. Touch devices produce equivalent events when someone taps.
Modifiers: shift, alt, ctrl and meta
Every mouse event also records which modifier keys were held while it fired. Four boolean properties:
event.shiftKey— Shiftevent.altKey— Alt (or Opt on Mac)event.ctrlKey— Ctrlevent.metaKey— Cmd on Mac
Each is true if that key was down at the moment of the event. That lets you build shortcuts like Alt+Shift+click. This button only reacts when both are held:
<button id="unlock">Alt+Shift+Click to unlock</button>
<script>
unlock.onclick = function(event) {
if (event.altKey && event.shiftKey) {
alert('Panel unlocked!');
}
};
</script>
Try it below. Hold different combinations of keys while you click and watch the four boolean flags flip. The button only unlocks when both Alt and Shift are down at the moment of the click:
Coordinates: clientX/Y, pageX/Y
Every mouse event reports where the pointer was, in two coordinate systems at once:
- Window-relative —
event.clientXandevent.clientY, measured from the top-left of the visible area. - Document-relative —
event.pageXandevent.pageY, measured from the top-left of the whole document.
The full story is in the Coordinates chapter. The short version: pageX/Y are anchored to the document, so scrolling doesn’t change them for a given spot in the content. clientX/Y are measured from the current viewport, so they do change as you scroll, the same way position:fixed stays put relative to the window.
Picture a 500×500 window. If the pointer sits in the top-left corner, clientX and clientY are both 0, no matter how far the page is scrolled. In the center they’re both 250, again independent of scroll. That’s the “fixed to the window” behavior.
To see clientX/Y change as you move, print them from a mousemove handler. Glide the pointer across the pad below — the readout updates on every move, and a dot tracks your position inside the box:
Preventing selection on mousedown
Double clicks have an annoying side effect: they select text. Double-click the word below and the browser highlights it, right alongside whatever your handler does:
<span ondblclick="alert('dblclick')">Double-click me</span>
Pressing the left button and dragging does the same thing — it starts a text selection, which is usually unwanted inside interactive widgets like a custom slider or a sortable list.
There are several ways to stop selection, covered in Selection and Range. For this specific problem the cleanest fix is to cancel the browser’s default action on mousedown, which is what starts a selection. Return false from the handler:
Before...
<b ondblclick="alert('Click!')" onmousedown="return false">
Double-click me
</b>
...After
Now double-clicking the bold text won’t highlight it, and press-and-drag on it won’t begin a selection either.
Compare the two directly. Double-click (or press and drag across) each label below. The first selects text the usual way; the second cancels the default on mousedown, so no highlight appears:
The text inside is still selectable — you just can’t begin a selection on the element itself. A user can start selecting before or after it and drag across, which is normally fine.
Summary
Mouse events hand you a consistent set of properties:
- Which button:
event.button(0left,1middle,2right,3/4side buttons). Needed mainly onmousedown/mouseup, which fire for any button. There’s also a pluralevent.buttonsbitmask for combinations, rarely needed. - Modifier keys (
truewhen held):event.altKey,event.ctrlKey,event.shiftKey, andevent.metaKey(Mac’s Cmd). For a Ctrl shortcut, checkif (event.ctrlKey || event.metaKey)so Mac users get Cmd, and always leave a path for devices with no keyboard. - Window-relative coordinates:
event.clientX/event.clientY, unaffected by scroll. - Document-relative coordinates:
event.pageX/event.pageY, anchored to the document.
Events from one action fire in a fixed order — mousedown → mouseup → click for a left click. The default action of mousedown is to begin text selection; cancel it when that gets in the way of your interface.
Next up: the events that track the pointer moving across elements, and how to follow what’s underneath it.