jquery.slimscroll.js 14 KB

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