How to make a horizontal scrolling area (iOS inertia style) with just CSS
It’s fairly common requirement that a section of user interface should scroll in the horizontal axis but not the other. In desktop (non-touch) browsers, using overflow-x: scroll;
and overflow-y: hidden;
usually does the trick.
If you’d like nice inertia scrolling in iOS, the easy solution is to use -webkit-overflow-scrolling: touch;
in the CSS. But there is a problem. If the elements within that overflow area are block level (e.g. anything set to display: block; such as divs by default) you’ll get vertical scrolling in the axis you don’t want it. With this proprietary property, WebKit wants to provide inertia scrolling to allow the full area of the enclosed item to be scrolled. More succinctly, the problem is that -webkit-overflow-scrolling: touch;
can’t be set on a single axis. There is no -webkit-overflow-scrolling-x: touch;
for example.
Thankfully, there’s a pretty easy and robust way to solve this, leaning on the white-space: nowrap;
property/value combination.
So, to illustrate with a specific example, give the wrapping element -webkit-overflow scrolling: touch;
alongside white-space: nowrap;
, then set every first child of that element to display: inline;
(regardless or not of whether it is a true inline element by default). Here is an example reduction of the technique:
Example Reduction
See the Pen vGetI by Ben Frain (@benfrain) on CodePen
Here is the relevant HTML (obviously you could have as many child elements as needed):
<div class="wrapper">
<a href="" class="internal">item 1</a>
<a href="" class="internal">item 2</a>
<a href="" class="internal">item 3</a>
<a href="" class="internal">item 4</a>
<a href="" class="internal">item 5</a>
</div>
And the CSS:
.wrapper {
width: 320px; /* arbitary for demo only */
white-space: nowrap;
overflow-y: hidden;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
.internal {
display: inline;
}
Tip: You can still have block level elements as children of the inline element but the first child of the wrapper needs to be inline (or inline-block or inline-table).
The result will be a scrollable area that uses iOS touch based inertia scrolling where relevant but only in the horizontal direction.
Note 2: when you want a flexible width container (e.g. width: 100% so it fills the parent) some OS’s require an additional nested element to contain the items inside the scroller. Then the outermost element has the scrolling properties and effectively scrolls one big long element (that contains each of the scrollable items). That probably sounds more complicated than it is so look at the code of this example: http://codepen.io/benfrain/pen/WwzORp
In the absence of that additional wrapper some OS’s (e.g. iOS 8/9) can scroll the whole page.
Hi Ben,
Thanks for this post. I’m having trouble implementing it though on my website. Would you be able to point me in the right direction please? I have gallery function which scrolls with the mouse wheel on a PC but doesn’t allow any interaction on a tablet or smart phone. I tried to apply what you’ve written with no success. Any help would be greatly appreciated. The site i’m working on is http://www.synapse-design.co.uk/mercedes If you click the first of the 3 categories you’ll see a gallery of images – it’s these that I’m trying to apply a horizontal scroll to.
Thanks again. Aliput