16

I have looked at I think all the scrollbars code but have not yet been able to find a simple one that does not use jQuery or a somewhat complex library.

Has anyone created there own simple scrollbar using just Javascript? What I am looking for is an example of how this can be done. In particular I have a simple Bootstrap web page with:

<body>
   <header> ....</header>
   <main> ......</main>
</body>

What I would like to do is to be able to have a small in page scroll bar appear to the right of the <main> area if the content there is larger than will fit on a single page. For styling purposes I would like this not to be the browser default scroll bar.

Here's an example of what I am looking for but this one does use jQuery so I cannot use it on my site:

http://manos.malihu.gr/repository/custom-scrollbar/demo/examples/complete_examples.html

I am looking for some way to do this using Javascript in a modern browser IE9 and above. As I think this would be useful for many people I have opened up a bounty of 200 for this in the hope that someone could provide a good example of a draggable page scrollbar that also would respond to the mousewheel when over the page content area.

Just an update. I am not looking for a mobile solution for this. I am just looking for a solution that would work inside a PC / Mac browser. The site is not set up or suitable for a phone. It's possible to use on an IPad / tablet but for those needs I would like to be able to have the scrollbar default to use just the normal tablet scrolling method.

7
  • 1
    Some browsers allow you to style the native scrollbar. I don't recommend using a custom scrollbar because if something breaks in your JS code, users won't be able to scroll.
    – Oriol
    Commented Feb 22, 2015 at 18:02
  • 1
    Would noraesae/perfect-scrollbar work for you? DEMO. It specifies "perfect-scrollbar is minimalistic but perfect (for me, and maybe for most developers) scrollbar plugin working with jQuery or vanilla JavaScript as well."
    – h2ooooooo
    Commented Feb 22, 2015 at 18:03
  • You could make your own scrollbar with js using events and manually setting the elements position and scrolling the content, that wouldn't be that hard, but I guess there must be some better solution.
    – Mykybo
    Commented Feb 22, 2015 at 18:04
  • 1
    Expanding on @Oriol 's point, I think libraries are tending not to incorporate this kind of thing anymore - either adopting a stance of, "it's not good UX design to deviate from native UI controls", or simply that it's an absolute ball-ache to maintain cross-browser - particularly on mobile devices where the gestures for interaction are different (pinch/zoom etc).
    – Emissary
    Commented Feb 22, 2015 at 18:11
  • I am not sure about perfect-scrollbar as when I go to the page the title is: perfect-scrollbar - Tiny but perfect jQuery scrollbar plugin. Commented Feb 22, 2015 at 18:15

3 Answers 3

25
+200

Surprisingly, there is not a great, simple solution out there using vanilla JavaScript. I made a pure JS lightweight, minimal cross browser solution. Adjust to your own needs and aesthetics

*Update 2019
The W3C has a working document that is on standards track to improve this out of the box through the css specification. Although limiting, if you want to use cutting edge css you can now style your scroll bars through the scrollbar-color and scrollbar-width. For more information please check https://drafts.csswg.org/css-scrollbars/

Examples:
Here is the fiddle and CodePen

HTML

<body>
    <div id="main" class="scrollable">
        <div class="content-wrapper">
            <div class="content">
                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt accusamus maxime voluptatem quasi. Recusandae optio nobis ratione iste consectetur consequatur cupiditate saepe laborum natus neque a provident eum explicabo delectus qui accusantium nostrum reiciendis soluta hic ut at sed laboriosam possimus repudiandae deserunt velit rerum. Aliquam ratione itaque corrupti aperiam quisquam unde aspernatur odio id repellendus corporis eaque expedita in ab minus possimus! Quo tempore consequatur repellat consectetur nemo molestiae perferendis ipsum esse nesciunt blanditiis nobis dicta? Laudantium quaerat inventore deleniti exercitationem explicabo quos pariatur sunt earum labore sed eius blanditiis architecto consequuntur ad consectetur unde sapiente nisi. Sunt eos.</p>
                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt accusamus maxime voluptatem quasi. Recusandae optio nobis ratione iste consectetur consequatur cupiditate saepe laborum natus neque a provident eum explicabo delectus qui accusantium nostrum reiciendis soluta hic ut at sed laboriosam possimus repudiandae deserunt velit rerum. Aliquam ratione itaque corrupti aperiam quisquam unde aspernatur odio id repellendus corporis eaque expedita in ab minus possimus! Quo tempore consequatur repellat consectetur nemo molestiae perferendis ipsum esse nesciunt blanditiis nobis dicta? Laudantium quaerat inventore deleniti exercitationem explicabo quos pariatur sunt earum labore sed eius blanditiis architecto consequuntur ad consectetur unde sapiente nisi. Sunt eos.</p>
            </div>
        </div>
    </div>
    <div>Not special and not contained within scrolling</div>
</body>

CSS

.scrollable {
    padding: 0% 10%;
    position: relative;
    border: 1px solid gray;
    overflow: hidden;
    height: 400px;
}

.scrollable.showScroll::after {
    position: absolute;
    content: '';
    top: 5%;
    right: 7px;
    height: 90%;
    width: 3px;
    background: rgba(224, 224, 255, .3);
}

.scrollable .content-wrapper {
    width: 100%;
    height: 100%;
    padding-right: 50%;
    overflow-y: scroll;
}
.scroller {
    z-index: 5;
    cursor: pointer;
    position: absolute;
    width: 10px;
    border-radius: 5px;
    background: rgb(111, 111, 190);
    top: 0px;
    right: 3px;
    -webkit-transition: top .08s;
    -moz-transition: top .08s;
    -ms-transition: top .08s;
    -o-transition: top .08s;
    transition: top .08s;
}
.content {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

JS

(function () {

var scrollContainer = document.querySelector('.scrollable'),
    scrollContentWrapper = document.querySelector('.scrollable .content-wrapper'),
    scrollContent = document.querySelector('.scrollable .content'),
    contentPosition = 0,
    scrollerBeingDragged = false,
    scroller,
    topPosition,
    scrollerHeight;

function calculateScrollerHeight() {
    // *Calculation of how tall scroller should be
    var visibleRatio = scrollContainer.offsetHeight / scrollContentWrapper.scrollHeight;
    return visibleRatio * scrollContainer.offsetHeight;
}

function moveScroller(evt) {
    // Move Scroll bar to top offset
    var scrollPercentage = evt.target.scrollTop / scrollContentWrapper.scrollHeight;
    topPosition = scrollPercentage * (scrollContainer.offsetHeight - 5); // 5px arbitrary offset so scroll bar doesn't move too far beyond content wrapper bounding box
    scroller.style.top = topPosition + 'px';
}

function startDrag(evt) {
    normalizedPosition = evt.pageY;
    contentPosition = scrollContentWrapper.scrollTop;
    scrollerBeingDragged = true;
}

function stopDrag(evt) {
    scrollerBeingDragged = false;
}

function scrollBarScroll(evt) {
    if (scrollerBeingDragged === true) {
        var mouseDifferential = evt.pageY - normalizedPosition;
        var scrollEquivalent = mouseDifferential * (scrollContentWrapper.scrollHeight / scrollContainer.offsetHeight);
        scrollContentWrapper.scrollTop = contentPosition + scrollEquivalent;
    }
}

function createScroller() {
    // *Creates scroller element and appends to '.scrollable' div
    // create scroller element
    scroller = document.createElement("div");
    scroller.className = 'scroller';

    // determine how big scroller should be based on content
    scrollerHeight = calculateScrollerHeight();

    if (scrollerHeight / scrollContainer.offsetHeight < 1){
        // *If there is a need to have scroll bar based on content size
        scroller.style.height = scrollerHeight + 'px';

        // append scroller to scrollContainer div
        scrollContainer.appendChild(scroller);

        // show scroll path divot
        scrollContainer.className += ' showScroll';

        // attach related draggable listeners
        scroller.addEventListener('mousedown', startDrag);
        window.addEventListener('mouseup', stopDrag);
        window.addEventListener('mousemove', scrollBarScroll)
    }

}

createScroller();


// *** Listeners ***
scrollContentWrapper.addEventListener('scroll', moveScroller);
}());

The concept is simple. We have a main div with a 'scrollable' class. The JavaScript recognizes this element and appends a scroller div that you can style yourself with CSS. By nesting the content-wrapper child div we can effectively push the native scroller outside of the parent div while still controlling padding.

Here is a diagram: Scroll Diagram

The reason we need to maintain native scrolling ability is because the JavaScript scroll event only fires on elements that have overflow set to scroll. See MDN reference on scroll. The benefit being that if JS is disabled, we will still gracefully fallback to scrolling without a scrollbar.


NOTE
You should note that you will have to adjust your version to recalculate the scroller size in certain cases:
1.) Where the screen is resized or
2.) If more content is appended.

Secondly, modifications will have to be made if you have multiple 'scrollable' elements. In this case, you will need to loop over those elements to both append a scroller div and listen for the scroll event.

9
  • AdamSchuld - Thank you very much for taking the time to help me with my problem. I tried the fiddle in IE and Chrome but I cannot see a scroll bar appear. When you have some time can you check this out. Maybe there is something I am missing. I noticed you have a web site for your work. Maybe would it be possible to add a sample page to this site to show the scroll. Just something like designgumption.com/scroll.html It's just a thought as sometimes I find it difficult to see the results of fiddles in the small window that they use. Thanks again Commented Feb 23, 2015 at 7:06
  • 1
    In the Codepen ( codepen.io/ElGrecode/pen/LEmbBE ) I took out the flashy animation effect, maybe it was a css property causing the problems. Other than IE, this is cross browser tested.
    – AdamSchuld
    Commented Feb 23, 2015 at 7:45
  • Thank you. I looked again at your jsfiddle implementation and it does work when I move the mouse wheel or cursor and then goes out of view again. This confused me as I was expecting to see the scollbar all the time. So your codepen version is I think better. When I look at codepen I see a scroll that they use which is really nice. Do you think you would be able to add to your implementation so it allows a user to also drag the scrollbar like with the scroll bars in codepen? Commented Feb 23, 2015 at 9:28
  • Actually just mouse drag is needed. I would like to have an alternate implementation (not part of this question) for touch which might just be to use the conventional scrolling for that. With your current implementation I think drag is really important. Thanks Commented Feb 23, 2015 at 10:31
  • Updated with draggable scroll. Pretty happy with the solution. A little polish and this could be the defacto non-jquery scroll bar
    – AdamSchuld
    Commented Feb 23, 2015 at 20:47
3

I know that there is already an answer to this question, but as soon as the OP wanted a simple vanilla javascript solution, that works on modern browsers - and I think that more people would probably need that too, I'll be late answering this question here, because I've developed a very simple and lightweight library in pure vanilla JS (DEMO), and it's only 1KB after gzipping and minification.

It uses the native scroll, so it works on all the modern browsers (Firefox, Chrome, Opera, Safari, Edge), and on IE10 and IE11. You can also use it in IE9 by including a classList polyfill.

All Android, iOS and Windows Phone browsers are supported either.

See a more detailed answer here: Custom scrollbars

Or read the full tutorial in the SimpleScrollbar's Github README page.

2

There is an awesome baron.js library. It can be user with jQuery or without it as well. The idea is exactly the same that @AdamSchuld describe in his answer.

Its advantages:

  • Doesn't replace native system scroll mechanic (it is important when you want your scrollbar to look customized but save original platform or device behavior).

  • Customizable scrollbar design with full CSS support.

  • No strong dependencies on jQuery.

  • Plugin system (fixable headers, sticky footer, autotests and more)

  • Can be inited on hidden blocks

Here is simple demo of it.

Not the answer you're looking for? Browse other questions tagged or ask your own question.