jquery.slimscroll.js (13832B)
1 /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la) 2 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 3 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 4 * 5 * Version: 1.3.8 6 * 7 */ 8 (function($) { 9 10 $.fn.extend({ 11 slimScroll: function(options) { 12 13 var defaults = { 14 15 // width in pixels of the visible scroll area 16 width : 'auto', 17 18 // height in pixels of the visible scroll area 19 height : '250px', 20 21 // width in pixels of the scrollbar and rail 22 size : '7px', 23 24 // scrollbar color, accepts any hex/color value 25 color: '#000', 26 27 // scrollbar position - left/right 28 position : 'right', 29 30 // distance in pixels between the side edge and the scrollbar 31 distance : '1px', 32 33 // default scroll position on load - top / bottom / $('selector') 34 start : 'top', 35 36 // sets scrollbar opacity 37 opacity : .4, 38 39 // enables always-on mode for the scrollbar 40 alwaysVisible : false, 41 42 // check if we should hide the scrollbar when user is hovering over 43 disableFadeOut : false, 44 45 // sets visibility of the rail 46 railVisible : false, 47 48 // sets rail color 49 railColor : '#333', 50 51 // sets rail opacity 52 railOpacity : .2, 53 54 // whether we should use jQuery UI Draggable to enable bar dragging 55 railDraggable : true, 56 57 // defautlt CSS class of the slimscroll rail 58 railClass : 'slimScrollRail', 59 60 // defautlt CSS class of the slimscroll bar 61 barClass : 'slimScrollBar', 62 63 // defautlt CSS class of the slimscroll wrapper 64 wrapperClass : 'slimScrollDiv', 65 66 // check if mousewheel should scroll the window if we reach top/bottom 67 allowPageScroll : false, 68 69 // scroll amount applied to each mouse wheel step 70 wheelStep : 20, 71 72 // scroll amount applied when user is using gestures 73 touchScrollStep : 200, 74 75 // sets border radius 76 borderRadius: '7px', 77 78 // sets border radius of the rail 79 railBorderRadius : '7px' 80 }; 81 82 var o = $.extend(defaults, options); 83 84 // do it for every element that matches selector 85 this.each(function(){ 86 87 var isOverPanel, isOverBar, isDragg, queueHide, touchDif, 88 barHeight, percentScroll, lastScroll, 89 divS = '<div></div>', 90 minBarHeight = 30, 91 releaseScroll = false; 92 93 // used in event handlers and for better minification 94 var me = $(this); 95 96 // ensure we are not binding it again 97 if (me.parent().hasClass(o.wrapperClass)) 98 { 99 // start from last bar position 100 var offset = me.scrollTop(); 101 102 // find bar and rail 103 bar = me.siblings('.' + o.barClass); 104 rail = me.siblings('.' + o.railClass); 105 106 getBarHeight(); 107 108 // check if we should scroll existing instance 109 if ($.isPlainObject(options)) 110 { 111 // Pass height: auto to an existing slimscroll object to force a resize after contents have changed 112 if ( 'height' in options && options.height == 'auto' ) { 113 me.parent().css('height', 'auto'); 114 me.css('height', 'auto'); 115 var height = me.parent().parent().height(); 116 me.parent().css('height', height); 117 me.css('height', height); 118 } else if ('height' in options) { 119 var h = options.height; 120 me.parent().css('height', h); 121 me.css('height', h); 122 } 123 124 if ('scrollTo' in options) 125 { 126 // jump to a static point 127 offset = parseInt(o.scrollTo); 128 } 129 else if ('scrollBy' in options) 130 { 131 // jump by value pixels 132 offset += parseInt(o.scrollBy); 133 } 134 else if ('destroy' in options) 135 { 136 // remove slimscroll elements 137 bar.remove(); 138 rail.remove(); 139 me.unwrap(); 140 return; 141 } 142 143 // scroll content by the given offset 144 scrollContent(offset, false, true); 145 } 146 147 return; 148 } 149 else if ($.isPlainObject(options)) 150 { 151 if ('destroy' in options) 152 { 153 return; 154 } 155 } 156 157 // optionally set height to the parent's height 158 o.height = (o.height == 'auto') ? me.parent().height() : o.height; 159 160 // wrap content 161 var wrapper = $(divS) 162 .addClass(o.wrapperClass) 163 .css({ 164 position: 'relative', 165 overflow: 'hidden', 166 width: o.width, 167 height: o.height 168 }); 169 170 // update style for the div 171 me.css({ 172 overflow: 'hidden', 173 width: o.width, 174 height: o.height 175 }); 176 177 // create scrollbar rail 178 var rail = $(divS) 179 .addClass(o.railClass) 180 .css({ 181 width: o.size, 182 height: '100%', 183 position: 'absolute', 184 top: 0, 185 display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none', 186 'border-radius': o.railBorderRadius, 187 background: o.railColor, 188 opacity: o.railOpacity, 189 zIndex: 90 190 }); 191 192 // create scrollbar 193 var bar = $(divS) 194 .addClass(o.barClass) 195 .css({ 196 background: o.color, 197 width: o.size, 198 position: 'absolute', 199 top: 0, 200 opacity: o.opacity, 201 display: o.alwaysVisible ? 'block' : 'none', 202 'border-radius' : o.borderRadius, 203 BorderRadius: o.borderRadius, 204 MozBorderRadius: o.borderRadius, 205 WebkitBorderRadius: o.borderRadius, 206 zIndex: 99 207 }); 208 209 // set position 210 var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance }; 211 rail.css(posCss); 212 bar.css(posCss); 213 214 // wrap it 215 me.wrap(wrapper); 216 217 // append to parent div 218 me.parent().append(bar); 219 me.parent().append(rail); 220 221 // make it draggable and no longer dependent on the jqueryUI 222 if (o.railDraggable){ 223 bar.bind("mousedown", function(e) { 224 var $doc = $(document); 225 isDragg = true; 226 t = parseFloat(bar.css('top')); 227 pageY = e.pageY; 228 229 $doc.bind("mousemove.slimscroll", function(e){ 230 currTop = t + e.pageY - pageY; 231 bar.css('top', currTop); 232 scrollContent(0, bar.position().top, false);// scroll content 233 }); 234 235 $doc.bind("mouseup.slimscroll", function(e) { 236 isDragg = false;hideBar(); 237 $doc.unbind('.slimscroll'); 238 }); 239 return false; 240 }).bind("selectstart.slimscroll", function(e){ 241 e.stopPropagation(); 242 e.preventDefault(); 243 return false; 244 }); 245 } 246 247 // on rail over 248 rail.hover(function(){ 249 showBar(); 250 }, function(){ 251 hideBar(); 252 }); 253 254 // on bar over 255 bar.hover(function(){ 256 isOverBar = true; 257 }, function(){ 258 isOverBar = false; 259 }); 260 261 // show on parent mouseover 262 me.hover(function(){ 263 isOverPanel = true; 264 showBar(); 265 hideBar(); 266 }, function(){ 267 isOverPanel = false; 268 hideBar(); 269 }); 270 271 // support for mobile 272 me.bind('touchstart', function(e,b){ 273 if (e.originalEvent.touches.length) 274 { 275 // record where touch started 276 touchDif = e.originalEvent.touches[0].pageY; 277 } 278 }); 279 280 me.bind('touchmove', function(e){ 281 // prevent scrolling the page if necessary 282 if(!releaseScroll) 283 { 284 e.originalEvent.preventDefault(); 285 } 286 if (e.originalEvent.touches.length) 287 { 288 // see how far user swiped 289 var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep; 290 // scroll content 291 scrollContent(diff, true); 292 touchDif = e.originalEvent.touches[0].pageY; 293 } 294 }); 295 296 // set up initial height 297 getBarHeight(); 298 299 // check start position 300 if (o.start === 'bottom') 301 { 302 // scroll content to bottom 303 bar.css({ top: me.outerHeight() - bar.outerHeight() }); 304 scrollContent(0, true); 305 } 306 else if (o.start !== 'top') 307 { 308 // assume jQuery selector 309 scrollContent($(o.start).position().top, null, true); 310 311 // make sure bar stays hidden 312 if (!o.alwaysVisible) { bar.hide(); } 313 } 314 315 // attach scroll events 316 attachWheel(this); 317 318 function _onWheel(e) 319 { 320 // use mouse wheel only when mouse is over 321 if (!isOverPanel) { return; } 322 323 var e = e || window.event; 324 325 var delta = 0; 326 if (e.wheelDelta) { delta = -e.wheelDelta/120; } 327 if (e.detail) { delta = e.detail / 3; } 328 329 var target = e.target || e.srcTarget || e.srcElement; 330 if ($(target).closest('.' + o.wrapperClass).is(me.parent())) { 331 // scroll content 332 scrollContent(delta, true); 333 } 334 335 // stop window scroll 336 if (e.preventDefault && !releaseScroll) { e.preventDefault(); } 337 if (!releaseScroll) { e.returnValue = false; } 338 } 339 340 function scrollContent(y, isWheel, isJump) 341 { 342 releaseScroll = false; 343 var delta = y; 344 var maxTop = me.outerHeight() - bar.outerHeight(); 345 346 if (isWheel) 347 { 348 // move bar with mouse wheel 349 delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight(); 350 351 // move bar, make sure it doesn't go out 352 delta = Math.min(Math.max(delta, 0), maxTop); 353 354 // if scrolling down, make sure a fractional change to the 355 // scroll position isn't rounded away when the scrollbar's CSS is set 356 // this flooring of delta would happened automatically when 357 // bar.css is set below, but we floor here for clarity 358 delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta); 359 360 // scroll the scrollbar 361 bar.css({ top: delta + 'px' }); 362 } 363 364 // calculate actual scroll amount 365 percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight()); 366 delta = percentScroll * (me[0].scrollHeight - me.outerHeight()); 367 368 if (isJump) 369 { 370 delta = y; 371 var offsetTop = delta / me[0].scrollHeight * me.outerHeight(); 372 offsetTop = Math.min(Math.max(offsetTop, 0), maxTop); 373 bar.css({ top: offsetTop + 'px' }); 374 } 375 376 // scroll content 377 me.scrollTop(delta); 378 379 // fire scrolling event 380 me.trigger('slimscrolling', ~~delta); 381 382 // ensure bar is visible 383 showBar(); 384 385 // trigger hide when scroll is stopped 386 hideBar(); 387 } 388 389 function attachWheel(target) 390 { 391 if (window.addEventListener) 392 { 393 target.addEventListener('DOMMouseScroll', _onWheel, false ); 394 target.addEventListener('mousewheel', _onWheel, false ); 395 } 396 else 397 { 398 document.attachEvent("onmousewheel", _onWheel) 399 } 400 } 401 402 function getBarHeight() 403 { 404 // calculate scrollbar height and make sure it is not too small 405 barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight); 406 bar.css({ height: barHeight + 'px' }); 407 408 // hide scrollbar if content is not long enough 409 var display = barHeight == me.outerHeight() ? 'none' : 'block'; 410 bar.css({ display: display }); 411 } 412 413 function showBar() 414 { 415 // recalculate bar height 416 getBarHeight(); 417 clearTimeout(queueHide); 418 419 // when bar reached top or bottom 420 if (percentScroll == ~~percentScroll) 421 { 422 //release wheel 423 releaseScroll = o.allowPageScroll; 424 425 // publish approporiate event 426 if (lastScroll != percentScroll) 427 { 428 var msg = (~~percentScroll == 0) ? 'top' : 'bottom'; 429 me.trigger('slimscroll', msg); 430 } 431 } 432 else 433 { 434 releaseScroll = false; 435 } 436 lastScroll = percentScroll; 437 438 // show only when required 439 if(barHeight >= me.outerHeight()) { 440 //allow window scroll 441 releaseScroll = true; 442 return; 443 } 444 bar.stop(true,true).fadeIn('fast'); 445 if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); } 446 } 447 448 function hideBar() 449 { 450 // only hide when options allow it 451 if (!o.alwaysVisible) 452 { 453 queueHide = setTimeout(function(){ 454 if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg) 455 { 456 bar.fadeOut('slow'); 457 rail.fadeOut('slow'); 458 } 459 }, 1000); 460 } 461 } 462 463 }); 464 465 // maintain chainability 466 return this; 467 } 468 }); 469 470 $.fn.extend({ 471 slimscroll: $.fn.slimScroll 472 }); 473 474 })(jQuery);