├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── rollup.config.js └── src ├── index.js └── slimscroll.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories 2 | node_modules/ 3 | dist/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # svelte-slimscroll 2 | svelte-slimscroll is a action for Svelte.js, which can transforms any div into a scrollable area with a nice scrollbar. 3 | 4 | **Demo**: https://svelte.dev/repl/e8dcf07c3f3c4573a62ec289b1958005?version=3.29.4. 5 | 6 | 7 | [![Stargazers repo roster for @MelihAltintas/svelte-slimscroll](https://reporoster.com/stars/MelihAltintas/svelte-slimscroll)](https://github.com/MelihAltintas/svelte-slimscroll/stargazers) 8 | ## Install 9 | - Install it by using npm. 10 | 11 | ``` 12 | npm i svelte-slimscroll 13 | ``` 14 | 15 | - Import it at `svelte` project. 16 | 17 | ``` js 18 | import {slimscroll} from "svelte-slimscroll" 19 | ``` 20 | 21 | ## Usage 22 | 23 | Using the `slimscroll` action 24 | ``` html 25 |
26 | ... 27 |
28 | ``` 29 | 30 | Using action with options 31 | 32 | ``` html 33 |
34 | ... 35 |
36 | 39 | ``` 40 | > The `options` is same as jQuery version. See their [documentation](http://rocha.la/jQuery-slimScroll) 41 | 42 | Options 43 | ------- 44 | * **width** - width in pixels of the visible scroll area Default: auto 45 | * **height** - Height in pixels of the visible scroll area. Default: 250px 46 | * **size** - Width in pixels of the scrollbar. Default: 7px 47 | * **color** - Color in hex of the scrollbar. Default: #000 48 | * **position** - left or right. Sets the position of the scrollbar. Default: right 49 | * **distance** - distance in pixels between the side edge and the scrollbar It is used together with position property. Default:1px 50 | * **start** - default scroll position on load - top / bottom / $('selector'). Default: top. 51 | * **opacity** - sets scrollbar opacity. Default: 0.4. 52 | * **alwaysVisible** - Disables scrollbar hide. Default: false 53 | * **disableFadeOut** - Disables scrollbar auto fade. When set to true scrollbar doesn't disappear after some time when mouse is over the slimscroll div.Default: false 54 | * **railVisible** - Enables scrollbar rail. Default: false 55 | * **railColor** - Sets scrollbar rail color, Default: #333 56 | * **railOpacity** - Sets scrollbar rail opacity. Default: 0.2 57 | * **railClass** - defautlt CSS class of the slimscroll rail. Default: 'slimScrollRail' 58 | * **barClass** - defautlt CSS class of the slimscroll bar. Default: 'slimScrollBar' 59 | * **wrapperClass** - defautlt CSS class of the slimscroll wrapper. Default: 'slimScrollDiv' 60 | * **allowPageScroll** - Checks if mouse wheel should scroll page when bar reaches top or bottom of the container. When set to true is scrolls the page.Default: false 61 | * **wheelStep** - Integer value for mouse wheel delta. Default: 20 62 | * **touchScrollStep** - Allows to set different sensitivity for touch scroll events. Negative number inverts scroll direction.Default: 200 63 | * **borderRadius** - sets border radius.Default: 7px 64 | * **railBorderRadius** - sets border radius of the rail.Default: 7px 65 | 66 | ## License 67 | [MIT](http://opensource.org/licenses/MIT) 68 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-slimscroll", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "14.14.6", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz", 10 | "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", 11 | "dev": true 12 | }, 13 | "@types/resolve": { 14 | "version": "0.0.8", 15 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", 16 | "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", 17 | "dev": true, 18 | "requires": { 19 | "@types/node": "*" 20 | } 21 | }, 22 | "builtin-modules": { 23 | "version": "3.1.0", 24 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", 25 | "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", 26 | "dev": true 27 | }, 28 | "estree-walker": { 29 | "version": "0.6.1", 30 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 31 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 32 | "dev": true 33 | }, 34 | "fsevents": { 35 | "version": "2.1.3", 36 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 37 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 38 | "dev": true, 39 | "optional": true 40 | }, 41 | "function-bind": { 42 | "version": "1.1.1", 43 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 44 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 45 | "dev": true 46 | }, 47 | "has": { 48 | "version": "1.0.3", 49 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 50 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 51 | "dev": true, 52 | "requires": { 53 | "function-bind": "^1.1.1" 54 | } 55 | }, 56 | "is-core-module": { 57 | "version": "2.1.0", 58 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", 59 | "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", 60 | "dev": true, 61 | "requires": { 62 | "has": "^1.0.3" 63 | } 64 | }, 65 | "is-module": { 66 | "version": "1.0.0", 67 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 68 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 69 | "dev": true 70 | }, 71 | "path-parse": { 72 | "version": "1.0.6", 73 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 74 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 75 | "dev": true 76 | }, 77 | "require-relative": { 78 | "version": "0.8.7", 79 | "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", 80 | "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", 81 | "dev": true 82 | }, 83 | "resolve": { 84 | "version": "1.18.1", 85 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", 86 | "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", 87 | "dev": true, 88 | "requires": { 89 | "is-core-module": "^2.0.0", 90 | "path-parse": "^1.0.6" 91 | } 92 | }, 93 | "rollup": { 94 | "version": "2.33.1", 95 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.1.tgz", 96 | "integrity": "sha512-uY4O/IoL9oNW8MMcbA5hcOaz6tZTMIh7qJHx/tzIJm+n1wLoY38BLn6fuy7DhR57oNFLMbDQtDeJoFURt5933w==", 97 | "dev": true, 98 | "requires": { 99 | "fsevents": "~2.1.2" 100 | } 101 | }, 102 | "rollup-plugin-node-resolve": { 103 | "version": "5.2.0", 104 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", 105 | "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", 106 | "dev": true, 107 | "requires": { 108 | "@types/resolve": "0.0.8", 109 | "builtin-modules": "^3.1.0", 110 | "is-module": "^1.0.0", 111 | "resolve": "^1.11.1", 112 | "rollup-pluginutils": "^2.8.1" 113 | } 114 | }, 115 | "rollup-plugin-svelte": { 116 | "version": "6.1.0", 117 | "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-6.1.0.tgz", 118 | "integrity": "sha512-TX1nIZSD6ePiSdYIEfpkvR7lLnP1nsSycCVz+vXbm5d5kIe5WMldo6fwcL/T8KPjc42XDgLaRcS74BorpQvpiA==", 119 | "dev": true, 120 | "requires": { 121 | "require-relative": "^0.8.7", 122 | "rollup-pluginutils": "^2.8.2", 123 | "sourcemap-codec": "^1.4.8" 124 | } 125 | }, 126 | "rollup-pluginutils": { 127 | "version": "2.8.2", 128 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 129 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 130 | "dev": true, 131 | "requires": { 132 | "estree-walker": "^0.6.1" 133 | } 134 | }, 135 | "sourcemap-codec": { 136 | "version": "1.4.8", 137 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 138 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 139 | "dev": true 140 | }, 141 | "svelte": { 142 | "version": "3.29.4", 143 | "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.29.4.tgz", 144 | "integrity": "sha512-oW0fGHlyFFMvzRtIvOs84b0fOc0gmZNQcL5Is3hxuTpvaYX3pfd8oHy4KnOvbq4Ca6SG6AHdRMk7OhApTo0NqA==", 145 | "dev": true 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-slimscroll", 3 | "version": "1.0.6", 4 | "description": "svelte-slimscroll is a action for Svelte.js, which can transforms any div into a scrollable area with a nice scrollbar.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/MelihAltintas/svelte-slimscroll" 8 | }, 9 | "homepage": "", 10 | "bugs": { 11 | "url": "https://github.com/MelihAltintas/svelte-slimscroll/issues" 12 | }, 13 | "main": "dist/index.js", 14 | "module": "dist/index.mjs", 15 | "svelte": "src/index.js", 16 | "scripts": { 17 | "build": "rollup -c", 18 | "dev": "rollup -c -w" 19 | }, 20 | "keywords": [ 21 | "svelte-slimscroll", 22 | "svelte", 23 | "svelte-scroll" 24 | ], 25 | "author": "Melih Altıntaş", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "rollup": "^2.33.1", 29 | "rollup-plugin-node-resolve": "^5.2.0", 30 | "rollup-plugin-svelte": "^6.1.0", 31 | "svelte": "^3.29.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | 3 | const pkg = require('./package.json'); 4 | 5 | export default { 6 | input: "src/index.js", 7 | output: [ 8 | { file: pkg.module, 'format': 'en' }, 9 | { file: pkg.main, 'format': 'umd', name: 'Name' } 10 | ], 11 | plugins: [ 12 | resolve() 13 | ], 14 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export {slimscroll} from "./slimscroll.js"; -------------------------------------------------------------------------------- /src/slimscroll.js: -------------------------------------------------------------------------------- 1 | var SlimScroll = (function () { 2 | function $(el) { 3 | if (!(this instanceof $)) { 4 | return new $(el) 5 | } 6 | if (typeof el === 'string') { 7 | var r = /<(\w+)><\/\1>$/.exec(el) 8 | if (r) { 9 | el = document.createElement(r[1]) 10 | } else { 11 | el = document.querySelector(el) 12 | } 13 | } 14 | this.el = (el && el.nodeType === 1) ? el : document.documentElement 15 | return this 16 | } 17 | 18 | $.prototype = { 19 | parent: function () { 20 | return $(this.el.parentNode || this.el.parentElement) 21 | }, 22 | closest: function (selector) { 23 | if (!selector) return $(document) 24 | var parent = this.parent() 25 | 26 | while (parent.el.className != $(selector).el.className) { 27 | 28 | parent = parent.parent() 29 | } 30 | return parent 31 | }, 32 | is: function (obj) { 33 | if (this.el === obj.el) { 34 | return true 35 | } 36 | return false 37 | }, 38 | hasClass: function (className) { 39 | if (this.el.className.indexOf(className) >= 0) { 40 | return true 41 | } 42 | return false 43 | }, 44 | addClass: function (className) { 45 | if (!className || typeof className === 'undefined') return 46 | if (this.hasClass(className)) return 47 | var cls = this.el.className.split(' ') 48 | cls.push(className) 49 | this.el.className = cls.join(' ').trim() 50 | return this 51 | }, 52 | css: function (styleObj) { 53 | if (typeof styleObj === 'string') { 54 | return this.el.style[styleObj].replace('px', '') 55 | } 56 | for (var key in styleObj) { 57 | if (typeof styleObj[key] === 'number' && parseInt(styleObj[key])) styleObj[key] = parseInt(styleObj[key]) + 'px' 58 | if (key === 'zIndex') styleObj[key] = parseInt(styleObj[key]) 59 | this.el.style[key] = styleObj[key] 60 | } 61 | return this 62 | }, 63 | show: function () { 64 | this.el.style.display = 'block' 65 | }, 66 | hide: function () { 67 | this.el.style.display = 'none' 68 | }, 69 | wrap: function (obj) { 70 | this.parent().el.insertBefore(obj.el, this.el) 71 | obj.append(this) 72 | return this 73 | }, 74 | append: function (obj) { 75 | this.el.appendChild(obj.el) 76 | return this 77 | }, 78 | scrollTop: function (y) { 79 | if (typeof y !== 'undefined') { 80 | this.el.scrollTop = parseInt(y) 81 | return this 82 | } 83 | return this.el.scrollTop 84 | }, 85 | outerHeight: function () { 86 | return this.el.offsetHeight || this.el.clientHeight 87 | }, 88 | hover: function (hoverIn, hoverOut) { 89 | this.bind('mouseenter', hoverIn) 90 | this.bind('mouseleave', hoverOut) 91 | }, 92 | bind: function (type, fn, capture) { 93 | var el = this.el; 94 | 95 | if (window.addEventListener) { 96 | el.addEventListener(type, fn, capture); 97 | 98 | var ev = document.createEvent('HTMLEvents'); 99 | ev.initEvent(type, capture || false, false); 100 | // 在元素上存储创建的事件,方便自定义触发 101 | if (!el['ev' + type]) { 102 | el['ev' + type] = ev; 103 | } 104 | 105 | } else if (window.attachEvent) { 106 | el.attachEvent('on' + type, fn); 107 | if (isNaN(el['cu' + type])) { 108 | // 自定义属性,触发事件用 109 | el['cu' + type] = 0; 110 | } 111 | 112 | var fnEv = function (event) { 113 | if (event.propertyName === 'cu' + type) { 114 | fn.call(el); 115 | } 116 | }; 117 | 118 | el.attachEvent('onpropertychange', fnEv); 119 | 120 | // 在元素上存储绑定的propertychange事件,方便删除 121 | if (!el['ev' + type]) { 122 | el['ev' + type] = [fnEv]; 123 | } else { 124 | el['ev' + type].push(fnEv); 125 | } 126 | } 127 | 128 | return this; 129 | }, 130 | trigger: function (type) { 131 | var el = this.el; 132 | if (typeof type === 'string') { 133 | if (document.dispatchEvent) { 134 | if (el['ev' + type]) { 135 | el.dispatchEvent(el['ev' + type]); 136 | } 137 | } else if (document.attachEvent) { 138 | // 改变对应自定义属性,触发自定义事件 139 | el['cu' + type]++; 140 | } 141 | } 142 | return this; 143 | }, 144 | unbind: function (type, fn, capture) { 145 | var el = this.el; 146 | if (window.removeEventListener) { 147 | el.removeEventListener(type, fn, capture || false); 148 | } else if (document.attachEvent) { 149 | el.detachEvent('on' + type, fn); 150 | var arrEv = el['ev' + type]; 151 | if (arrEv instanceof Array) { 152 | for (var i = 0; i < arrEv.length; i += 1) { 153 | // 删除该方法名下所有绑定的propertychange事件 154 | el.detachEvent('onpropertychange', arrEv[i]); 155 | } 156 | } 157 | } 158 | return this; 159 | } 160 | } 161 | 162 | $.extend = function (deep) { 163 | var start = 1 164 | if (typeof deep === 'object') { 165 | start = 0 166 | } 167 | var objs = Array.prototype.slice.call(arguments, start), 168 | newObj = {} 169 | 170 | for (var i = 0; i < objs.length; i++) { 171 | if (typeof objs !== 'object') return 172 | for (var key in objs[i]) { 173 | newObj[key] = typeof objs[i][key] === 'object' && deep === true ? $.extend(true, objs[i][key]) : objs[i][key] 174 | } 175 | } 176 | return newObj 177 | } 178 | 179 | $.isPlainObject = function (obj) { 180 | return typeof obj === 'object' && !(obj instanceof Array) 181 | } 182 | 183 | function SlimScroll(el, options) { 184 | var defaults = { 185 | 186 | // width in pixels of the visible scroll area 187 | width: 'auto', 188 | 189 | // height in pixels of the visible scroll area 190 | height: '250px', 191 | 192 | // width in pixels of the scrollbar and rail 193 | size: '7px', 194 | 195 | // scrollbar color, accepts any hex/color value 196 | color: '#000', 197 | 198 | // scrollbar position - left/right 199 | position: 'right', 200 | 201 | // distance in pixels between the side edge and the scrollbar 202 | distance: '1px', 203 | 204 | // default scroll position on load - top / bottom / $('selector') 205 | start: 'top', 206 | 207 | // sets scrollbar opacity 208 | opacity: 0.4, 209 | 210 | // enables always-on mode for the scrollbar 211 | alwaysVisible: false, 212 | 213 | // check if we should hide the scrollbar when user is hovering over 214 | disableFadeOut: false, 215 | 216 | // sets visibility of the rail 217 | railVisible: false, 218 | 219 | // sets rail color 220 | railColor: '#333', 221 | 222 | // sets rail opacity 223 | railOpacity: 0.2, 224 | 225 | // whether we should use jQuery UI Draggable to enable bar dragging 226 | railDraggable: true, 227 | 228 | // defautlt CSS class of the slimscroll rail 229 | railClass: 'slimScrollRail', 230 | 231 | // defautlt CSS class of the slimscroll bar 232 | barClass: 'slimScrollBar', 233 | 234 | // defautlt CSS class of the slimscroll wrapper 235 | wrapperClass: 'slimScrollDiv', 236 | 237 | // check if mousewheel should scroll the window if we reach top/bottom 238 | allowPageScroll: false, 239 | 240 | // scroll amount applied to each mouse wheel step 241 | wheelStep: 20, 242 | 243 | // scroll amount applied when user is using gestures 244 | touchScrollStep: 200, 245 | 246 | // sets border radius 247 | borderRadius: '7px', 248 | 249 | // sets border radius of the rail 250 | railBorderRadius: '7px' 251 | }; 252 | 253 | 254 | 255 | var o = $.extend(defaults, options) 256 | 257 | // do it for every element that matches selector 258 | // this.each(function () { 259 | 260 | var isOverPanel, isOverBar, isDragg, queueHide, touchDif, 261 | barHeight, percentScroll, lastScroll, 262 | divS = '
', 263 | minBarHeight = 30, 264 | releaseScroll = false; 265 | 266 | // used in event handlers and for better minification 267 | // var me = $(this); 268 | var me = $(el); 269 | 270 | 271 | // ensure we are not binding it again 272 | if (me.parent().hasClass(o.wrapperClass)) { 273 | // start from last bar position 274 | var offset = me.scrollTop(); 275 | 276 | // find bar and rail 277 | bar = me.siblings('.' + o.barClass); 278 | rail = me.siblings('.' + o.railClass); 279 | 280 | getBarHeight(); 281 | 282 | // check if we should scroll existing instance 283 | if ($.isPlainObject(options)) { 284 | // Pass height: auto to an existing slimscroll object to force a resize after contents have changed 285 | if ('height' in options && options.height === 'auto') { 286 | me.parent().css({ height: 'auto' }); 287 | me.css({ height: 'auto' }); 288 | var height = me.parent().parent().outerHeight(); 289 | me.parent().css({ height: height }); 290 | me.css({ height: height }); 291 | } else if ('height' in options) { 292 | var h = options.height; 293 | me.parent().css({ height: h }); 294 | me.css({ height: h }); 295 | } 296 | 297 | if ('scrollTo' in options) { 298 | // jump to a static point 299 | offset = parseInt(o.scrollTo); 300 | } 301 | else if ('scrollBy' in options) { 302 | // jump by value pixels 303 | offset += parseInt(o.scrollBy); 304 | } 305 | else if ('destroy' in options) { 306 | // remove slimscroll elements 307 | bar.remove(); 308 | rail.remove(); 309 | me.unwrap(); 310 | return; 311 | } 312 | 313 | // scroll content by the given offset 314 | scrollContent(offset, false, true); 315 | } 316 | 317 | return; 318 | } else if ($.isPlainObject(options)) { 319 | if ('destroy' in options) { 320 | return; 321 | } 322 | } 323 | 324 | // optionally set height to the parent's height 325 | o.height = (o.height === 'auto') ? me.parent().outerHeight() : o.height; 326 | 327 | // wrap content 328 | var wrapper = $(divS) 329 | .addClass(o.wrapperClass) 330 | .css({ 331 | position: 'relative', 332 | overflow: 'hidden', 333 | width: o.width, 334 | height: o.height 335 | }); 336 | 337 | // update style for the div 338 | me.css({ 339 | overflow: 'hidden', 340 | width: o.width, 341 | height: o.height 342 | }); 343 | 344 | // create scrollbar rail 345 | var rail = $(divS) 346 | .addClass(o.railClass) 347 | .css({ 348 | width: o.size, 349 | height: '100%', 350 | position: 'absolute', 351 | top: 0, 352 | display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none', 353 | 'border-radius': o.railBorderRadius, 354 | background: o.railColor, 355 | opacity: o.railOpacity, 356 | zIndex: 998 357 | }); 358 | 359 | // create scrollbar 360 | var bar = $(divS) 361 | .addClass(o.barClass) 362 | .css({ 363 | background: o.color, 364 | width: o.size, 365 | position: 'absolute', 366 | top: 0, 367 | opacity: o.opacity, 368 | display: o.alwaysVisible ? 'block' : 'none', 369 | 'border-radius': o.borderRadius, 370 | BorderRadius: o.borderRadius, 371 | MozBorderRadius: o.borderRadius, 372 | WebkitBorderRadius: o.borderRadius, 373 | zIndex: 999 374 | }); 375 | 376 | // set position 377 | var posCss = (o.position === 'right') ? { right: o.distance } : { left: o.distance }; 378 | rail.css(posCss); 379 | bar.css(posCss); 380 | 381 | // wrap it 382 | me.wrap(wrapper); 383 | 384 | // append to parent div 385 | me.parent().append(bar); 386 | me.parent().append(rail); 387 | 388 | 389 | //all binding events callback 390 | var events = { 391 | touchStart: function (e, b) { 392 | 393 | if (e.touches.length) { 394 | // record where touch started 395 | touchDif = e.touches[0].pageY; 396 | } 397 | }, 398 | touchMove: function (e) { 399 | // prevent scrolling the page if necessary 400 | if (!releaseScroll) { 401 | e.preventDefault(); 402 | } 403 | if (e.touches.length) { 404 | // see how far user swiped 405 | var diff = (touchDif - e.touches[0].pageY) / o.touchScrollStep; 406 | // scroll content 407 | scrollContent(diff, true); 408 | touchDif = e.touches[0].pageY; 409 | } 410 | }, 411 | hoverIn: function () { 412 | isOverPanel = true; 413 | showBar(); 414 | hideBar(); 415 | }, 416 | hoverOut: function () { 417 | isOverPanel = false; 418 | hideBar(); 419 | }, 420 | barHoverIn: function () { 421 | isOverBar = true; 422 | }, 423 | barHoverOut: function () { 424 | isOverBar = false; 425 | }, 426 | railHoverIn: function () { 427 | showBar(); 428 | }, 429 | railHoverOut: function () { 430 | hideBar(); 431 | }, 432 | barMouseDown: function (e) { 433 | var $doc = $(document); 434 | var t = parseFloat(bar.css('top')); 435 | var pageY = e.pageY; 436 | isDragg = true; 437 | 438 | function mousemove(e) { 439 | var currTop = t + e.pageY - pageY; 440 | bar.css({ top: currTop }); 441 | scrollContent(0, currTop, false);// scroll content 442 | } 443 | 444 | function mouseup(e) { 445 | isDragg = false; hideBar(); 446 | $doc.unbind('mousemove', mousemove); 447 | $doc.unbind('mouseup', mouseup); 448 | } 449 | 450 | $doc.bind('mousemove', mousemove); 451 | 452 | $doc.bind('mouseup', mouseup); 453 | return false; 454 | }, 455 | barSelectedStart: function (e) { 456 | e.stopPropagation(); 457 | e.preventDefault(); 458 | return false; 459 | } 460 | } 461 | 462 | // make it draggable and no longer dependent on the jqueryUI 463 | if (o.railDraggable) { 464 | bar.bind('mousedown', events.barMouseDown).bind('selectstart', events.barSelectedStart); 465 | } 466 | 467 | // on rail over 468 | rail.hover(events.railHoverIn, events.railHoverOut); 469 | 470 | // on bar over 471 | bar.hover(events.barHoverIn, events.barHoverOut); 472 | 473 | // show on parent mouseover 474 | me.hover(events.hoverIn, events.hoverOut); 475 | 476 | // support for mobile 477 | me.bind('touchstart', events.touchStart); 478 | 479 | me.bind('touchmove', events.touchMove); 480 | 481 | // set up initial height 482 | getBarHeight(); 483 | 484 | // check start position 485 | if (o.start === 'bottom') { 486 | // scroll content to bottom 487 | bar.css({ top: me.outerHeight() - bar.outerHeight() }); 488 | scrollContent(0, true); 489 | } 490 | else if (o.start !== 'top') { 491 | // assume jQuery selector 492 | scrollContent($(o.start).position().top, null, true); 493 | 494 | // make sure bar stays hidden 495 | if (!o.alwaysVisible) { bar.hide(); } 496 | } 497 | 498 | // attach scroll events 499 | attachWheel(el); 500 | 501 | function _onWheel(e) { 502 | // use mouse wheel only when mouse is over 503 | if (!isOverPanel) { return; } 504 | 505 | e = e || window.event; 506 | 507 | var delta = 0; 508 | if (e.wheelDelta) { delta = -e.wheelDelta / 120; } 509 | if (e.detail) { delta = e.detail / 3; } 510 | 511 | var target = e.target || e.srcTarget || e.srcElement; 512 | if ($(target).closest('.' + o.wrapperClass).is(me.parent())) { 513 | // scroll content 514 | scrollContent(delta, true); 515 | } 516 | 517 | // stop window scroll 518 | if (e.preventDefault && !releaseScroll) { e.preventDefault(); } 519 | if (!releaseScroll) { e.returnValue = false; } 520 | } 521 | 522 | function scrollContent(y, isWheel, isJump) { 523 | releaseScroll = false; 524 | var delta = y; 525 | var maxTop = me.outerHeight() - bar.outerHeight(); 526 | 527 | if (isWheel) { 528 | // move bar with mouse wheel 529 | delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight(); 530 | 531 | // move bar, make sure it doesn't go out 532 | delta = Math.min(Math.max(delta, 0), maxTop); 533 | 534 | // if scrolling down, make sure a fractional change to the 535 | // scroll position isn't rounded away when the scrollbar's CSS is set 536 | // this flooring of delta would happened automatically when 537 | // bar.css is set below, but we floor here for clarity 538 | delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta); 539 | 540 | // scroll the scrollbar 541 | bar.css({ top: delta + 'px' }); 542 | } 543 | 544 | // calculate actual scroll amount 545 | percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight()); 546 | // delta = percentScroll * (me[0].scrollHeight - me.outerHeight()); 547 | delta = percentScroll * (me.el.scrollHeight - me.outerHeight()); 548 | 549 | if (isJump) { 550 | delta = y; 551 | // var offsetTop = delta / me[0].scrollHeight * me.outerHeight(); 552 | var offsetTop = delta / me.el.scrollHeight * me.outerHeight(); 553 | offsetTop = Math.min(Math.max(offsetTop, 0), maxTop); 554 | bar.css({ top: offsetTop + 'px' }); 555 | } 556 | 557 | // scroll content 558 | me.scrollTop(delta); 559 | 560 | // fire scrolling event 561 | me.trigger('slimscrolling', ~~delta); 562 | 563 | // ensure bar is visible 564 | showBar(); 565 | 566 | // trigger hide when scroll is stopped 567 | hideBar(); 568 | } 569 | 570 | function attachWheel(target) { 571 | if (window.addEventListener) { 572 | target.addEventListener('DOMMouseScroll', _onWheel, false); 573 | target.addEventListener('mousewheel', _onWheel, false); 574 | } 575 | else { 576 | document.attachEvent('onmousewheel', _onWheel) 577 | } 578 | } 579 | 580 | function getBarHeight() { 581 | // calculate scrollbar height and make sure it is not too small 582 | barHeight = Math.max((me.outerHeight() / me.el.scrollHeight) * me.outerHeight(), minBarHeight); 583 | bar.css({ height: barHeight + 'px' }); 584 | 585 | // hide scrollbar if content is not long enough 586 | var display = barHeight == me.outerHeight() ? 'none' : 'block'; 587 | bar.css({ display: display }); 588 | } 589 | 590 | function showBar() { 591 | // recalculate bar height 592 | getBarHeight(); 593 | clearTimeout(queueHide); 594 | 595 | // when bar reached top or bottom 596 | if (percentScroll == ~~percentScroll) { 597 | //release wheel 598 | releaseScroll = o.allowPageScroll; 599 | 600 | // publish approporiate event 601 | if (lastScroll != percentScroll) { 602 | var msg = (~~percentScroll == 0) ? 'top' : 'bottom'; 603 | me.trigger('slimscroll', msg); 604 | } 605 | } 606 | else { 607 | releaseScroll = false; 608 | } 609 | lastScroll = percentScroll; 610 | 611 | // show only when required 612 | if (barHeight >= me.outerHeight()) { 613 | //allow window scroll 614 | releaseScroll = true; 615 | return; 616 | } 617 | // bar.stop(true, true).fadeIn('fast'); 618 | bar.show() 619 | // if (o.railVisible) { rail.stop(true, true).fadeIn('fast'); } 620 | if (o.railVisible) { rail.show(); } 621 | } 622 | 623 | function hideBar() { 624 | // only hide when options allow it 625 | if (!o.alwaysVisible) { 626 | queueHide = setTimeout(function () { 627 | if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg) { 628 | // bar.fadeOut('slow'); 629 | // rail.fadeOut('slow'); 630 | bar.hide() 631 | rail.hide() 632 | } 633 | }, 1000); 634 | } 635 | } 636 | 637 | // }); 638 | 639 | function unbind() { 640 | // make it draggable and no longer dependent on the jqueryUI 641 | bar.unbind('mousedown', events.barMouseDown).unbind('selectstart', events.barSelectedStart); 642 | // on rail over 643 | rail.unbind('mouseenter', events.railHoverIn).unbind('mouseleave', events.railHoverOut); 644 | 645 | // on bar over 646 | bar.unbind('mouseenter', events.barHoverIn).unbind('mouseleave', events.barHoverOut); 647 | 648 | // show on parent mouseover 649 | me.unbind('mouseenter', events.hoverIn).unbind('mouseleave', events.hoverOut); 650 | 651 | // support for mobile 652 | me.unbind('touchstart', events.touchStart); 653 | 654 | me.unbind('touchmove', events.touchMove); 655 | } 656 | return { 657 | 658 | unbind: function () { 659 | 660 | 661 | bar.unbind('mousedown', events.barMouseDown) 662 | .unbind('mouseenter', events.barHoverIn) 663 | .unbind('mouseleave', events.barHoverOut) 664 | .unbind('selectstart', events.barSelectedStart); 665 | rail.unbind('mouseenter', events.railHoverIn) 666 | .unbind('mouseleave', events.railHoverOut); 667 | bar.unbind('mouseenter', events.barHoverIn) 668 | .unbind('mouseleave', events.barHoverOut); 669 | me.unbind('mouseenter', events.hoverIn) 670 | .unbind('mouseleave', events.hoverOut) 671 | .unbind('touchstart', events.touchStart) 672 | .unbind('touchmove', events.touchMove); 673 | bar.el.remove(); 674 | rail.el.remove(); 675 | 676 | }, 677 | 678 | } 679 | } 680 | 681 | return SlimScroll 682 | })() 683 | 684 | 685 | 686 | 687 | function unwrap(node) { 688 | if(node != undefined && node != null) 689 | node.replaceWith(...node.childNodes); 690 | } 691 | 692 | export function slimscroll(node, options) { 693 | // the node has been mounted in the DOM 694 | 695 | let slim = SlimScroll(node,options) 696 | return { 697 | update(options) { 698 | 699 | slim.unbind() 700 | unwrap(node.parentNode); 701 | slim = SlimScroll(node,options) 702 | 703 | }, 704 | 705 | destroy() { 706 | // the node has been removed from the DOM 707 | slim.unbind(); 708 | unwrap(node.parentNode); 709 | } 710 | }; 711 | } 712 | 713 | --------------------------------------------------------------------------------