860Days

860 days since this post was last revised. Specific details are likely out of date.

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.

Many other solutions to this problem that I have seen require a specific width set on the wrapper element. It’s not necessary with this technique. That means if you don’t know how many items might be added into the list it will still function as you would hope.

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.

Ben Frain Developer, Author: 'Enduring CSS', 'Responsive Web Design with HTML5 & CSS3'.