How can I create a simple page vertical scroll bar without using jQuery?
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:
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.
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.
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.