Programmatic scrolling

For a project I was working on, I had to add horizontal scrolling to an element that would otherwise wrap on narrow screens.

Forcing horizontal content to scroll is pretty easy when you have control over the content width. Set the content container’s overflow property like so:

.scroller {
  overflow: hidden;
  overflow-x: auto;
}

See it in action here:

On touch devices, however, the scrollbar doesn’t appear until you swipe to scroll. So how do you make it clear to the user that there is more content? That’s the fun part.

Scrolling an element in JavaScript is easy enough. Assuming the element as el, update its scrollLeft property. Increase the value to scroll right; decrease to scroll left.

el.scrollLeft = el.scrollLeft + 50

Unfortunately, the scrolling isn’t very pretty. However, smooth scrolling is pretty easy to achieve with jQuery (which, as it happens, I am already using in this particular project). Using the animate() function, update the scrollLeft property, and give it a duration (in milliseconds).

$(el).animate({
  scrollLeft: el.scrollLeft + 50
}, 200);

So far, we’ve added scrolling by clicking (or touching, if you’re on a mobile device) buttons. But to further enhance the experience, I want the left and right controls to only be visible when needed. For that, we need a function that fires on a scroll event, and it needs to know when to display which control.

Knowing when to show the left button is easy: we’ll use the scrollLeft property. If it’s greater than zero, then we need to show the button.

if (el.scrollLeft > 0) {
  $('.pager.left').show();
}

Determining when to show the right button is a little trickier, since there’s no “scrollRight” property. For that, we need to look at two widths of the scrollable element: the width of the visible content and the width of the total content.

We can get the total width of the content with the scrollWidth property, and the width of the visible content with the clientWidth property. If the visible width, subtracted from the total width, equals the length scrolled, then we’ve reached the end of the content and should hide the right scroll button.

if (el.scrollLeft !== (el.scrollWidth - el.clientWidth)) {
  $('.pager.right').show();
}

Assuming the logic above is all inside a function called handleScroll(), we can attach it to the scrolling container’s scroll event like so:

el.onscroll = handleScroll;

Side note: it’s a bad idea to call any function directly from the scroll event. You’ll want to throttle or debounce the event so it doesn’t kill your performance, like this:

el.onscroll = $.debounce(250, handleScroll);

You can see the whole thing put together here:

Some other notes from working on this thing:

  • iOS devices don’t allow for smooth scrolling on overflowed containers natively. To fix this, add this property to your container: -webkit-overflow-scrolling: touch;
  • The scrollbar of the container can be customized (or hidden entirely) for Chrome and Safari by using the ::-webkit-scrollbar property.
  • Microsoft only allows the color of the scrollbar to be customized, but you can make sure it doesn’t increase the size of the container with this property: -ms-overflow-style: -ms-autohiding-scrollbar;
  • Firefox doesn’t allow you to customize the scrollbar at all. Click for drama!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related