npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@desto/scrollbar-js

v1.4.0

Published

A script which produces stylable scrollbars. It also allows styling of the resize handle.

Downloads

9

Readme

What is this?

A script which produces stylable scrollbars. It also allows styling of the resize handle. Check out the demo page

Aren't there a ton of those already?

Yes, but they all had at least 1 of the following shortcomings:

  • Dependent on a library or framework
  • Only works vertically
  • Native scroll behavior is lost (hello autoscrolling) or messily emulated (e.g. smooth scrolling isn't applied)
  • Doesn't dynamically adjust when content changes
  • Messes with the element (nesting content into a new div, applying css rules, ...)
  • Requires setting height/width on the element to work
  • Probably some other things I forgot before writing it down

However, I can't blame them. I couldn't come up with a solution that doesn't have any of these problems either. See the Limitations and What have I tried? section below for more on that topic.

Browser support?

I've only tested it in Internet Explorer 11, Firefox 47 and Chrome 51. It probably works on older versions as well.

Goals

I only want to style scrollbars, so...

  • It should behave like the native one
  • It shouldn't mess with existing elements

Limitations

  • Since there is no way to figure out the distance the native scrollbar would scroll on click, the distance has to be guessed (see tools/scrollDistance/)
  • The content of the target element will be placed inside another div. That's to retain native scrolling behavior with the mouse and keyboard. If you dynamically add content to it, check if the class scrollbarjs is present to add it to its first child instead.
  • Doesn't work with textareas (yet?)
  • Doesn't work with direction:rtl (yet?)
  • padding-right doesn't properly work on containers that overflow horizontally. That's not a problem with this script though. This behavior already exists in natively overlflowing containers.
  • While modifying the scrollbar of the body does work, I would suggest against doing so on mobile devices. The autohide behavior of the url bar will break. It would hide only after scrolling all the way to the bottom, and then a bit. Same on the way back up, which is really frustrating.

Installation

Just include the script, call scrollbarjs.init() and add elements via scrollbarjs.add( element ).

<!-- at the end of body -->
<script src="scrollbar.js"></script>
<script>
	scrollbarjs.init();
	var $elems = document.getElementsByClassName('custom-scrollbar');
	for( var i = 0; i < $elems.length; ++i ){
		scrollbarjs.add( $elems[i] );
	}
</script>
</body>

You might also want to link scrollbar-basic.css when starting out. This file contains some basic styling.

<link rel="stylesheet" href="scrollbar-basic.css">

You can change some default values by passing an object to scrollbarjs.init().

  • prefix: string = "scrollbarjs" the prefix to use for generated classes (don't forget to change the .css accordingly)
  • buttonDistance: number = 60 the distance to scroll when clicking on a scrollbar button. If this is an integer, it will scroll by that many pixels. If this is a floating point number, it will be used as a multiplicator (e.g. 0.90 scrolls by 90%). If you want to scroll by 100%, you have to specifiy something really close to it, like 0.9999. That's because JS can't actually differentiate between ints and floats.
  • trackDistance: number = 0.9 the distance to scroll when clicking on the track (the part between button and thumb). Values act the same as above.
  • delay: number = 200 the time in milliseconds to wait until the mousedown event repeats
  • repeat: number = 50 the time in milliseconds to wait between repeats

scrollbarjs.init() returns an object containing some information that you might want to make use of:

  • nativeHeight: number height of the native scrollbar
  • nativeWidth: number width of the native scrollbar
  • nativeDisplaces: boolean does the native scrollbar displace the content? (usually desktop browsers do, mobile ones don't)
  • supportsWebkitStyling: boolean are ::-webkit-scrollbar pseudo classes supported? WebKit Blog
  • supportsMsHiding: boolean is -ms-overflow-style supported? MDN
  • supportsScrollbarWidth: boolean is scrollbar-width supported? MDN
var scrollbarProperties = scrollbarjs.init({
	prefix: 'sb',
	buttonDistance: 0.25
});

Note!

  • If you have padding on a target element, apply it to target > .scrollbarjs-viewport instead.
  • On the body element, the body's height will be set to 100vh. Otherwise, it would gradually grow when it's smaller, or it won't scroll if it is bigger.

How does it work?

First the script will figure out the native scrollbar width and height. It does so by creating an element with scrollbars and calculating offsetWidth - clientWidth and the same for height. This element will be removed afterwards.

scrollbarjs.init() will add some required CSS styling to the page, as well as adjust some variables, if a fitting object is passed as a paramater.

When adding an element ($elem) via scrollbarjs.add($elem), its content will be copied to a newly created div ($viewport). $elem's scrollbars will be hidden via the CSS rule overflow:hidden; while $viewport will have a negative margin to hide its native scrollbars. This way the mousewheel, arrow keys and other means of scrolling still work like normal. $viewport will be placed inside $elem, alongside the scrollbars.

Using requestAnimationFrame(), the script will check each frame if a scrollbar or the $viewport itself needs to be updated. Therefore the size and scroll position of the $viewport will be saved to its dataset. If it changes, the scrollbars will be updated.

What have I tried?

Just putting overflow:hidden;:

  • Native scroll behavior is lost
  • It's still possible to scroll via JavaScript, but that might feel off depending on the default browser scrolling

Put scrollbar on top of the native one:

  • Maybe I want a narrow scrollbar? The native one won't be fully covered then
  • Maybe I want a transparent scrollbar? The native one would shine through then

Place absolutely positioned div above the target area:

  • Well, scrolling works, but selecting text is no longer possible
  • Adding mousedown event listener to remove the "overlay" didn't work as hoped (text selection still was dodgy)
  • Also having to deal with passing events to the actual element was too fiddly

Put the content into a nested div:

  • This is the one I settled with, as it feels the most natural, at the expense of more work to integrate it into ones website
  • Messes with the content, so if content is dynamically added, extra care must be taken
  • Eventually conflicts with existing CSS rules

Wrap the entire element into a new div:

  • a bunch of CSS rules need to be copied (margin, positon, display border, border-radius, outside box-shadow, width, max-width, ...) to the wrapper...
  • ...and then be removed from the element to not make things look weird
  • watching for CSS rule changes (via adding a class, using developer tools, ...) might be a CPU-intensive task compared to just checking the size and scroll position