How can I create a simple page vertical scroll bar without using jQuery?

Samantha J T Star picture Samantha J T Star · Dec 5, 2014 · Viewed 14.2k times · Source

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.

Answer

AdamSchuld picture AdamSchuld · Feb 23, 2015

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.