Custom scrollbar

by Noir Noir

HTML

<div class="container js-scroller">
  <p>text text text text text text text text tex t text text text text</p>
  <p>text text text text text text text text text text text text text</p>
  <p>text text text text text text text text text text text text text</p>
  <div class="space-block"></div>
  <p>text text text text text text text text text text text text text</p>
  <div class="space-block"></div>
  <p>text text text text text text text text text text text text text</p>
  <p>text text text text text text text text text text text text text</p>
</div>

CSS

.container {
  width: 300px;
  height: 300px;
  overflow: auto;
}
.container p {
  margin: 0;
  padding: 0;
  margin-bottom: 5px;
}
.space-block {
  height: 100px;
  background: #cc0;
}

.js-scroller-container {
  position: relative;
}
.js-scroller-native-scrollbar-hider {
  position: relative;
  overflow: hidden;
}
.js-scroller-scrollbar {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  height: 100%;
  width: 5px;
  opacity: 0.5;
  transition: opacity 0.2s ease;
}
.js-scroller-container:hover .js-scroller-scrollbar {
  opacity: 1;
}
.js-scroller-roller {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  background: #aaa;
}
.js-scroller-top-shadow,
.js-scroller-bottom-shadow {
  position: absolute;
  left: 0;
  width: 100%;
  height: 0;
  background: none;
  box-shadow: 0 0 40px 15px #fff;
}
.js-scroller-top-shadow {
  top: 0;
}
.js-scroller-bottom-shadow {
  bottom: 0;
}

JavaScript

var jsScroller = {
	init: function(target) {
  	if (!jsScroller.isNeeded(target)) {
    	jsScroller.destroy(target);
    	return true;
    }
  	jsScroller.buildHtml(target);
    jsScroller.updateRoller(target);
    jsScroller.updateShadows(target);
    jsScroller.events(target);
  },
  
  buildHtml: function(target) {
  	var container = target.closest('.'+jsScroller.containerClass);
    if (container.length) {
    	return true;
    }
    
    container = $('<div class="js-scroller-container"><div class="js-scroller-native-scrollbar-hider"></div><div class="js-scroller-scrollbar"><div class="js-scroller-roller"/></div></div></div>').appendTo(target.parent());
    $('.js-scroller-native-scrollbar-hider', container).append(target).append('<div class="js-scroller-top-shadow"></div><div class="js-scroller-bottom-shadow">');
    container.width('auto');
    container.width(target.width());
  },
  
  destroy: function(target){
  	var container = target.closest('.js-scroller-container');
    if (!container.length) {
    	return true;
    }
    
    $('.js-scroller-scrollbar', container).remove();
    container.after(target);
    container.remove();
  },
  
  events: function(target) {
  	var container = target.closest('.js-scroller-container');
    if (!container.length || container.is('.js-events-attached')) {
    	return true;
		}
    
    target.on('scroll', function(){
    	jsScroller.updateRoller(target);
      jsScroller.updateShadows(target);
    });
    
    container.addClass('js-events-attached');
  },
  
  updateRoller(target) {
  	var container = target.closest('.js-scroller-container'),
    		roller = $('.js-scroller-roller', container);
    if (!roller.length) {
    	return false;
    }
    
    roller.height(jsScroller.getRollerHeight(target));
    roller.css('top', jsScroller.getRollerPosition(target, roller));
  },
  
  updateShadows(target) {
  	var container = target.closest('.js-scroller-container'),
    		roller = $('.js-scroller-roller',...