Detecting Touch Devices – A 2018 Update

In Technology
Scroll this

Detecting touch capability for your web site or application is a famously complicated task. A quick search on Stack Overflow returns many threads full of frustrated developers. Given the break-neck pace of the modern web, most of the discussion is pretty outdated. I thought a 2018 overview of the present state of things might be called for.

TL;DR: this topic has become no less complicated since the last time you read about it.

If you want to follow me down the rabbit hole, a great place to start is an article by Stu Cox called “You Can’t Detect a Touchscreen”. It was true in 2013 and it’s still true in 2018: you can’t detect with 100% accuracy whether a site visitor is using a device with touch capability or not. There are simply too many browsers and devices, and no one has rallied behind one unifying specification.

Do you have a legitimate need?

It’s best to accept the cold, hard reality up front: this is a path of frustration. I won’t go so far as to say “futility”, but it’s pretty close. You are going to run into limitations, exceptions, and lack of full cross-browser support. If you want to save yourself a lot of hassle, you should ask yourself upfront if you’re pursuing touch detection out of a legitimate need.

A lot of developers, including Stu, are a bit belligerent on this point. They see a lot of frivolous use cases for touch detection, and they’re not wrong to be exasperated by it. But it’s not all folly. Having this capability would be a real boon to many of us.

My interest in touch detection started with my web app Anonynote. It’s a mobile-first app designed to be as cross-device, cross-browser compatible as possible. I want it to be as usable and full-featured on the smallest mobile screens as it is on a 27″ 5K Retina Display.

One of Anonynote’s functions—the ability to sort notes by clicking and dragging—is only possible on non-touch devices. That’s a limitation of jQuery UI, which, while there is a workaround, is not that great from a usability perspective in this case.

This is a feature I do not want to be without on desktop devices. But on touchscreens, I don’t want my users to see a broken button. It’s confusing, as well as a waste of precious real estate. For all of these reasons, I strongly feel that touch detection would be a legitimate benefit for my app.

Screen size is not a viable indicator

@media screen and (max-width: 767px) {
	/* Mobile and tablet styles */
}
@media screen and (min-width: 768px) {
	/* Desktop styles */
}

Using a CSS Media Query to detect screen size, while a decent idea in general, is fatally flawed. There’s too much diversity in the hardware marketplace.

Not all small devices are touchscreens, and not all large screens use pointing devices like mice. Look at the size of this touchscreen from Microsoft.

This is the technology world we live in. In some ways, it’s wondrous. But how can a developer consider every use case when there are devices like this on the market?

It’s a rhetorical question. You can’t. Don’t try.

Modernizr

You can begin to approach the problem yourself using JavaScript, but there’s already a pretty good packaged solution. Modernizr's Touch Events detection handles some of the cross-browser support for you. They’re very clear, however, about the limitations of their code:

  • Indicates if the browser supports the W3C Touch Events API.
  • This does not necessarily reflect a touchscreen device.
  • Older touchscreen devices only emulate mouse events.
  • Modern IE touch devices implement the Pointer Events API instead: use Modernizr.pointerevents to detect support for that.
  • Some browsers & OS setups may enable touch APIs when no touchscreen is connected.
  • Future browsers may implement other event models for touch interactions.

That’s a pretty big list of “buts”. Nonetheless, I was using it for a time with reasonable success. IE, Chrome, and Firefox on desktop PC were all correctly flagged as non-touch. Mobile Safari and various Android phones were correctly flagged as touch. My quality control was no more robust than that, but I didn’t feel a need to do better. If a lesser-used device was incorrectly flagged, well, Anonynote would still be functional.

Then the other day, on a non-touch PC laptop using Chrome, Modernizr was flagging it as a touch device. I haven’t yet confirmed the cause, but I suspect Chrome may now be implementing a touch API even in the absence of a touchscreen. Here’s one random guy who seems to echo that suspicion.

With that, I jettisoned Modernizr. It was nice while it lasted. If nowhere else, I have to be able to rely upon a proper user experience in Chrome.

But what to replace it with? I began the search.

W3C Touch Events

David Gilbertson has a 2016 article on Codeburst in which he differentiates device touch capability from the act of a user actually touching a touchscreen—in other words, device touch vs. human touch. The former is impossible, but the latter is very easy for browsers that support the W3C Touch Events specification.

window.addEventListener('touchstart', function onFirstTouch() {
	// perform action here and remove listener
	window.removeEventListener('touchstart', onFirstTouch, false);
}, false);

He goes on to make the case that, in most instances, it’s not only good enough but preferable to detect for human touch. I can’t speak for “most instances” but it’s certainly not convenient for my situation. I want to make layout changes to my app depending on whether the user’s device is touch capable. If I can only know that after the execution of the first touch event, I’m having to make an unexpected layout change mid-use. That’s far from ideal.

But I don’t doubt that, for many, this could be your answer. Here in 2018 the Touch Events spec is pretty well supported among modern browsers. It’s still flawed—depending on your use case, perhaps more than a little. But you may be able to throw extra code at the problem until it gets to a place of reasonable satisfaction.

Just remember: there is a line beyond which your code is simply too unwieldy—too complex to easily modify, or too many potential points of failure as browsers and frameworks continue to evolve. As a developer, you know in your gut where that line is.

Try it, but JavaScript may not be your best solution.

Back to CSS

New developments in CSS may provide a better answer. Enter the media queries hover and pointer.

The hover media feature is used to query the user’s ability to hover over elements on the page with the primary pointing device.

The pointer media feature is used to query the presence and accuracy of a pointing device such as a mouse.

Andrés Galante at CSS Tricks has an excellent write up on these two features, and how you can combine the use of both of them to predict the presence of a mouse or touchscreen with surprising accuracy.

/* smartphones, touchscreens */
@media (hover: none) and (pointer: coarse) { ... }
/* stylus-based screens */
@media (hover: none) and (pointer: fine) { ... }
/* Nintendo Wii controller, Kinect */
@media (hover: hover) and (pointer: coarse) { ... }
/* mouse, touch pad */
@media (hover: hover) and (pointer: fine) { ... }

Note that in the case of multiple inputs, hover and pointer both query the primary input device. Use any-hover and any-pointer to query all input devices.

If this sounds almost too good to be true, it kind of is. It’s still early days for these features. In 2018, browser support is probably not as good as you’re hoping for.

But one day soon, right? One day soon…

Conclusion

We’re still stuck in the mire, my friends.

I can’t give you a cookie-cutter answer. Your particular use case is going to require a skillful combination of several considerations:

  1. Do I really need touch detection?
  2. What’s possible using CSS and/or JavaScript?
  3. How much cross-browser, cross-device support am I willing to sacrifice?

I’ll leave you with one more thought: when the solution is more convoluted than the problem, it’s probably best to just live with the problem.

Submit a comment

Your email address will not be published. Required fields are marked *