jquery.slimscroll.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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.3
  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.parent().find('.' + o.barClass);
  74. rail = me.parent().find('.' + 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. }
  87. if ('scrollTo' in options)
  88. {
  89. // jump to a static point
  90. offset = parseInt(o.scrollTo);
  91. }
  92. else if ('scrollBy' in options)
  93. {
  94. // jump by value pixels
  95. offset += parseInt(o.scrollBy);
  96. }
  97. else if ('destroy' in options)
  98. {
  99. // remove slimscroll elements
  100. bar.remove();
  101. rail.remove();
  102. me.unwrap();
  103. return;
  104. }
  105. // scroll content by the given offset
  106. scrollContent(offset, false, true);
  107. }
  108. return;
  109. }
  110. else if ($.isPlainObject(options))
  111. {
  112. if ('destroy' in options)
  113. {
  114. return;
  115. }
  116. }
  117. // optionally set height to the parent's height
  118. o.height = (o.height == 'auto') ? me.parent().height() : o.height;
  119. // wrap content
  120. var wrapper = $(divS)
  121. .addClass(o.wrapperClass)
  122. .css({
  123. position: 'relative',
  124. overflow: 'hidden',
  125. width: o.width,
  126. height: o.height
  127. });
  128. // update style for the div
  129. me.css({
  130. overflow: 'hidden',
  131. width: o.width,
  132. height: o.height,
  133. //Fix for IE10
  134. "-ms-touch-action": "none"
  135. });
  136. // create scrollbar rail
  137. var rail = $(divS)
  138. .addClass(o.railClass)
  139. .css({
  140. width: o.size,
  141. height: '100%',
  142. position: 'absolute',
  143. top: 0,
  144. display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none',
  145. 'border-radius': o.railBorderRadius,
  146. background: o.railColor,
  147. opacity: o.railOpacity,
  148. zIndex: 90
  149. });
  150. // create scrollbar
  151. var bar = $(divS)
  152. .addClass(o.barClass)
  153. .css({
  154. background: o.color,
  155. width: o.size,
  156. position: 'absolute',
  157. top: 0,
  158. opacity: o.opacity,
  159. display: o.alwaysVisible ? 'block' : 'none',
  160. 'border-radius': o.borderRadius,
  161. BorderRadius: o.borderRadius,
  162. MozBorderRadius: o.borderRadius,
  163. WebkitBorderRadius: o.borderRadius,
  164. zIndex: 99
  165. });
  166. // set position
  167. var posCss = (o.position == 'right') ? {right: o.distance} : {left: o.distance};
  168. rail.css(posCss);
  169. bar.css(posCss);
  170. // wrap it
  171. me.wrap(wrapper);
  172. // append to parent div
  173. me.parent().append(bar);
  174. me.parent().append(rail);
  175. // make it draggable and no longer dependent on the jqueryUI
  176. if (o.railDraggable) {
  177. bar.bind("mousedown", function (e) {
  178. var $doc = $(document);
  179. isDragg = true;
  180. t = parseFloat(bar.css('top'));
  181. pageY = e.pageY;
  182. $doc.bind("mousemove.slimscroll", function (e) {
  183. currTop = t + e.pageY - pageY;
  184. bar.css('top', currTop);
  185. scrollContent(0, bar.position().top, false);// scroll content
  186. });
  187. $doc.bind("mouseup.slimscroll", function (e) {
  188. isDragg = false;
  189. hideBar();
  190. $doc.unbind('.slimscroll');
  191. });
  192. return false;
  193. }).bind("selectstart.slimscroll", function (e) {
  194. e.stopPropagation();
  195. e.preventDefault();
  196. return false;
  197. });
  198. }
  199. // on rail over
  200. rail.hover(function () {
  201. showBar();
  202. }, function () {
  203. hideBar();
  204. });
  205. // on bar over
  206. bar.hover(function () {
  207. isOverBar = true;
  208. }, function () {
  209. isOverBar = false;
  210. });
  211. // show on parent mouseover
  212. me.hover(function () {
  213. isOverPanel = true;
  214. showBar();
  215. hideBar();
  216. }, function () {
  217. isOverPanel = false;
  218. hideBar();
  219. });
  220. if (window.navigator.msPointerEnabled) {
  221. // support for mobile
  222. me.bind('MSPointerDown', function (e, b) {
  223. if (e.originalEvent.targetTouches.length)
  224. {
  225. // record where touch started
  226. touchDif = e.originalEvent.targetTouches[0].pageY;
  227. }
  228. });
  229. me.bind('MSPointerMove', function (e) {
  230. // prevent scrolling the page if necessary
  231. e.originalEvent.preventDefault();
  232. if (e.originalEvent.targetTouches.length)
  233. {
  234. // see how far user swiped
  235. var diff = (touchDif - e.originalEvent.targetTouches[0].pageY) / o.touchScrollStep;
  236. // scroll content
  237. scrollContent(diff, true);
  238. touchDif = e.originalEvent.targetTouches[0].pageY;
  239. }
  240. });
  241. } else {
  242. // support for mobile
  243. me.bind('touchstart', function (e, b) {
  244. if (e.originalEvent.touches.length)
  245. {
  246. // record where touch started
  247. touchDif = e.originalEvent.touches[0].pageY;
  248. }
  249. });
  250. me.bind('touchmove', function (e) {
  251. // prevent scrolling the page if necessary
  252. if (!releaseScroll)
  253. {
  254. e.originalEvent.preventDefault();
  255. }
  256. if (e.originalEvent.touches.length)
  257. {
  258. // see how far user swiped
  259. var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep;
  260. // scroll content
  261. scrollContent(diff, true);
  262. touchDif = e.originalEvent.touches[0].pageY;
  263. }
  264. });
  265. }
  266. // set up initial height
  267. getBarHeight();
  268. // check start position
  269. if (o.start === 'bottom')
  270. {
  271. // scroll content to bottom
  272. bar.css({top: me.outerHeight() - bar.outerHeight()});
  273. scrollContent(0, true);
  274. }
  275. else if (o.start !== 'top')
  276. {
  277. // assume jQuery selector
  278. scrollContent($(o.start).position().top, null, true);
  279. // make sure bar stays hidden
  280. if (!o.alwaysVisible) {
  281. bar.hide();
  282. }
  283. }
  284. // attach scroll events
  285. attachWheel();
  286. function _onWheel(e)
  287. {
  288. // use mouse wheel only when mouse is over
  289. if (!isOverPanel) {
  290. return;
  291. }
  292. var e = e || window.event;
  293. var delta = 0;
  294. if (e.wheelDelta) {
  295. delta = -e.wheelDelta / 120;
  296. }
  297. if (e.detail) {
  298. delta = e.detail / 3;
  299. }
  300. var target = e.target || e.srcTarget || e.srcElement;
  301. if ($(target).closest('.' + o.wrapperClass).is(me.parent())) {
  302. // scroll content
  303. scrollContent(delta, true);
  304. }
  305. // stop window scroll
  306. if (e.preventDefault && !releaseScroll) {
  307. e.preventDefault();
  308. }
  309. if (!releaseScroll) {
  310. e.returnValue = false;
  311. }
  312. }
  313. function scrollContent(y, isWheel, isJump)
  314. {
  315. releaseScroll = false;
  316. var delta = y;
  317. var maxTop = me.outerHeight() - bar.outerHeight();
  318. if (isWheel)
  319. {
  320. // move bar with mouse wheel
  321. delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight();
  322. // move bar, make sure it doesn't go out
  323. delta = Math.min(Math.max(delta, 0), maxTop);
  324. // if scrolling down, make sure a fractional change to the
  325. // scroll position isn't rounded away when the scrollbar's CSS is set
  326. // this flooring of delta would happened automatically when
  327. // bar.css is set below, but we floor here for clarity
  328. delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta);
  329. // scroll the scrollbar
  330. bar.css({top: delta + 'px'});
  331. }
  332. // calculate actual scroll amount
  333. percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight());
  334. delta = percentScroll * (me[0].scrollHeight - me.outerHeight());
  335. if (isJump)
  336. {
  337. delta = y;
  338. var offsetTop = delta / me[0].scrollHeight * me.outerHeight();
  339. offsetTop = Math.min(Math.max(offsetTop, 0), maxTop);
  340. bar.css({top: offsetTop + 'px'});
  341. }
  342. // scroll content
  343. me.scrollTop(delta);
  344. // fire scrolling event
  345. me.trigger('slimscrolling', ~~delta);
  346. // ensure bar is visible
  347. showBar();
  348. // trigger hide when scroll is stopped
  349. hideBar();
  350. }
  351. function attachWheel()
  352. {
  353. if (window.addEventListener)
  354. {
  355. this.addEventListener('DOMMouseScroll', _onWheel, false);
  356. this.addEventListener('mousewheel', _onWheel, false);
  357. }
  358. else
  359. {
  360. document.attachEvent("onmousewheel", _onWheel)
  361. }
  362. }
  363. function getBarHeight()
  364. {
  365. // calculate scrollbar height and make sure it is not too small
  366. barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight);
  367. bar.css({height: barHeight + 'px'});
  368. // hide scrollbar if content is not long enough
  369. var display = barHeight == me.outerHeight() ? 'none' : 'block';
  370. bar.css({display: display});
  371. }
  372. function showBar()
  373. {
  374. // recalculate bar height
  375. getBarHeight();
  376. clearTimeout(queueHide);
  377. // when bar reached top or bottom
  378. if (percentScroll == ~~percentScroll)
  379. {
  380. //release wheel
  381. releaseScroll = o.allowPageScroll;
  382. // publish approporiate event
  383. if (lastScroll != percentScroll)
  384. {
  385. var msg = (~~percentScroll == 0) ? 'top' : 'bottom';
  386. me.trigger('slimscroll', msg);
  387. }
  388. }
  389. else
  390. {
  391. releaseScroll = false;
  392. }
  393. lastScroll = percentScroll;
  394. // show only when required
  395. if (barHeight >= me.outerHeight()) {
  396. //allow window scroll
  397. releaseScroll = true;
  398. return;
  399. }
  400. bar.stop(true, true).fadeIn('fast');
  401. if (o.railVisible) {
  402. rail.stop(true, true).fadeIn('fast');
  403. }
  404. }
  405. function hideBar()
  406. {
  407. // only hide when options allow it
  408. if (!o.alwaysVisible)
  409. {
  410. queueHide = setTimeout(function () {
  411. if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg)
  412. {
  413. bar.fadeOut('slow');
  414. rail.fadeOut('slow');
  415. }
  416. }, 1000);
  417. }
  418. }
  419. });
  420. // maintain chainability
  421. return this;
  422. }
  423. });
  424. $.fn.extend({
  425. slimscroll: $.fn.slimScroll
  426. });
  427. })(jQuery);