Scrolling a overflow:auto; element on a touch screen device
Monday, May 03, 2010
In mobile Safari on the iPhone, iPod Touch, and iPad (as well as the webkit based browser on Android phones) it's not immediately obvious how to scroll a div that has overflow:auto; set on it. If this were a desktop browser you would see scrollbars and be able to manipulate those or even use your mouse wheel. No such concepts exist on a touch screen device!
To scroll the entire page you just touch it and move your finger. But when you touch the element that would normally scroll, the entire page scrolls instead. This is a little bit broken in my opinion since there's no visual indicator that you aren't seeing all the content. However, if you are on a site and you know there's a scrollable div there is a simple (but not obvious) workaround. Simple use two fingers at the same time and scroll them in the same direction.
This works OK but like I said it's not obvious, there's still no indicator that the content is scrollable, and when you use more than one finger you might accidentally trigger some other gesture like scaling the page. I recently ran into this exact issue at work and came up with a pretty solid solution in javascript. I broke this down into two simple functions, but the last one is where the magic happens.
This first function simply attempts to create a new touch event. Only touch screen browsers like mobile Safari have these events, so if it doesn't throw an error then we are using a touch screen device. Otherwise it's probably a desktop browser.
function isTouchDevice(){
try{
document.createEvent("TouchEvent");
return true;
}catch(e){
return false;
}
}
Next, this function calls the isTouchDevice() function, and if it succeeds we attach some special touch events to the page. First when a touch event begins (the touchstart event) we get the current scroll position and prevent the default browser behavior, which in this case would be to scroll the page. Next when you move your finger the touchmove event is called. Here we just subtract the position of your finger from the scroll position we saved earlier and again prevent the page from scrolling.
function touchScroll(id){
if(isTouchDevice()){ //if touch events exist...
var el=document.getElementById(id);
var scrollStartPos=0;
document.getElementById(id).addEventListener("touchstart", function(event) {
scrollStartPos=this.scrollTop+event.touches[0].pageY;
event.preventDefault();
},false);
document.getElementById(id).addEventListener("touchmove", function(event) {
this.scrollTop=scrollStartPos-event.touches[0].pageY;
event.preventDefault();
},false);
}
}
So that's it, just include these functions on your page and just call it by passing in the ID of the element you want to scroll. Like so: touchScroll("MyElement");. You can see a working demo here: http://chris-barr.com/files/touchScroll.htm. I feel like this is a better way of doing things because it's more intuitive since you're just using one finger, and it's potentially more obvious. Even if you don't immediately know there's hidden content, you might accidentally touch this while scrolling the page and realize there's more to see in this div.
Comments
Dude you rock! Silly that these browsers don’t handle this type of element in a more intuitive way, considering CSS overflow is taking over what we used to use iframes for…
Thanks much for the very concise, very functional code!
Oh, something you should be aware of: the try/catch block in isTouchDevice seems to have no effect.. As in all the real browsers on desktop computers (Firefox and WebKit—Chrome and Safari) don’t fail in that block and allow a TouchEvent to be created. This doesn’t seem to have any adverse effects on usability, it just could mean that this check is entirely unnecessary.
Thank you so much for this code snippet, really useful!
Do you know any way to add kinetic/momentum scroll to the scrollable div?
Thanks!
@Max - I haven’t looked into it, but I imagine it wouldn’t be too hard to add in if you found the right algorithm.
The one flaw in this, at least for my application, is that I have clickable links inside my scrolling region that the touchstart event is capturing. So no clicking.
Ideas?
@Nick - I’ve fixed the issue with the try/catch block. The
if(isTouchDevice()){… was missing the parenthesis to run it as a function. This actually caused it to break pretty bad in IE as well.Anyway: a dumb oversight by me, but a simple fix as well.
Wow. I developed our site with desktop in mind. Bought the iPhone 4 and most of the site renders correctly. Then I tried to scroll a long list (client list) and couldn’t! I panicked. Finally found your page and it’s amazing that the double-finger parallel scroll works for scrolling the div! Whew! Didn’t want to make some hack or patch. Since the “double flick” seems to work, it’s odd that Apple wouldn’t include that in their basic gesture manual. I’d rather just tell someone the site works fine and they just need to use the “standard” gesture than make some unnecessary patches. As for no visible scrollbars, that’s a problem. Mine is cutting the middle of content so it’s kind of obvious, but I can see a break in white space would cause problems.
I can see, though, why iPhoneOS gets confused with the gestures. I don’t see why they wouldn’t show scrollbars in overflow DIVs though!! What’s the logic there???
Thanks for sharing Chris - this was a great solution!
As Brad pointed out, there’s a problem with links, or even nested touch events. Do you have any ideas how to cope with this?