` that you want to make scrollable, and the library will turn it for you
63 |
64 | ```HTML
65 |
69 | ```
70 |
71 | ### Manual binding
72 | If you want to manually turn your div in a SimpleScrollbar, you can use the `SimpleScrollbar.initEl` method.
73 |
74 | ```HTML
75 |
76 |
77 |
81 | ```
82 |
83 | ### Dynamically added content
84 | If you use some client Framework, like Angular, Aurelia, etc - or any library that includes DOMElements dynamically in your app, and you want to use the SimpleScrollbar `ss-container` attribute, you can use the `SimpleScrollbar.initAll` method, and it will turn all the elements with that attribute in a scrollable one for you.
85 |
86 | ```Javascript
87 | var div = document.createElement('div');
88 | div.insertAdjacentHTML('afterbegin', '
');
89 | div.setAttribute('ss-container', true);
90 |
91 | var otherDiv = div.cloneNode(true);
92 | otherDiv.querySelector('span').textContent = 'Two';
93 |
94 | document.body.appendChild(div);
95 | document.body.appendChild(otherDiv);
96 |
97 | SimpleScrollbar.initAll();
98 | ```
99 |
100 |
101 | ### RTL Support
102 |
103 | Add `direction: rtl;` to your `
`'s CSS, and SimpleScrollbar will detect the direction automatically.
104 |
105 | ## Credits
106 | Inspired by yairEO's jQuery plugin ([fakescroll](https://github.com/yairEO/fakescroll))
107 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple-scrollbar",
3 | "version": "0.4.0",
4 | "description": "Very simple vanilla javascript library for creating a custom scrollbar cross-browser and cross-devices.",
5 | "main": "simple-scrollbar.js",
6 | "types": "./types.d.ts",
7 | "repository": {
8 | "type": "git",
9 | "url": "git+https://github.com/buzinas/simple-scrollbar.git"
10 | },
11 | "keywords": [
12 | "scrollbar",
13 | "custom-scrollbar",
14 | "tiny-scrollbar",
15 | "simple-scrollbar",
16 | "vanilla-js"
17 | ],
18 | "author": "Vitor Buzinaro",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/buzinas/simple-scrollbar/issues"
22 | },
23 | "homepage": "https://github.com/buzinas/simple-scrollbar#readme"
24 | }
25 |
--------------------------------------------------------------------------------
/simple-scrollbar.css:
--------------------------------------------------------------------------------
1 | .ss-wrapper {
2 | overflow: hidden;
3 | width: 100%;
4 | height: 100%;
5 | position: relative;
6 | z-index: 1;
7 | float: left;
8 | }
9 |
10 | .ss-content {
11 | height: 100%;
12 | width: calc(100% + 18px);
13 | padding: 0 0 0 0;
14 | position: relative;
15 | overflow-x: auto;
16 | overflow-y: scroll;
17 | box-sizing: border-box;
18 | }
19 |
20 | .ss-content.rtl {
21 | width: calc(100% + 18px);
22 | right: auto;
23 | }
24 |
25 | .ss-scroll {
26 | position: relative;
27 | background: rgba(0, 0, 0, 0.1);
28 | width: 9px;
29 | border-radius: 4px;
30 | top: 0;
31 | z-index: 2;
32 | cursor: pointer;
33 | opacity: 0;
34 | transition: opacity 0.25s linear;
35 | }
36 |
37 | .ss-hidden {
38 | display: none;
39 | }
40 |
41 | .ss-container:hover .ss-scroll,
42 | .ss-container:active .ss-scroll {
43 | opacity: 1;
44 | }
45 |
46 | .ss-grabbed {
47 | -o-user-select: none;
48 | -ms-user-select: none;
49 | -moz-user-select: none;
50 | -webkit-user-select: none;
51 | user-select: none;
52 | }
53 |
--------------------------------------------------------------------------------
/simple-scrollbar.js:
--------------------------------------------------------------------------------
1 | ;(function(root, factory) {
2 | if (typeof exports === 'object') {
3 | module.exports = factory(window, document)
4 | } else {
5 | root.SimpleScrollbar = factory(window, document)
6 | }
7 | })(this, function(w, d) {
8 | var raf = w.requestAnimationFrame || w.setImmediate || function(c) { return setTimeout(c, 0); };
9 |
10 | function initEl(el) {
11 | Object.defineProperty(el, 'data-simple-scrollbar', { value: new SimpleScrollbar(el), configurable: true });
12 | }
13 |
14 | function unbindEl(el) {
15 | if (!Object.prototype.hasOwnProperty.call(el, 'data-simple-scrollbar')) return;
16 | el['data-simple-scrollbar'].unBind();
17 | //Remove the elements property
18 | delete el['data-simple-scrollbar'];
19 | }
20 |
21 | // Mouse drag handler
22 | function dragDealer(el, context) {
23 | var lastPageY;
24 |
25 | el.addEventListener('mousedown', function(e) {
26 | lastPageY = e.pageY;
27 | el.classList.add('ss-grabbed');
28 | d.body.classList.add('ss-grabbed');
29 |
30 | d.addEventListener('mousemove', drag);
31 | d.addEventListener('mouseup', stop);
32 |
33 | return false;
34 | });
35 |
36 | function drag(e) {
37 | var delta = e.pageY - lastPageY;
38 | lastPageY = e.pageY;
39 |
40 | raf(function() {
41 | context.el.scrollTop += delta / context.scrollRatio;
42 | });
43 | }
44 |
45 | function stop() {
46 | el.classList.remove('ss-grabbed');
47 | d.body.classList.remove('ss-grabbed');
48 | d.removeEventListener('mousemove', drag);
49 | d.removeEventListener('mouseup', stop);
50 | }
51 | }
52 |
53 | // Constructor
54 | function ss(el) {
55 | this.target = el;
56 | this.content = el.firstElementChild;
57 |
58 | this.direction = w.getComputedStyle(this.target).direction;
59 |
60 | this.bar = '
';
61 | //Create a reference to the function binding to remove the event listeners
62 | this.mB = this.moveBar.bind(this);
63 |
64 | this.wrapper = d.createElement('div');
65 | this.wrapper.setAttribute('class', 'ss-wrapper');
66 |
67 | this.el = d.createElement('div');
68 | this.el.setAttribute('class', 'ss-content');
69 |
70 | if (this.direction === 'rtl') {
71 | this.el.classList.add('rtl');
72 | }
73 |
74 | this.wrapper.appendChild(this.el);
75 |
76 | while (this.target.firstChild) {
77 | this.el.appendChild(this.target.firstChild);
78 | }
79 | this.target.appendChild(this.wrapper);
80 |
81 | this.target.insertAdjacentHTML('beforeend', this.bar);
82 | this.bar = this.target.lastChild;
83 |
84 | dragDealer(this.bar, this);
85 | this.moveBar();
86 |
87 | w.addEventListener('resize', this.mB);
88 | this.el.addEventListener('scroll', this.mB);
89 | this.el.addEventListener('mouseenter', this.mB);
90 |
91 | this.target.classList.add('ss-container');
92 |
93 | var css = w.getComputedStyle(el);
94 | if (css['height'] === '0px' && css['max-height'] !== '0px') {
95 | el.style.height = css['max-height'];
96 | }
97 |
98 | this.unBind = function() {
99 | //Remove event listeners
100 | w.removeEventListener('resize', this.mB);
101 | this.el.removeEventListener('scroll', this.mB);
102 | this.el.removeEventListener('mouseenter', this.mB);
103 |
104 | this.target.classList.remove('ss-container');
105 |
106 | //Unwrap the initial content and remove remaining wrappers
107 | this.target.insertBefore(this.content, this.wrapper);
108 | this.target.removeChild(this.wrapper);
109 |
110 | //Remove the bar including its drag-dealer event listener
111 | this.target.removeChild(this.bar);
112 | this.bar = null; //make way for the garbage collector
113 | }
114 | }
115 |
116 | ss.prototype = {
117 | moveBar: function(e) {
118 | var totalHeight = this.el.scrollHeight,
119 | ownHeight = this.el.clientHeight,
120 | _this = this;
121 |
122 | this.scrollRatio = ownHeight / totalHeight;
123 |
124 | var isRtl = _this.direction === 'rtl';
125 | var right = isRtl ?
126 | (_this.target.clientWidth - _this.bar.clientWidth + 18) :
127 | (_this.target.clientWidth - _this.bar.clientWidth) * -1;
128 |
129 | raf(function() {
130 | // Hide scrollbar if no scrolling is possible
131 | if(_this.scrollRatio >= 1) {
132 | _this.bar.classList.add('ss-hidden')
133 | } else {
134 | _this.bar.classList.remove('ss-hidden')
135 | _this.bar.style.cssText = 'height:' + Math.max(_this.scrollRatio * 100, 10) + '%; top:' + (_this.el.scrollTop / totalHeight ) * 100 + '%;right:' + right + 'px;';
136 | }
137 | });
138 | }
139 | }
140 |
141 | function initAll() {
142 | var nodes = d.querySelectorAll('*[ss-container]');
143 |
144 | for (var i = 0; i < nodes.length; i++) {
145 | initEl(nodes[i]);
146 | }
147 | }
148 |
149 | function unbindAll() {
150 | var nodes = d.querySelectorAll('.ss-container');
151 |
152 | for (var i = 0; i < nodes.length; i++) {
153 | unbindEl(nodes[i]);
154 | }
155 | }
156 |
157 | d.addEventListener('DOMContentLoaded', initAll);
158 | ss.initEl = initEl;
159 | ss.initAll = initAll;
160 | ss.unbindEl = unbindEl;
161 | ss.unbindAll = unbindAll;
162 |
163 | var SimpleScrollbar = ss;
164 | return SimpleScrollbar;
165 | });
166 |
--------------------------------------------------------------------------------
/simple-scrollbar.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports?module.exports=e(window,document):t.SimpleScrollbar=e(window,document)}(this,function(t,e){function s(t){Object.prototype.hasOwnProperty.call(t,"data-simple-scrollbar")||Object.defineProperty(t,"data-simple-scrollbar",{value:new o(t)})}function i(t,s){function i(t){var e=t.pageY-a;a=t.pageY,n(function(){s.el.scrollTop+=e/s.scrollRatio})}function r(){t.classList.remove("ss-grabbed"),e.body.classList.remove("ss-grabbed"),e.removeEventListener("mousemove",i),e.removeEventListener("mouseup",r)}var a;t.addEventListener("mousedown",function(s){return a=s.pageY,t.classList.add("ss-grabbed"),e.body.classList.add("ss-grabbed"),e.addEventListener("mousemove",i),e.addEventListener("mouseup",r),!1})}function r(t){for(this.target=t,this.direction=window.getComputedStyle(this.target).direction,this.bar='
',this.wrapper=e.createElement("div"),this.wrapper.setAttribute("class","ss-wrapper"),this.el=e.createElement("div"),this.el.setAttribute("class","ss-content"),"rtl"===this.direction&&this.el.classList.add("rtl"),this.wrapper.appendChild(this.el);this.target.firstChild;)this.el.appendChild(this.target.firstChild);this.target.appendChild(this.wrapper),this.target.insertAdjacentHTML("beforeend",this.bar),this.bar=this.target.lastChild,i(this.bar,this),this.moveBar(),this.el.addEventListener("scroll",this.moveBar.bind(this)),this.el.addEventListener("mouseenter",this.moveBar.bind(this)),this.target.classList.add("ss-container");var s=window.getComputedStyle(t);"0px"===s.height&&"0px"!==s["max-height"]&&(t.style.height=s["max-height"])}function a(){for(var t=e.querySelectorAll("*[ss-container]"),i=0;i=1?i.bar.classList.add("ss-hidden"):(i.bar.classList.remove("ss-hidden"),i.bar.style.cssText="height:"+Math.max(100*i.scrollRatio,10)+"%; top:"+i.el.scrollTop/e*100+"%;right:"+a+"px;")})}},e.addEventListener("DOMContentLoaded",a),r.initEl=s,r.initAll=a;var o=r;return o});
2 |
--------------------------------------------------------------------------------
/types.d.ts:
--------------------------------------------------------------------------------
1 | export default interface SimpleScrollBar {
2 | initEl (element: Element): void;
3 | initAll (): void;
4 | moveBar (e: Event): void;
5 | }
6 |
--------------------------------------------------------------------------------