Trackpad Scroll Emulator jQuery plugin

When Rdio rolled out their redesigned application recently, I was intrigued by the app’s scrolling implementation. There are no conventional browser scrollbars in Rdio, and instead scrollbars appear on-demand when the user’s mouse enters a scrollable portion of the interface, or when the user performs a mousewheel or trackpad scroll. Rdio’s implementation mimics the experience of scrolling in OSX Lion using a trackpad pointing device, except that it works for any user, on any platform.

Inspired by Rdio’s scrollbars I have created a jQuery plugin that emulates Lion’s trackpad-style scrolling, regardless of the user’s pointing device, browser, or platform.

Demo and download

View a demo, download the plugin, or fork this project on github.

What it does

Traditionally scrollbars are permanently displayed whenever an area of a webpage is scrollable. By contrast, scrollbars in OSX Lion are hidden from sight, and revealed only when the user executes a swipe gesture with a ‘magic’ trackpad pointing device.

My plugin emulates Lion’s UI pattern by replacing the browser’s default scrollbars with a custom CSS-styled scrollbar that is only revealed when the user hovers over a scrollable element.

Modern browsers get a very accurate emulation of Lion’s scrollbars, while less capable browsers miss out on a few bells and whistles such as rounded corners, opacity, and animated fades.

Usage

Include Trackpad Scroll Emulator and jQuery in your document. The paths and filenames may differ from those shown here:

<link rel="stylesheet" href="css/trackpad-scroll-emulator.css" />
<script src="js/jquery-1.7.1.min.js"></script>
<script src="js/jquery.trackpad-scroll-emulator-1.0.min.js"></script>

Mark up the content you wish to scroll like so:

<div class="tse-scrollable wrapper">
  <div class="tse-content">
    My content
  </div>
</div>

For horizontal scrolling add the class horizontal to the tse-content element:

<div class="tse-scrollable wrapper">
  <div class="tse-content horizontal">
    My content
  </div>
</div>

In the above examples the wrapper class is not required, but gives us a unique hook to use as a jQuery selector when initializing the plugin:

$('wrapper').TrackpadScrollEmulator();

Notifying the plugin of content changes

If you later dynamically modify your content, for instance changing its height or width, or adding or removing content, you should recalculate the scrollbars like so:

$('wrapper').TrackpadScrollEmulator('recalculate');

Destroying the plugin

To remove the plugin from your element, call its `destroy` method:

$('wrapper').TrackpadScrollEmulator('destroy');

Overwriting the content dimensions

The dimensions of the tse-scrollable wrapping element determine the visible dimensions of your content. Chances are that you’ll want to change the width or height of the wrapping element, which can be done using CSS or JavaScript:

.wrapper {
  width: 250px; /* Example of overwiting default width */
}

The demo bundled with Trackpad Scroll Emulator demonstrates how you might dynamically alter the dimensions of tse-scrollable using JavaScript.

Non-JS fallback

Trackpad Scroll Emulator hides the browser’s default scrollbars, which obviously is undesirable if the user has JavaScript disabled. To restore the browser’s scrollbars you can optionally include the following noscript element in your document’s head:

<noscript>
  <style>
    .tse-scrollable {
      overflow-y: scroll;
    }
    .tse-scrollable.horizontal {
      overflow-x: scroll;
      overflow-y: hidden;
    }
  </style>
</noscript>

If you’re wondering why I didn’t just apply these default styles in Trackpad Scroll Emulator’s CSS stylesheet, the reason is that Internet Explorer would display the browser scrollbars for a moment or two before the plugin kicked in, which is really ugly. I think that using a noscript tag is an acceptable compromise.

How it works

For the most part Trackpad Scroll Emulator uses the browser’s native scrolling functionality, but replaces the conventional scrollbar with a custom CSS-styled scrollbar. The plugin listens for scroll events and redraws the custom scrollbar accordingly.

Key to this technique is hiding the native browser scrollbar. In modern browsers (i.e. WebKit) this is achieved simply by giving the scrollbar a width/height of zero using the ::webkit-scrollbar and ::scrollbar pseudo selectors. In other browsers the scrollable element is made slightly wider/taller than its containing element, effectively hiding the scrollbar from view.

Limitations

Trackpad Scroll Emulator can currently handle vertical or horizontal scrollbars, but not both simultaneously.

Credits

Most of the credit for this technique goes to Rdio’s developers. Rdio is a Backbone application, so their solution is a combination of Backbone, Underscore, jQuery, CSS and Bujagali (their own templating system). What I have done is to recreate the same scrolling functionality using only jQuery and CSS.

Credit is also due to Jonathan Sharp, who wrote the original function for measuring the width of the browser’s scrollbar.

16 thoughts on “Trackpad Scroll Emulator jQuery plugin

  1. ruvan says:

    great script! but have question, how did you implement the mouse scroll wheel, looked through the code but couldnt see any functions related?, thanks.

  2. Jonathan says:

    @ruvan The plugin doesn’t listen for mouse wheel events. The scrolling (the actual movement of the scrollable area) is handled by the browser. The plugin listens for browser scroll events, and redraws the custom scrollbar to match the movement of the browser scrollbar. It is really just a clever illusion – because the browser scrollbar is hidden it seems as if the plugin is controlling the scrolling, but really it’s the browser that does the heavy lifting.

  3. ruvan says:

    neat trick, i must say

  4. arjun says:

    is it possible to add mousewheel and arrow keys support in d horizontal scrolling example??

  5. arjun says:

    hey ur technique is not working properly in d latest version of chrome,chrome 25

  6. Jonathan says:

    @arjun I tested just now in Chrome 25.0.1364.99 on OS X and it works for me?

  7. arjun says:

    in windows 7, this is hw it luks like
    https://docs.google.com/file/d/0B3tW8HqE_ubMNGxlUThnbk9LbFk/edit?usp=sharing

    custom scrollbar on top of default browser scrollbar

  8. Jonathan says:

    @arjun Thanks for letting me know. It looks like there were some breaking changes in Chrome 25, which I’ve fixed now.

  9. Sorin says:

    Great plugin!Thanks For sharing ;)

  10. Grsmto says:

    I found your plugin after seeing it on twitch.tv and I was really impressed by this trick. I’m very interested in your plugin and I’m actually working on it to add features (like mobile support). I think this is also the ultimate solution for custom scrollbars that all developers all over the world are looking for, for years…
    100% of customs scrollbars plugins are crap cause they override native scroll feeling, that’s ugly and laggy. Except yours.

  11. etune says:

    I wish it wasn’t necessary, but man I like this.

  12. samurai says:

    The plugin for horizontal scroll seem to not work the new element (from demo say boxes) when appended the scrollbar width doesn’t alter and the box wraps to the next line.

    $(‘.add-boxes’).on(‘click’, function(e){
    e.preventDefault();
    $(‘.demo2 .tse-scroll-content .tse-content .boxes’).append(’11’);
    $(‘demo2’).TrackpadScrollEmulator(‘recalculate’);
    });
    });

  13. Jonathan says:

    @samurai I’ve updated the plugin/demo to include an example of adding content to the horizontal scroller: http://jnicol.github.io/trackpad-scroll-emulator/

  14. Frances says:

    I love the look of this plugin but it failed when using WP plugin uploader…any suggestions? My website is in maintenance mode until I get the kinks worked out.
    Thanks ever so much.

  15. Jonathan says:

    @Frances I’m not sure what you mean. This isn’t a WordPress plugin, it’s a jQuery plugin.

  16. Krish says:

    How to ‘preventDefault’ once content scroll is done on page.

Comments are closed.