diff --git a/README.md b/README.md new file mode 100644 index 0000000..44d1b8f --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# What is slimScroll? + +slimScroll is a small jQuery plugin that transforms any div into a scrollable area with a nice scrollbar - similar to the one Facebook and Google started using in their products recently. slimScroll doesn't occupy any visual space as it only appears on a user initiated mouse-over. User can drag the scrollbar or use mouse-wheel to change the scroll value. + +Demo and more: http://rocha.la/jQuery-slimScroll + +Copyright (c) 2011 Piotr Rochala (http://rocha.la) +Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + diff --git a/slimScroll.js b/slimScroll.js index ba9d64f..06ef6c1 100644 --- a/slimScroll.js +++ b/slimScroll.js @@ -7,265 +7,292 @@ */ (function($) { - jQuery.fn.extend({ - slimScroll: function(o) { - - var ops = o; - //do it for every element that matches selector - this.each(function(){ - - var isOverPanel, isOverBar, isDragg, queueHide, barHeight, - divS = '
', - minBarHeight = 30, - releaseScroll = false, - o = ops || {}, - wheelStep = parseInt(o.wheelStep) || 20, - cwidth = o.width || 'auto', - cheight = o.height || '250px', - size = o.size || '7px', - color = o.color || '#000', - position = o.position || 'right', - distance = o.distance || '1px', - start = o.start || 'top', - opacity = o.opacity || .4, - alwaysVisible = o.alwaysVisible === true, - railVisible = o.railVisible || false, - railColor = o.railColor || '#333', - railOpacity = o.railOpacity || 0.2; - - //used in event handlers and for better minification - var me = $(this); - - //wrap content - var wrapper = $(divS).css({ - position: 'relative', - overflow: 'hidden', - width: cwidth, - height: cheight - }).attr({ 'class': 'slimScrollDiv' }); - - //update style for the div - me.css({ - overflow: 'hidden', - width: cwidth, - height: cheight - }); - - //create scrollbar rail - var rail = $(divS).css({ - width: size, - height: '100%', - position: 'absolute', - top: 0, - display: (alwaysVisible && railVisible) ? 'block' : 'none', - 'border-radius': size, - background: railColor, - opacity: railOpacity, - zIndex: 90 - }); - - //create scrollbar - var bar = $(divS).attr({ - 'class': 'slimScrollBar ', - style: 'border-radius: ' + size - }).css({ - background: color, - width: size, - position: 'absolute', - top: 0, - opacity: opacity, - display: alwaysVisible ? 'block' : 'none', - BorderRadius: size, - MozBorderRadius: size, - WebkitBorderRadius: size, - zIndex: 99 - }); - - //set position - var posCss = (position == 'right') ? { right: distance } : { left: distance }; - rail.css(posCss); - bar.css(posCss); - - //wrap it - me.wrap(wrapper); - - //append to parent div - me.parent().append(bar); - me.parent().append(rail); - - //make it draggable - bar.draggable({ - axis: 'y', - containment: 'parent', - start: function() { isDragg = true; }, - stop: function() { isDragg = false; hideBar(); }, - drag: function(e) - { - //scroll content - scrollContent(0, $(this).position().top, false); - } - }); - - //on rail over - rail.hover(function(){ - showBar(); - }, function(){ - hideBar(); - }); - - //on bar over - bar.hover(function(){ - isOverBar = true; - }, function(){ - isOverBar = false; - }); - - //show on parent mouseover - me.hover(function(){ - isOverPanel = true; - showBar(); - hideBar(); - }, function(){ - isOverPanel = false; - hideBar(); - }); - - var _onWheel = function(e) - { - //use mouse wheel only when mouse is over - if (!isOverPanel) { return; } - - var e = e || window.event; - - var delta = 0; - if (e.wheelDelta) { delta = -e.wheelDelta/120; } - if (e.detail) { delta = e.detail / 3; } - - //scroll content - scrollContent(delta, true); - - //stop window scroll - if (e.preventDefault && !releaseScroll) { e.preventDefault(); } - if (!releaseScroll) { e.returnValue = false; } - } - - var scrollContent = function(y, isWheel, isJump) - { - var delta = y; - - if (isWheel) - { - //move bar with mouse wheel - delta = bar.position().top + y * wheelStep; - - //move bar, make sure it doesn't go out - delta = Math.max(delta, 0); - var maxTop = me.outerHeight() - bar.outerHeight(); - delta = Math.min(delta, maxTop); - - //scroll the scrollbar - bar.css({ top: delta + 'px' }); - } - - //calculate actual scroll amount - var percentScroll = parseInt(bar.position().top) / (me.outerHeight() - bar.outerHeight()); - delta = percentScroll * (me[0].scrollHeight - me.outerHeight()); - - if (isJump) - { - delta = y; - var offsetTop = delta / me[0].scrollHeight * me.outerHeight(); - bar.css({ top: offsetTop + 'px' }); - } - - //scroll content - me.scrollTop(delta); - - //ensure bar is visible - showBar(); - } - - var attachWheel = function() - { - if (window.addEventListener) - { - this.addEventListener('DOMMouseScroll', _onWheel, false ); - this.addEventListener('mousewheel', _onWheel, false ); - } - else - { - document.attachEvent("onmousewheel", _onWheel) - } - } - - //attach scroll events - attachWheel(); - - var getBarHeight = function() - { - //calculate scrollbar height and make sure it is not too small - barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight); - bar.css({ height: barHeight + 'px' }); - } - - //set up initial height - getBarHeight(); - - var showBar = function() - { - //recalculate bar height - getBarHeight(); - clearTimeout(queueHide); - - //show only when required - if(barHeight >= me.outerHeight()) { - //allow window scroll - releaseScroll = true; - return; - } - bar.fadeIn('fast'); - if (railVisible) { rail.fadeIn('fast'); } - } - - var hideBar = function() - { - //only hide when options allow it - if (!alwaysVisible) - { - queueHide = setTimeout(function(){ - if (!isOverBar && !isDragg) - { - bar.fadeOut('slow'); - rail.fadeOut('slow'); - } - }, 1000); - } - } - - //check start position - if (start == 'bottom') - { - //scroll content to bottom - bar.css({ top: 'auto', bottom: 0 }); - scrollContent(0, true); - } - else if (typeof start == 'object') - { - //scroll content - scrollContent($(start).position().top, null, true); - - //make sure bar stays hidden - if (!alwaysVisible) { bar.hide(); } - } - }); - - //maintain chainability - return this; - } - }); - - jQuery.fn.extend({ - slimscroll: jQuery.fn.slimScroll - }); + jQuery.fn.extend({ + slimScroll: function(options) { + + var defaults = { + wheelStep : 20, + width : 'auto', + height : '250px', + size : '7px', + color: '#000', + position : 'right', + distance : '1px', + start : 'top', + opacity : .4, + alwaysVisible : true, + railVisible : false, + railColor : '#333', + railOpacity : '0.2', + railClass : 'slimScrollRail', + barClass : 'slimScrollBar', + wrapperClass : 'slimScrollDiv' + }; + + var o = ops = $.extend( defaults , options ); + + // do it for every element that matches selector + this.each(function(){ + + var isOverPanel, isOverBar, isDragg, queueHide, barHeight, + divS = '
', + minBarHeight = 30, + releaseScroll = false, + wheelStep = parseInt(o.wheelStep), + cwidth = o.width, + cheight = o.height, + size = o.size, + color = o.color, + position = o.position, + distance = o.distance, + start = o.start, + opacity = o.opacity, + alwaysVisible = o.alwaysVisible, + railVisible = o.railVisible, + railColor = o.railColor, + railOpacity = o.railOpacity; + + // used in event handlers and for better minification + var me = $(this); + + // wrap content + var wrapper = $(divS) + .addClass( o.wrapperClass ) + .css({ + position: 'relative', + overflow: 'hidden', + width: cwidth, + height: cheight + }); + + // update style for the div + me.css({ + overflow: 'hidden', + width: cwidth, + height: cheight + }); + + // create scrollbar rail + var rail = $(divS) + .addClass( o.railClass ) + .css({ + width: size, + height: '100%', + position: 'absolute', + top: 0, + display: (alwaysVisible && railVisible) ? 'block' : 'none', + 'border-radius': size, + background: railColor, + opacity: railOpacity, + zIndex: 90 + }); + + // create scrollbar + var bar = $(divS) + .addClass( o.barClass ) + .css({ + background: color, + width: size, + position: 'absolute', + top: 0, + opacity: opacity, + display: alwaysVisible ? 'block' : 'none', + 'border-radius' : size, + BorderRadius: size, + MozBorderRadius: size, + WebkitBorderRadius: size, + zIndex: 99 + }); + + // set position + var posCss = (position == 'right') ? { right: distance } : { left: distance }; + rail.css(posCss); + bar.css(posCss); + + // wrap it + me.wrap(wrapper); + + // append to parent div + me.parent().append(bar); + me.parent().append(rail); + + // make it draggable + bar.draggable({ + axis: 'y', + containment: 'parent', + start: function() { isDragg = true; }, + stop: function() { isDragg = false; hideBar(); }, + drag: function(e) + { + // scroll content + scrollContent(0, $(this).position().top, false); + } + }); + + // on rail over + rail.hover(function(){ + showBar(); + }, function(){ + hideBar(); + }); + + // on bar over + bar.hover(function(){ + isOverBar = true; + }, function(){ + isOverBar = false; + }); + + // show on parent mouseover + me.hover(function(){ + isOverPanel = true; + showBar(); + hideBar(); + }, function(){ + isOverPanel = false; + hideBar(); + }); + + var _onWheel = function(e) + { + // use mouse wheel only when mouse is over + if (!isOverPanel) { return; } + + var e = e || window.event; + + var delta = 0; + if (e.wheelDelta) { delta = -e.wheelDelta/120; } + if (e.detail) { delta = e.detail / 3; } + + // scroll content + scrollContent(delta, true); + + // stop window scroll + if (e.preventDefault && !releaseScroll) { e.preventDefault(); } + if (!releaseScroll) { e.returnValue = false; } + } + + var scrollContent = function(y, isWheel, isJump) + { + var delta = y; + + if (isWheel) + { + // move bar with mouse wheel + delta = bar.position().top + y * wheelStep; + + // move bar, make sure it doesn't go out + delta = Math.max(delta, 0); + var maxTop = me.outerHeight() - bar.outerHeight(); + delta = Math.min(delta, maxTop); + + // scroll the scrollbar + bar.css({ top: delta + 'px' }); + } + + // calculate actual scroll amount + var percentScroll = parseInt(bar.position().top) / (me.outerHeight() - bar.outerHeight()); + delta = percentScroll * (me[0].scrollHeight - me.outerHeight()); + + if (isJump) + { + delta = y; + var offsetTop = delta / me[0].scrollHeight * me.outerHeight(); + bar.css({ top: offsetTop + 'px' }); + } + + // scroll content + me.scrollTop(delta); + + // ensure bar is visible + showBar(); + + // trigger hide when scroll is stopped + hideBar(); + + } + + var attachWheel = function() + { + if (window.addEventListener) + { + this.addEventListener('DOMMouseScroll', _onWheel, false ); + this.addEventListener('mousewheel', _onWheel, false ); + } + else + { + document.attachEvent("onmousewheel", _onWheel) + } + } + + // attach scroll events + attachWheel(); + + var getBarHeight = function() + { + // calculate scrollbar height and make sure it is not too small + barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight); + bar.css({ height: barHeight + 'px' }); + } + + // set up initial height + getBarHeight(); + + var showBar = function() + { + // recalculate bar height + getBarHeight(); + clearTimeout(queueHide); + + // show only when required + if(barHeight >= me.outerHeight()) { + //allow window scroll + releaseScroll = true; + return; + } + bar.fadeIn('fast'); + if (railVisible) { rail.fadeIn('fast'); } + } + + var hideBar = function() + { + // only hide when options allow it + if (!alwaysVisible) + { + queueHide = setTimeout(function(){ + if (!isOverBar && !isDragg) + { + bar.fadeOut('slow'); + rail.fadeOut('slow'); + } + }, 1000); + } + } + + // check start position + if (start == 'bottom') + { + // scroll content to bottom + bar.css({ top: 'auto', bottom: 0 }); + scrollContent(0, true); + } + else if (typeof start == 'object') + { + // scroll content + scrollContent($(start).position().top, null, true); + + // make sure bar stays hidden + if (!alwaysVisible) { bar.hide(); } + } + }); + + // maintain chainability + return this; + } + }); + + jQuery.fn.extend({ + slimscroll: jQuery.fn.slimScroll + }); })(jQuery); \ No newline at end of file diff --git a/slimScroll.min.js b/slimScroll.min.js index 319efee..23d2c1f 100644 --- a/slimScroll.min.js +++ b/slimScroll.min.js @@ -3,6 +3,5 @@ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. * * Version: 0.3.0 - * - */ -(function(a){jQuery.fn.extend({slimScroll:function(c){var b=c;this.each(function(){var F,z,m,s,A,n="
",C=30,y=false,t=b||{},u=parseInt(t.wheelStep)||20,B=t.width||"auto",w=t.height||"250px",r=t.size||"7px",x=t.color||"#000",K=t.position||"right",h=t.distance||"1px",k=t.start||"top",g=t.opacity||0.4,q=t.alwaysVisible===true,j=t.railVisible||false,i=t.railColor||"#333",v=t.railOpacity||0.2;var I=a(this);var l=a(n).css({position:"relative",overflow:"hidden",width:B,height:w}).attr({"class":"slimScrollDiv"});I.css({overflow:"hidden",width:B,height:w});var d=a(n).css({width:r,height:"100%",position:"absolute",top:0,display:(q&&j)?"block":"none","border-radius":r,background:i,opacity:v,zIndex:90});var D=a(n).attr({"class":"slimScrollBar ",style:"border-radius: "+r}).css({background:x,width:r,position:"absolute",top:0,opacity:g,display:q?"block":"none",BorderRadius:r,MozBorderRadius:r,WebkitBorderRadius:r,zIndex:99});var e=(K=="right")?{right:h}:{left:h};d.css(e);D.css(e);I.wrap(l);I.parent().append(D);I.parent().append(d);D.draggable({axis:"y",containment:"parent",start:function(){m=true},stop:function(){m=false;p()},drag:function(o){E(0,a(this).position().top,false)}});d.hover(function(){f()},function(){p()});D.hover(function(){z=true},function(){z=false});I.hover(function(){F=true;f();p()},function(){F=false;p()});var H=function(o){if(!F){return}var o=o||window.event;var L=0;if(o.wheelDelta){L=-o.wheelDelta/120}if(o.detail){L=o.detail/3}E(L,true);if(o.preventDefault&&!y){o.preventDefault()}if(!y){o.returnValue=false}};var E=function(Q,M,o){var P=Q;if(M){P=D.position().top+Q*u;P=Math.max(P,0);var O=I.outerHeight()-D.outerHeight();P=Math.min(P,O);D.css({top:P+"px"})}var N=parseInt(D.position().top)/(I.outerHeight()-D.outerHeight());P=N*(I[0].scrollHeight-I.outerHeight());if(o){P=Q;var L=P/I[0].scrollHeight*I.outerHeight();D.css({top:L+"px"})}I.scrollTop(P);f()};var G=function(){if(window.addEventListener){this.addEventListener("DOMMouseScroll",H,false);this.addEventListener("mousewheel",H,false)}else{document.attachEvent("onmousewheel",H)}};G();var J=function(){A=Math.max((I.outerHeight()/I[0].scrollHeight)*I.outerHeight(),C);D.css({height:A+"px"})};J();var f=function(){J();clearTimeout(s);if(A>=I.outerHeight()){y=true;return}D.fadeIn("fast");if(j){d.fadeIn("fast")}};var p=function(){if(!q){s=setTimeout(function(){if(!z&&!m){D.fadeOut("slow");d.fadeOut("slow")}},1000)}};if(k=="bottom"){D.css({top:"auto",bottom:0});E(0,true)}else{if(typeof k=="object"){E(a(k).position().top,null,true);if(!q){D.hide()}}}});return this}});jQuery.fn.extend({slimscroll:jQuery.fn.slimScroll})})(jQuery); \ No newline at end of file + * + */(function(a){jQuery.fn.extend({slimScroll:function(b){var c={wheelStep:20,width:"auto",height:"250px",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:.4,alwaysVisible:!0,railVisible:!1,railColor:"#333",railOpacity:"0.2",railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv"},d=ops=a.extend(c,b);this.each(function(){var b,c,e,f,g,h="
",i=30,j=!1,k=parseInt(d.wheelStep),l=d.width,m=d.height,n=d.size,p=d.color,q=d.position,r=d.distance,s=d.start,t=d.opacity,u=d.alwaysVisible,v=d.railVisible,w=d.railColor,x=d.railOpacity,y=a(this),z=a(h).addClass(d.wrapperClass).css({position:"relative",overflow:"hidden",width:l,height:m});y.css({overflow:"hidden",width:l,height:m});var A=a(h).addClass(d.railClass).css({width:n,height:"100%",position:"absolute",top:0,display:u&&v?"block":"none","border-radius":n,background:w,opacity:x,zIndex:90}),B=a(h).addClass(d.barClass).css({background:p,width:n,position:"absolute",top:0,opacity:t,display:u?"block":"none","border-radius":n,BorderRadius:n,MozBorderRadius:n,WebkitBorderRadius:n,zIndex:99}),C=q=="right"?{right:r}:{left:r};A.css(C),B.css(C),y.wrap(z),y.parent().append(B),y.parent().append(A),B.draggable({axis:"y",containment:"parent",start:function(){e=!0},stop:function(){e=!1,I()},drag:function(b){E(0,a(this).position().top,!1)}}),A.hover(function(){H()},function(){I()}),B.hover(function(){c=!0},function(){c=!1}),y.hover(function(){b=!0,H(),I()},function(){b=!1,I()});var D=function(a){if(!!b){var a=a||window.event,c=0;a.wheelDelta&&(c=-a.wheelDelta/120),a.detail&&(c=a.detail/3),E(c,!0),a.preventDefault&&!j&&a.preventDefault(),j||(a.returnValue=!1)}},E=function(a,b,c){var d=a;if(b){d=B.position().top+a*k,d=Math.max(d,0);var e=y.outerHeight()-B.outerHeight();d=Math.min(d,e),B.css({top:d+"px"})}var f=parseInt(B.position().top)/(y.outerHeight()-B.outerHeight());d=f*(y[0].scrollHeight-y.outerHeight());if(c){d=a;var g=d/y[0].scrollHeight*y.outerHeight();B.css({top:g+"px"})}y.scrollTop(d),H(),I()},F=function(){window.addEventListener?(this.addEventListener("DOMMouseScroll",D,!1),this.addEventListener("mousewheel",D,!1)):document.attachEvent("onmousewheel",D)};F();var G=function(){g=Math.max(y.outerHeight()/y[0].scrollHeight*y.outerHeight(),i),B.css({height:g+"px"})};G();var H=function(){G(),clearTimeout(f);g>=y.outerHeight()?j=!0:(B.fadeIn("fast"),v&&A.fadeIn("fast"))},I=function(){u||(f=setTimeout(function(){!c&&!e&&(B.fadeOut("slow"),A.fadeOut("slow"))},1e3))};s=="bottom"?(B.css({top:"auto",bottom:0}),E(0,!0)):typeof s=="object"&&(E(a(s).position().top,null,!0),u||B.hide())});return this}}),jQuery.fn.extend({slimscroll:jQuery.fn.slimScroll})})(jQuery); \ No newline at end of file