How to detect a touch screen in JavaScript and CSS — and why ontouchstart lies

The most popular answer to this question is 'ontouchstart' in window. It's wrong in both directions.
On a laptop with a touchscreen it says "touch", even though the person uses a mouse all day. And on iPad, Safari pretends to be a Mac by default: its user agent says Macintosh. A user-agent check says "desktop" — and the person gets a menu that only opens on hover.
The good news: the right way is shorter than the wrong one. Let's go step by step.
Step 1. Decide what you actually need
"Is this a touch device?" is almost always the wrong question. What you really care about is one of two things:
- Can the device hover? If not,
:hovereffects and tooltips won't work. - How precise is the pointer? A finger is coarse, a mouse is fine. Fingers need bigger buttons.
The browser has an exact tool for each question.
Step 2. Gate hover effects behind a CSS check
Instead of guessing the device type, ask the browser directly:
/* hover effect — only where hovering really exists */
@media (hover: hover) and (pointer: fine) {
.card:hover {
transform: translateY(-4px);
}
}
hover: hover — the primary pointer can hover. pointer: fine — it's precise, like a mouse. On a phone the rule simply won't apply. No more "sticky" hover that fires on tap and never goes away on mobile.
If something important lives on hover — say, a tooltip — see why my tooltip doesn't show on mobile.
There's also the reverse trick: on devices without hover, show what usually hides behind hover. For example, a card's "edit" and "delete" buttons:
@media (hover: none) {
.card .actions { opacity: 1; }
}
On a computer they appear on hover; on a phone they're always visible. A finger can reach them.
Step 3. Make tap targets bigger for fingers
@media (pointer: coarse) {
button, a.nav-link {
min-height: 44px;
min-width: 44px;
}
}
pointer: coarse — the primary pointer is imprecise, i.e. a finger. 44 points is the minimum button size from Apple's iPhone guidelines. Smaller, and people miss.
Step 4. Need it in JavaScript — use the same queries
The same conditions are available from code via matchMedia:
const coarse = window.matchMedia("(pointer: coarse)");
if (coarse.matches) {
enableSwipeGallery();
}
coarse.addEventListener("change", (e) => {
// a tablet got attached to a keyboard with a trackpad — the primary pointer changed
e.matches ? enableSwipeGallery() : disableSwipeGallery();
});
Note the change listener. A device can switch its primary pointer mid-session: a tablet gets clipped onto a keyboard with a trackpad. A one-time check at load would miss it.
Step 5. Need "is there any touch at all" — use any-pointer
pointer and hover look only at the primary pointer. If you care whether there's at least one touch input among all of them, use the any- prefix:
const hasAnyTouch = window.matchMedia("(any-pointer: coarse)").matches;
On a touchscreen laptop: pointer: fine (the main input is a trackpad or mouse), but any-pointer: coarse (there's a touchscreen too). That's the honest answer to the original question. For iPad there's also navigator.maxTouchPoints > 0 — it returns the number of touch points even when the user agent pretends to be a Mac.
Step 6. How exactly was it pressed — check the event
Sometimes what matters isn't "what device" but "what made this press". Pointer Events tell you:
button.addEventListener("pointerdown", (e) => {
console.log(e.pointerType); // "mouse", "touch" or "pen"
});
One event for all three input types. No need to wire up click and touchstart separately — and chase double firing.
Step 7. Test on a real device
In Chrome DevTools, open device mode (Ctrl+Shift+M) and pick a phone — it emulates touches. But the emulator won't show you the real size of a finger. Open the site on your phone on the same Wi-Fi network and tap the buttons yourself.
What you get
- On a phone: no sticky hover effects, buttons a comfortable size.
- On a touchscreen laptop: the interface stays "mouse-first" but doesn't break on taps.
- iPad gets mobile behavior, even though it calls itself a Mac.
The rest of mobile layout is in how to make your site mobile-friendly.
Can I just check the screen width?
Width tells you size, not input method. A tablet in landscape is wider than many laptops, and a narrow desktop window still has a mouse. Use width for layout and hover / pointer for behavior.
Does this work in all browsers?
Yes. hover, pointer, any-pointer and Pointer Events are supported by all modern browsers, including Safari on iPhone. No polyfills needed.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





