Adapting an Interface for Touch Devices
The world we design for is changing at a fairly rapid pace. This time last year the mobile web was the hot topic. We were all building optimized versions of our sites and applications for handheld devices, and marveling at how much we could make them look like native iPhone apps.
Shortly after that, when the Android platform became popular, we realized that we shouldn’t be tying our interfaces so closely to a specific device. Because, well, not everyone has an iPhone. To design a mobile site or application as if it were part of the Apple army may lessen the overall experience for the great majority of our users. So we began liberating ourselves in mobile design, much as we did with the desktop.
The techniques we use for device detection have become much the standard, to the point where they’re rarely questioned. We either detect server-side and send the user off to a different site optimized for a mobile device, or we use media queries to detect screen size and adjust the design accordingly.
But as Bertrand Russell wrote: “In all affairs it’s a healthy thing now and then to hang a question mark on the things you have long taken for granted.”
I’m here to let you know that there’s at least one more step towards building that perfect user experience: touch detection.
The Current State of Detection
There are two ways dominating the best-practice scene for detecting if a user is on a mobile device. The first one, which has been around for a while, is server-side, device-based detection. And the second is using CSS media queries to detect screen size. There are pros and cons with both these methods, of course. So, let’s have a look, shall we?
Detecting for a Device
Detecting for a device can be a very effective way to target specific users. It’s usually done by pulling in the User-Agent
string and reading through it for certain keywords (like “iphone”). In doing this, you can tell which device is being used to view your site or application, and modify your design for each occasion.
Benefits
Running a detection script on the server definitely has its benefits. 99 times out of 100 the server will be faster than the client anyway, so there’s a clear performance benefit, and you have a lot more control over the data being returned. As a general rule, unless you have a controlled or targeted environment, this should rarely be left to a technology that is sometimes absent, like JavaScript. Another advantage is that you can specifically target any number of devices within your user base. For instance, if you only want iPhones and Android devices seeing your optimized design, it’s very easy to implement using this method.
Pulling the User-Agent
is very easy with PHP. Here we’re just echoing it out for debugging purposes:
<?php echo $_SERVER['HTTP_USER_AGENT']; ?>
Drawbacks
The drawbacks of this method are specifically what sparked my search for another way. When trying to design for mobile devices, ideally, you’d like to target them all and create a flexible and consistent interface. This is where the problem lies. When using any sort of device detection, will be difficult to scale effectively. After a while, you find yourself trying to manage a list of 20-30 supported devices, and if a device isn’t in that list, it won’t be picked up.
Another problem you can run into is partial User-Agent
string matches. I discovered this while building a mobile site and noticing that Opera was picking up my mobile design for some reason. After looking into it, I found that the Opera User-Agent
string contains the word “Presto,” which is its rendering engine. Unfortunately, this caused problems with the “Palm Pre” User-Agent
because of the p-r-e. As the range of available devices expands, I think we’ll see a lot more of this type of issue with server-side device detection.
Detecting for Screen Size
Currently, screen (or window) size detection is done with CSS media queries. The queries are generally embedded right in the main style sheet (though they can be separate), and can be sectioned off into as many screen sizes as you want. Most people work with two or three. If you’d like to read more on media queries, check out Ethan Marcotte’s Responsive Web Design article from A List Apart.
Benefits
Using a media query is a very fast and lightweight way to target a user on a smaller screen. These are great because they not only apply to mobile devices, but also to any device with a smaller screen, such as a netbook. They help a great deal in preventing horizontal scrolling,not to mention the user possibly feeling cramped when using a smaller browser window, while still presenting a usable design.
Drawbacks
As much as we want it to, a small screen doesn’t necessarily mean a touch device. Even a small screen that supports media queries may not be a touch device. So while this method may work very well for general design and layout, it basically fails to transfer over into interface design. Some user interactions, like drag and drop, work fine on a device with a small screen—as long as there’s also a pointing device. With a touch-only device, drag and drop is woefully inadequate. There is also the possibility that media queries might be unsupported by a given device. Depending on your audience, you may want to explore another option.
Why We Need Another Way
We’ve established that the two most popular methods we have right now for mobile detection have their downsides: one is hard to scale, and the other lends itself poorly to interface design.
Let’s take a step back and ask ourselves which devices we’re concerned with. Basically, any device that has either a small screen size, a touch-capable screen, or both, should be a target for a specially adapted interface. Technically, this covers quite a few devices, but in reality, we’re talking about these specific categories:
-
touch tablets (including the iPad)
-
netbooks
-
modern mobile phones or devices
The difference between iPads (or other touch tablets) and netbooks is a perfect example of where media queries alone will fall short in terms of interface design. The iPad and a typical netbook will have roughly the same screen size, so media queries work well for layout considerations; however, the touch capabilities of the iPad hinder a lot of the normal interface elements of an application that we take for granted. And we have to account for that to create the best possible experience.
What We Lose with a Touch Interface
Touch interfaces are awesome, right? They’re the wave of the future. But the way we interact with a touch-enabled device is a lot different than the way we interact with a non-touch device. This goes far beyond the normal compensations, like inserting more padding around a link to create a larger hit area for a person’s finger, or accounting for the lack of a mouse.
When developing in a touch environment, we lose some of the more rich interface elements we’ve grown to love; for example, hover states, internal page swiping, and drag and drop.
If we focus on the capabilities rather than the limitations, we can avoid resorting to a less exciting interface to account for these new devices. The reason drag and drop fails to work on a touch device isn’t because it doesn’t make sense; it’s because the action of pressing a finger to the screen and moving it already has a default behavior: to scroll. The same principle holds for finger pinching to act as a full-page zoom on most modern touch devices. In the same way we can prevent a link from firing with a return statement of false
, we can prevent a finger movement from scrolling or zooming.
Creating a Rich Experience
So there we have it. Media queries allow us to target device screen sizes with specific versions of our layouts, but they fail when it comes to delivering specialized interfaces to touch and non-touch devices. After my frustration reaching breaking point with the iPad, I decided to do some old-fashioned research to see if there was a way to tell if a device had touch capabilities. After a few hours of scouring the Safari developer documentation, I found a small section outlining the Touch
object and how to handle certain events. Here’s what I found.
How to Detect Touch
Safari offers us a JavaScript event object called Touch
, and the sole duty of this little fella is to let you know that the device you’re working with has a responsive touch screen interface. This was the Holy Grail, exactly what I was looking for when I started poking around. Needless to say, I was geeking out.
The implementation is amazingly easy:
if(window.Touch) { /* JavaScript for your touch interface */ }
After you detect for touch capabilities you can make all sorts of adjustments to complement the interface. The downside (you didn’t think it was going to be that easy, did you?) being that it only works on Apple devices right now.
Of course, there’s another way: you can perform your touch detections upon first touch of the screen. This far from ideal, but it’s still an effective way to detect for touch on a device. This method is also more widely supported across most modern devices. The downside is that you can’t rely on it for major alterations to your design; that would result in the page suddenly transforming as soon as the user touched the screen. But if, as we’ve discussed, you mainly want touch detection in order to adjust rich interactions like hover states or drag and drop, you should be able to manage that:
var touch_detect = { handler: function(event){ /* JavaScript for your touch interface */ }};document.ontouchstart = touch_detect.handler;
By detecting touch this way we can do everything: from toggling special classes to compensate for the lack of hover controls, to building an entirely new interface that focuses on taking advantage of the rich capabilities native to the device.
We can also track the movement or behaviors with the relevant JavaScript events: ontouchmove
, ontouchend
, ontouchcancel
, orientationchange
, gesturestart
, gesturechange
, and gestureend
. All these events have been available on Apple devices since iPhone OS 2.0. Only some of these actions have Android support as of right now, such as ontouchstart
, so that’s safe to use.
When we combine this detection method with CSS media queries, we can make some very fast, responsive, and memorable applications. These aren’t just limited to phones, but can be deployed everywhere in one fell swoop. Building once, and deploying everywhere.
Default Behaviors
Now that you know whether the device is touch-enabled or not, you can start building your interface specifically for that device. But before we do that, there are some relatively global default behaviors across touch devices that we’ll need to overwrite, as touched on earlier (no pun intended). These are mainly finger scrolling and pinch-to-zoom, which can affect any drag and drop or special finger movement actions we may want to create. However, we want to avoid disabling these behaviors globally, because they’re still very useful. Instead, we’ll disable them on an element-by-element basis using event listeners.
By capturing the finger movement and hijacking it, we can prevent the default scrolling behavior using jQuery’s event.preventDefault()
method, and insert anything we want.
Here’s an example combining touch detection with event listeners to prevent the default actions:
$(function(){ if(window.Touch) { touch_detect.auto_detected(); } else { document.ontouchstart = touch_detect.surface; }}); // End loaded jQueryvar touch_detect = { auto_detected: function(event){ /* add everything you want to do onLoad here (eg. activating hover controls) */ alert('this was auto detected'); activateTouchArea(); }, surface: function(event){ /* add everything you want to do ontouchstart here (eg. drag & drop) - you can fire this in both places */ alert('this was detected by touching'); activateTouchArea(); }}; // touch_detectfunction activateTouchArea(){ /* make sure our screen doesn't scroll when we move the "touchable area" */ var element = document.getElementById('element_id
'); element.addEventListener("touchstart", touchStart, false);}function touchStart(event) { /* modularize preventing the default behavior so we can use it again */ event.preventDefault();}
You can see that we’re adding an event listener for the touchstart
event. When that happens, we call the function touchStart()
, which disables the default behavior (scroll and zoom) for whatever element the listener is attached to. This element could be a div
, a link, a draggable area—anything you want. It’ll allow you to work with the element without worrying about odd behaviors.
Support for Touch Detection
Currently, the Touch
object is only available in Apple products (iPod, iPad, iPhone), but swipe detection and use of the ontouchstart
event is available in a number of modern devices, including Android devices. I didn’t get a chance to fully test the new Blackberry.
If I had to guess, I’d say that the Touch
object should be on most devices in the near future. It just makes sense, and it’s already in Safari’s WebKit; it’s a short hop to make it into Chrome, Android, and WebOS.
Looking Forward with Interface Design
Every design or development technique will have holes poked in it; there are no perfect solutions out there. Best practices will evolve, along with the underlying technologies. If we can take some incremental steps towards creating the best possible experience for our users, we’ll be able to inject a layer of interaction that seems so natural that it’s virtually invisible. Ultimately, the best user experience is the one we never hear about.