{"id":779,"date":"2010-06-10T14:49:19","date_gmt":"2010-06-10T13:49:19","guid":{"rendered":"http:\/\/www.simonbattersby.com\/blog\/?page_id=779"},"modified":"2014-03-04T12:27:23","modified_gmt":"2014-03-04T12:27:23","slug":"vertical-scrollbar-using-jquery-ui-slider","status":"publish","type":"page","link":"https:\/\/www.simonbattersby.com\/blog\/vertical-scrollbar-using-jquery-ui-slider\/","title":{"rendered":"Vertical scrollbar using jQuery UI slider"},"content":{"rendered":"<p class=\"orange\">Also available as a <a href=\"\/blog\/vertical-scrollbar-plugin-using-jquery-ui-slider\/\" title=\"Vertical scrollbar as a jQuery plugin\">jQuery plugin<\/a>.<\/p>\r\n<p>Here&#8217;s my attempt at using jQuery UI Slider as a vertical scrollbar. I began this in 2010 by adapting the <a href=\"http:\/\/jqueryui.com\/demos\/slider\/#side-scroll\" title=\"jQuery UI horizontal scrollbar\">code on the jQuery UI site<\/a> for a horizontal slider. This worked, but seemed unnecessarily complex, so I had another go, starting from the <a href=\"http:\/\/jqueryui.com\/demos\/slider\/#slider-vertical\" title=\"jQuery vertical slider\">vertical slider<\/a> code. Since then I&#8217;ve had numerous requests for different variations on the basic scrollbar, and these are all now incorporated into a single piece of code.<\/p>\r\n\r\n<p>The code assumes a div with fixed height (<code>#scroll-pane<\/code> or <code>.scroll-pane<\/code> in my examples) which contains the content for scrolling. The script wraps the contents of the div to be scrolled in another div (<code>.scroll-content<\/code>) which is used positioned absolutely, and then the <code>top<\/code> value of <code>#scroll-content<\/code> is changed via the slider to make the content scroll up and down. The scrollbar itself is positioned within <code>#scroll-pane<\/code>, using absolute positioning on the right hand side, but it can go anywhere you like.<\/p>\r\n\r\n<p>There&#8217;s a fundamental aspect of the way the vertical slider works which took me a little time to get my head round, and that&#8217;s that the slider as a default starts from zero at the bottom and increases upwards &#8211; which is the wrong way round for a vertical scroller, hence the need for a bit of extra calculation.<\/p>\r\n\r\n<p>So, here&#8217;s the css:<\/p>\r\n<pre>#scroll-pane,.scroll-pane{position:relative}\r\n.scroll-content {position:absolute;top:0;left:0}\r\n.slider-wrap{position:absolute;right:0;top:0;background-color:lightgrey;width:20px;border-left:1px solid gray;}\r\n.slider-vertical{position:relative;height:100%}\r\n.ui-slider-handle{width:20px;height:10px;margin:0 auto;background-color:darkgray;display:block;position:absolute}\r\n.ui-slider-handle img{border:none}\r\n.scrollbar-top{position:absolute;top:0;}\r\n.scrollbar-bottom{position:absolute;bottom:0;}\r\n.scrollbar-grip{position:absolute;top:50%;margin-top:-6px;}\r\n.ui-slider-range{position:absolute;width:100%;background-color:lightgrey}\r\n<\/pre>\r\n\r\n<p>and here&#8217;s the javascript. For convenience I&#8217;ve wrapped all the code needed for all the different variations below into one function called <code>setSlider()<\/code>.<\/p>\r\n<pre>function setSlider($scrollpane){<span class=\"code_comment\">\/\/$scrollpane is the div to be scrolled\r\n<\/span>\t\r\n\t<span class=\"code_comment\">\/\/set options for handle image - amend this to true or false as required\r\n<\/span>\tvar handleImage = false;\r\n\t\r\n\t<span class=\"code_comment\">\/\/change the main div to overflow-hidden as we can use the slider now\r\n<\/span>\t$scrollpane.css('overflow','hidden');\r\n\t\r\n\t<span class=\"code_comment\">\/\/if it's not already there, wrap an extra div around the scrollpane so we can use the mousewheel later\r\n<\/span>\tif ($scrollpane.parent('.scroll-container').length==0) $scrollpane.wrap('&lt;\\div class=\"scroll-container\"&gt; \/');\r\n\t<span class=\"code_comment\">\/\/and again, if it's not there, wrap a div around the contents of the scrollpane to allow the scrolling\r\n<\/span>\tif ($scrollpane.find('.scroll-content').length==0) $scrollpane.children().wrapAll('&lt;\\div class=\"scroll-content\"&gt; \/');\r\n\t\r\n\t<span class=\"code_comment\">\/\/compare the height of the scroll content to the scroll pane to see if we need a scrollbar\r\n<\/span>\tvar difference = $scrollpane.find('.scroll-content').height()-$scrollpane.height();<span class=\"code_comment\">\/\/eg it's 200px longer \r\n<\/span>\t$scrollpane.data('difference',difference); \r\n\t\r\n\tif(difference&lt;=0 &#038;&#038; $scrollpane.find('.slider-wrap').length&gt;0)<span class=\"code_comment\">\/\/scrollbar exists but is no longer required\r\n<\/span>\t{\r\n\t\t$scrollpane.find('.slider-wrap').remove();<span class=\"code_comment\">\/\/remove the scrollbar\r\n<\/span>\t\t$scrollpane.find('.scroll-content').css({top:0});<span class=\"code_comment\">\/\/and reset the top position\r\n<\/span>\t}\r\n\t\r\n\tif(difference&gt;0)<span class=\"code_comment\">\/\/if the scrollbar is needed, set it up...\r\n<\/span>\t{\r\n\t\tvar proportion = difference \/ $scrollpane.find('.scroll-content').height();<span class=\"code_comment\">\/\/eg 200px\/500px\r\n<\/span>\t\t\r\n\t\tvar handleHeight = Math.round((1-proportion)*$scrollpane.height());<span class=\"code_comment\">\/\/set the proportional height - round it to make sure everything adds up correctly later on\r\n<\/span>\t\thandleHeight -= handleHeight%2; \r\n\t\t\r\n\t\t<span class=\"code_comment\">\/\/if the slider has already been set up and this function is called again, we may need to set the position of the slider handle\r\n<\/span>\t\tvar contentposition = $scrollpane.find('.scroll-content').position();\t\r\n\t\tvar sliderInitial = 100*(1-Math.abs(contentposition.top)\/difference);\r\n\t\t\r\n\t\tif($scrollpane.find('.slider-wrap').length==0)<span class=\"code_comment\">\/\/if the slider-wrap doesn't exist, insert it and set the initial value\r\n<\/span>\t\t{\r\n\t\t\t$scrollpane.append('&lt;\\div class=\"slider-wrap\"&gt;&lt;\\div class=\"slider-vertical\"&gt;&lt;\\\/div&gt;&lt;\\\/div&gt;');<span class=\"code_comment\">\/\/append the necessary divs so they're only there if needed\r\n<\/span>                        sliderInitial = 100;\r\n\t\t}\r\n\t\t\r\n\t\t$scrollpane.find('.slider-wrap').height($scrollpane.height());<span class=\"code_comment\">\/\/set the height of the slider bar to that of the scroll pane\r\n<\/span>\t\t\r\n\t\t<span class=\"code_comment\">\/\/set up the slider \r\n<\/span>\t\t$scrollpane.find('.slider-vertical').slider({\r\n\t\t\torientation: 'vertical',\r\n\t\t\tmin: 0,\r\n\t\t\tmax: 100,\r\n\t\t\trange:'min',\r\n\t\t\tvalue: sliderInitial,\r\n\t\t\tslide: function(event, ui) {\r\n\t\t\t\tvar topValue = -((100-ui.value)*difference\/100);\r\n\t\t\t\t$scrollpane.find('.scroll-content').css({top:topValue});<span class=\"code_comment\">\/\/move the top up (negative value) by the percentage the slider has been moved times the difference in height\r\n<\/span>\t\t\t\t$('ui-slider-range').height(ui.value+'%');<span class=\"code_comment\">\/\/set the height of the range element\r\n<\/span>\t\t\t},\r\n\t\t\tchange: function(event, ui) {\r\n\t\t\t\tvar topValue = -((100-ui.value)*($scrollpane.find('.scroll-content').height()-$scrollpane.height())\/100);<span class=\"code_comment\">\/\/recalculate the difference on change\r\n<\/span>\t\t\t\t$scrollpane.find('.scroll-content').css({top:topValue});<span class=\"code_comment\">\/\/move the top up (negative value) by the percentage the slider has been moved times the difference in height\r\n<\/span>\t\t\t\t$('ui-slider-range').height(ui.value+'%');\r\n\t\t  }\t  \r\n\t\t});\r\n\t\t\r\n\t\t<span class=\"code_comment\">\/\/set the handle height and bottom margin so the middle of the handle is in line with the slider\r\n<\/span>\t\t$scrollpane.find(\".ui-slider-handle\").css({height:handleHeight,'margin-bottom':-0.5*handleHeight});\r\n\t\tvar origSliderHeight = $scrollpane.height();<span class=\"code_comment\">\/\/read the original slider height\r\n<\/span>\t\tvar sliderHeight = origSliderHeight - handleHeight ;<span class=\"code_comment\">\/\/the height through which the handle can move needs to be the original height minus the handle height\r\n<\/span>\t\tvar sliderMargin =  (origSliderHeight - sliderHeight)*0.5;<span class=\"code_comment\">\/\/so the slider needs to have both top and bottom margins equal to half the difference\r\n<\/span>\t\t$scrollpane.find(\".ui-slider\").css({height:sliderHeight,'margin-top':sliderMargin});<span class=\"code_comment\">\/\/set the slider height and margins\r\n<\/span>\t\t$scrollpane.find(\".ui-slider-range\").css({bottom:-sliderMargin});<span class=\"code_comment\">\/\/position the slider-range div at the top of the slider container\r\n<\/span>\t\t\r\n\t\t<span class=\"code_comment\">\/\/if required create elements to hold the images for the scrollbar handle\r\n<\/span>\t\tif (handleImage){\r\n\t\t\t$(\".ui-slider-handle\").append('&lt;img class=\"scrollbar-top\" src=\"\/images\/misc\/scrollbar-handle-top.png\"\/&gt;');\r\n\t\t\t$(\".ui-slider-handle\").append('&lt;img class=\"scrollbar-bottom\" src=\"\/images\/misc\/scrollbar-handle-bottom.png\"\/&gt;');\r\n\t\t\t$(\".ui-slider-handle\").append('&lt;img class=\"scrollbar-grip\" src=\"\/images\/misc\/scrollbar-handle-grip.png\"\/&gt;');\r\n\t\t}\r\n\t}<span class=\"code_comment\">\/\/end if\r\n<\/span>\t\t \r\n\t<span class=\"code_comment\">\/\/code for clicks on the scrollbar outside the slider\r\n<\/span>\t$(\".ui-slider\").click(function(event){<span class=\"code_comment\">\/\/stop any clicks on the slider propagating through to the code below\r\n<\/span>\t\tevent.stopPropagation();\r\n\t});\r\n\t   \r\n\t$(\".slider-wrap\").click(function(event){<span class=\"code_comment\">\/\/clicks on the wrap outside the slider range\r\n<\/span>\t\tvar offsetTop = $(this).offset().top;<span class=\"code_comment\">\/\/read the offset of the scroll pane\r\n<\/span>\t\tvar clickValue = (event.pageY-offsetTop)*100\/$(this).height();<span class=\"code_comment\">\/\/find the click point, subtract the offset, and calculate percentage of the slider clicked\r\n<\/span>\t\t$(this).find(\".slider-vertical\").slider(\"value\", 100-clickValue);<span class=\"code_comment\">\/\/set the new value of the slider\r\n<\/span>\t}); \r\n\t\r\n\t\t \r\n\t<span class=\"code_comment\">\/\/additional code for mousewheel\r\n<\/span>\tif($.fn.mousewheel){\t\t\r\n\t\r\n\t\t$scrollpane.parent().unmousewheel();<span class=\"code_comment\">\/\/remove any previously attached mousewheel events\r\n<\/span>\t\t$scrollpane.parent().mousewheel(function(event, delta){\r\n\t\t\t\r\n\t\t\tvar speed = Math.round(5000\/$scrollpane.data('difference'));\r\n\t\t\tif (speed &lt;1) speed = 1;\r\n\t\t\tif (speed &gt;100) speed = 100;\r\n\t\r\n\t\t\tvar sliderVal = $(this).find(\".slider-vertical\").slider(\"value\");<span class=\"code_comment\">\/\/read current value of the slider\r\n<\/span>\t\t\t\r\n\t\t\tsliderVal += (delta*speed);<span class=\"code_comment\">\/\/increment the current value\r\n<\/span>\t \r\n\t\t\t$(this).find(\".slider-vertical\").slider(\"value\", sliderVal);<span class=\"code_comment\">\/\/and set the new value of the slider\r\n<\/span>\t\t\t\r\n\t\t\tevent.preventDefault();<span class=\"code_comment\">\/\/stop any default behaviour\r\n<\/span>\t\t});\r\n\t\t\r\n\t}\r\n\t\r\n}<\/pre>\r\n\r\n<p>I&#8217;ve tested this on: IE6, IE7, IE8, FF3.6, Safari 4, Chrome and Opera9\/10\/11 on Win\/XP; IE8, IE9, FF9\/10, Opera 11, Safari 5 and Chrome on W7; FF7, Epiphany and Chromium on Ubuntu; and Safari 5 on Mac OS X\/Snow Leopard. See the demos below for various applications and uses.<\/p>\r\n<h3>Can I use this on my site?<\/h3>\r\n<p>Probably. See my <a href=\"\/blog\/use-of-code-from-this-site\/\">policies on using code from this site<\/a>.<\/p>\r\n<p class=\"orange\">Please don&#8217;t link directly to the javascript on my site. Due to the number of hits on my bandwidth because people were doing this I&#8217;ve had to deny direct access to javascript files. Use a CDN like Google&#8217;s instead.<\/p>\r\n<p><strong>Touch screens:<\/strong> As it stands, this will not work with touch screens as jQuery UI does not itself support touch events. I have done only limited testing, but <a href=\"http:\/\/touchpunch.furf.com\/\">TouchPunch<\/a> seems to work on an iPad and iPhone, but not on earlier Android devices. <a href=\"\/blog\/2014\/03\/jquery-ui-slider-touchscreen-devices\/\">See here<\/a> for some limited testing results &#8211; please let me know of anything you have to add to this.<\/p>\r\n\r\n<h3>Help! It doesn&#8217;t work<\/h3>\r\n<p>If you hit problems, start off by looking at my <a href=\"\/blog\/debugging-jquery-a-beginners-guide\/\" title=\"Debugging jQuery \u2013 a beginner\u2019s guide\">beginners&#8217; guide to debugging jquery<\/a>. Most of the problems encountered by users are listed here.<\/p>\r\n\r\n<div id=\"variations\">\r\n<h3>Demos<\/h3>\r\n\r\n<h4>Basic scrollbar: <a href=\"\/demos\/vertical_scrollbar_demo_1_basic.htm\" title=\"Basic scrollbar\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));<\/pre>\r\n<\/div>\r\n\r\n<h4>Multiple scrollbars: <a href=\"\/demos\/vertical_scrollbar_demo_6_multiple.htm\" title=\"Multiple scrollbars\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>$('.myElementClass').each(function(){\r\n\tsetSlider($(this));\r\n});<\/pre>\r\n<p>Originally added after these <a href=\"#comment-2491\">two<\/a> <a href=\"#comment-2543\">questions<\/a> within days of each other.<\/p>\r\n<\/div>\r\n\r\n<h4>Changing the contents after load: add or remove content within the scrolled element <a href=\"\/demos\/vertical_scrollbar_demo_7_addcontent.htm\" title=\"Change scrolled content after load\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n\r\nfunction changeContent{<span class=\"code_comment\">\/\/do whatever you want....\r\n<\/span>...code to change content....\r\nsetSlider($('#myElement'));<span class=\"code_comment\">\/\/call the function again to reset the slider for the new content\r\n<\/span>}<\/pre>\r\n<p>Originally in response to <a href=\"#comment-2574\">this comment<\/a>, the slider code will check for the existence of a slider, remove it if it&#8217;s no longer required (or establish it if it&#8217;s newly required), and aslso set the slider position to match any previous scroll point.<\/p>\r\n<\/div>\r\n\r\n<h4>Scrollbar resizes with window: <a href=\"\/demos\/vertical_scrollbar_demo_10_windowresize.htm\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n\r\n$(window).resize(function() {\r\n\tsetSlider($('#myElement'));\r\n});<\/pre>\r\n<p>originally in response to <a href=\"#comment-3682\"> Igor&#8217;s comment<\/a>, this supports a percentage height <code>.scroll-pane<\/code> and resizes with the viewport resize. Again, the solution is to call <code>setSlider()<\/code> on a window resize event.<\/p><\/div>\r\n\r\n<h4>Scrollbar on a div that&#8217;s originally hidden <a href=\"\/demos\/vertical_scrollbar_demo_12_hiddendivs.htm\" title=\"Scrollbar on a hidden div\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>$('#myElement').show();<span class=\"code_comment\">\/\/or whatever you want, then...\r\n<\/span>setSlider($('#myElement'));<span class=\"code_comment\">\/\/...call the function again to reset the slider for the new content\r\n<\/span>}\r\n});<\/pre>\r\n<p>When the div is originally hidden &#8211; for example via <code>display:none<\/code>, most browsers will read it as having zero height and hence all the caslculations decide that a scrollbar isn&#8217;t needed. The solution to this just to call <code>setSlider()<\/code> (again) when the hidden div is displayed.<\/p><\/div>\r\n\r\n<h4>Preset the scroll position on load: <a href=\"\/demos\/vertical_scrollbar_demo_11_scrollonload.htm\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n\r\n<span class=\"code_comment\">\/\/code to set an initial scrolled position\r\n<\/span>var position = $(\"#targetElement\").position();<span class=\"code_comment\">\/\/find the position of the required item\r\n<\/span>var topValue = position.top;<span class=\"code_comment\">\/\/extract the top value\r\n<\/span>var sliderValue = 100-(100*topValue\/($(\".scroll-content\").height()-$(\"#scroll-pane\").height()));<span class=\"code_comment\">\/\/calculate the percentage of the top of the required item\r\n<\/span>$(\".slider-vertical\").slider(\"value\", sliderValue);\/\/and set the new value of the slider<\/pre>\r\n<\/div>\r\n\r\n<h4 class=\"active\">Autocomplete: scroll to an item selected from a database <a href=\"\/demos\/vertical_scrollbar_demo_5_autocomplete.php\" title=\"Scrolling div with draggable addition\">Demo<\/a> <a href=\"\/blog\/vertical-scrollbar-with-integrated-autocomplete-jquery-ui\/\" title=\"Slider with Autocomplete\">Fuller explanation<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n<span class=\"code_comment\">\/\/autocomplete\r\n<\/span>var difference = $(\".scroll-content\").height()-$(\"myElement\").height(); \r\n$(\"#quickfind\").autocomplete({\r\n\tsource: \"get_descriptions.php\",\r\n\tminLength: 2,<span class=\"code_comment\">\/\/search after two characters\r\n<\/span>\tselect: function(event,ui){\r\n\t\tposition = $(\".scroll-content-item:contains(\"+ui.item.value+\")\").position();<span class=\"code_comment\">\/\/search for a div containing the selected description and read its position\r\n<\/span>\t\t\r\n\t\tvar topValue = -(position.top);<span class=\"code_comment\">\/\/content top is just the negative of the top position\r\n<\/span>\t\t\r\n\t\tif (topValue&gt;0) topValue = 0;<span class=\"code_comment\">\/\/stop the content scrolling down too much\r\n<\/span>\t\tif (Math.abs(topValue)&gt;difference) topValue = (-1)*difference;<span class=\"code_comment\">\/\/stop the content scrolling up too much\r\n<\/span>\t\r\n\t\tsliderVal = (100+(topValue*100\/difference));<span class=\"code_comment\">\/\/calculate the slider position from the content position\r\n<\/span>\t\t$(\".slider-vertical\").slider(\"value\", sliderVal);<span class=\"code_comment\">\/\/set the slider position\r\n<\/span>\t\t}\r\n});<\/pre>\r\n<p>Originally in response to <a href=\"#comment-1446\">Dan&#8217;s comment<\/a>.<\/p>\r\n<\/div>\r\n\r\n\r\n<h4>Scroll automatically on load: have the element scrolling without user input <a href=\"\/demos\/vertical_scrollbar_demo_15_auto_scroll.htm\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n\r\nvar scrollId = setInterval('scrollDown()', 60);<span class=\"code_comment\">\/\/ start the autoscroll via a function\r\n<\/span>\r\n$(\".ui-slider-handle\").mouseover(function(){<span class=\"code_comment\">\/\/stop the autoscroll if the handle is hovered\r\n<\/span>    clearInterval(scrollId);\r\n});\r\n\r\nfunction scrollDown(){\r\n\tif ($(\".slider-vertical\").slider(\"value\")&gt;0){<span class=\"code_comment\">\/\/if it's not at the bottom, scroll down\r\n<\/span>\t\t$(\".slider-vertical\").slider(\"value\",$(\".slider-vertical\").slider(\"value\")-1);\r\n\t}\r\n}<\/pre>\t\r\n<p>Originally in response to <a href=\"#comment-5194\">this comment<\/a>. Can&#8217;t quite see the application of this one either&#8230;<\/p>\r\n<\/div>\r\n\r\n<h4>Scrolling with buttons: <a href=\"\/demos\/vertical_scrollbar_demo_14_scroll_buttons.htm\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n\r\n$('#arrow_up').mousedown(function(event) {\r\n\tif(event.preventDefault) event.preventDefault();\r\n    intervalId = setInterval('scrollUp()', 30);\r\n\t$(this).bind('mouseup mouseleave', function() {\r\n\t\tclearInterval(intervalId);\r\n\t});\r\n});\r\n$('#arrow_down').mousedown(function(event) {\r\n\tif(event.preventDefault) event.preventDefault();\r\n    intervalId = setInterval('scrollDown()', 30);\r\n\t$(this).bind('mouseup mouseleave', function() {\r\n\t\tclearInterval(intervalId);\r\n\t});\r\n});\r\n\r\nfunction scrollUp(){\r\n\tif ($(\".slider-vertical\").slider(\"value\")&lt;100){<span class=\"code_comment\">\/\/if it's not at the top, scroll up\r\n<\/span>\t\t$(\".slider-vertical\").slider(\"value\",$(\".slider-vertical\").slider(\"value\")+1);\r\n\t}\r\n}\r\nfunction scrollDown(){\r\n\tif ($(\".slider-vertical\").slider(\"value\")&gt;0){<span class=\"code_comment\">\/\/if it's not at the bottom, scroll down\r\n<\/span>\t\t$(\".slider-vertical\").slider(\"value\",$(\".slider-vertical\").slider(\"value\")-1);\r\n\t}\r\n}<\/pre>\r\n<p><code>#arrow_up<\/code> and <code>#arrow_down<\/code> are the ids for the arrow buttons, although you&#8217;d probably guessed that&#8230;Originally in response to <a href=\"#comment-4974\">this comment<\/a>. <a href=\"#comment-6017\">Dick pointed out<\/a> that if you try and drag the scrolling buttons it can occasionally cause the scroller to break. I couldn&#8217;t recreate this at all, but added some code to prevent the buttons being dragged, which Dick confirms fixes the problem.<\/p>\r\n<\/div>\r\n\r\n<h4>Scrolling to anchors: <a href=\"\/demos\/vertical_scrollbar_demo_13_anchors.htm\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#scroll-pane'));\r\n\r\n$('#anchors a').click(function(){\r\n\r\n\tvar difference = $('.scroll-content').height()-$('#scroll-pane').height();<span class=\"code_comment\">\/\/calculate the difference - not needed if within the main slider code\r\n<\/span>\t\r\n\tvar targetclass = ($(this).attr('class'));<span class=\"code_comment\">\/\/read the class of the clicked anchor\r\n<\/span>    \r\n\tvar position = $(\".scroll-content .\"+targetclass).position();<span class=\"code_comment\">\/\/search for a div with matching class and read its position\r\n<\/span>    var topValue = -(position.top);<span class=\"code_comment\">\/\/content top is just the negative of the top position\r\n<\/span>\t\t\r\n\tif (topValue&gt;0) topValue = 0;<span class=\"code_comment\">\/\/stop the content scrolling down too much\r\n<\/span>\tif (Math.abs(topValue)&gt;difference) topValue = (-1)*difference;<span class=\"code_comment\">\/\/stop the content scrolling up too much\r\n<\/span>\r\n\tsliderVal = (100+(topValue*100\/difference));<span class=\"code_comment\">\/\/calculate the slider position from the content position\r\n<\/span>\t$(\".slider-vertical\").slider(\"value\", sliderVal);<span class=\"code_comment\">\/\/set the slider position\r\n<\/span>\t\r\n\treturn false;\r\n});<\/pre>\r\n<p>This assumes markup where the anchors are assigned a class which is also added to the &#8216;target&#8217; scroll point within the div. View source for the details.<\/p>\r\n<\/div>\r\n\r\n<h4>Easing: animate the slider using easing <a href=\"\/demos\/vertical_scrollbar_demo_8_easing.htm\" title=\"Scrollbar with easing\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<p>Had a couple of comments asking about easing. It doesn&#8217;t work well with the code as it is, because the bar scrolls at every mouse move. However, if the slider code is changed to call the animation on the <code>change<\/code> event instead of the <code>slide<\/code> event, then it is possible, although the scrolled content will not move until the slider is stopped. I don&#8217;t care for this one at all, although it does work better <a href=\"\/demos\/vertical_scrollbar_demo_8_easing_anchors.htm\">with anchors<\/a>.<\/p> \r\n<p>Note that this (the only demo that) uses a different version of setSlider(), where the slider is instantiated via:.<\/p>\r\n<pre>   <span class=\"code_comment\">\/\/set up the slider \r\n<\/span>   $scrollpane.find('.slider-vertical').slider({\r\n      orientation: 'vertical',\r\n      min: 0,\r\n      max: 100,\r\n\t  range:'min',\r\n      value: sliderInitial,\r\n      change: function(event, ui) {\r\n\t\t\t var topValue = -((100-ui.value)*difference\/100);\r\n\t\t\t $scrollpane.find('.scroll-content').<span class=\"code_highlight\">animate({top:topValue},animateSpeed,easingMethod<\/span>);<span class=\"code_comment\">\/\/move the top up (negative value) by the percentage the slider has been moved times the difference in height\r\n<\/span>\t\t\t $('ui-slider-range').height(ui.value+'%');\r\n\t  }\t  \r\n   });<\/pre>\r\n\r\n<p>You need to set the variables <code>animateSpeed<\/code> and <code>easingMethod<\/code>, of course, and include the easing plugin.<\/p>\r\n<\/div>\r\n\r\n<h4>Shopping cart: with jQuery UI draggable\/droppable <a href=\"\/demos\/vertical_scrollbar_demo_9_draggable.php\" title=\"Scrolling div with draggable addition\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n$('.item').draggable({appendTo:'body',helper:'clone'});\r\n$('#droppable').droppable({\r\n\tdrop:function(event,ui){\r\n\t\t$( this ).find( \"#placeholder\" ).remove();\r\n\t\tvar description = ui.draggable.find('p').text();\r\n\t\t$( \"&lt;\\li&gt;&lt;\\\/li&gt;\" ).text(description).appendTo( $('#droppable ul') );\r\n\t}\r\n});<\/pre>\r\n<p>originally in response to <a href=\"#comment-2573\">Juan&#8217;s comment<\/a>, minimal droppable code to to allow dragging from the scrolling div and dropping into a basket.<\/p>\r\n<\/div>\r\n\r\n<h4 class=\"active\">Autohide scrollbar: so it&#8217;s displayed only on hover <a href=\"\/demos\/vertical_scrollbar_demo_3_autohide.htm\" title=\"Scrollbar with autohide\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));\r\n$(\".slider-wrap\").hide();\r\n$(\"#myElement\").hover(function(){\r\n\t$(\".slider-wrap\").show();\r\n\t},\r\n\tfunction(){\r\n\t$(\".slider-wrap\").hide();\r\n});<\/pre>\r\n<p><a href=\"#comment-2169\">Robert<\/a> asked about an autohide function. I really don&#8217;t like it&#8230;<\/p>\r\n<\/div>\r\n\r\n<h4 class=\"active\">Styling the handle <a href=\"\/demos\/vertical_scrollbar_demo_4_imagehandle.htm\" title=\"Styled scrollbar handle\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<pre>setSlider($('#myElement'));<\/pre>\r\n<p>plus these settings within <code>setSlider()<\/code>:<\/p>\r\n<pre>function setSlider($scrollpane){<span class=\"code_comment\">\/\/$scrollpane is the div to be scrolled\r\n<\/span>\t\r\n\t<span class=\"code_comment\">\/\/set options for handle image - amend this to true or false as required\r\n<\/span>\tvar handleImage = <span class=\"code_highlight\">true<\/span>;\r\n         \r\n        ...\r\n\r\n\tif (handleImage){<span class=\"code_comment\">\/\/change the image paths as required for your images - delete any that are not required if you want\r\n<\/span>\t\t$(\".ui-slider-handle\").append('&lt;img class=\"scrollbar-top\" src=\"<span class=\"code_highlight\">\/images\/misc\/scrollbar-handle-top.png<\/span>\"\/&gt;');\t\t\t\r\n                $(\".ui-slider-handle\").append('&lt;img class=\"scrollbar-bottom\" src=\"<span class=\"code_highlight\">\/images\/misc\/scrollbar-handle-bottom.png<\/span>\"\/&gt;');\r\n\t\t$(\".ui-slider-handle\").append('&lt;img class=\"scrollbar-grip\" src=\"<span class=\"code_highlight\">\/images\/misc\/scrollbar-handle-grip.png<\/span>\"\/&gt;');\r\n\t}\r\n\r\n        ...<\/pre>\r\n<p>and this addtional css<\/p>\r\n<pre>.ui-slider-handle{background:transparent url(\/images\/misc\/scrollbar-handle-middle.png) repeat-y top left;}<\/pre>\r\n<p>In order to make this example bulletproof, I&#8217;ve used four images (!) &#8211; one each for the top and bottom of the handle, one for the white &#8216;grip&#8217; in the middle, and one for the rest set via the css. Although this feels a bit over the top it works for a scrollbar of any length. In order to do this the code optionally appends three images into the scrollbar handle html after instantiating the slider.<\/p>\r\n<\/div>\r\n\r\n<h4 class=\"active\">Styling the scrollbar <a href=\"\/demos\/vertical_scrollbar_demo_2_colouredscrollbar.htm\" title=\"Colouring the scrollbar\">Demo<\/a><\/h4>\r\n<div class=\"changelog_detail\">\r\n<p>This is just an additional piece of css:<\/p>\r\n<pre>.slider-wrap{background-color:#EA9531;}<\/pre>\r\n<p>Originally in response to <a href=\"#comment-1892\">this question<\/a> this allows one side of the scrollbar to extend &#8211; easier to view than describe.The slider already adds an extra element <code>.ui-slider-range<\/code> which in the standard css is coloured the same as the scrollbar background &#8211; so it&#8217;s just a question of changing one of these. IE6 does odd things during the scroll though, which I can&#8217;t fix, suggestions welcome.<\/p><\/div>\r\n\r\n\r\n<\/div>\r\n\r\n<div id=\"changelog\">\r\n<h3>Changelog<\/h3>\r\n<h4 class=\"active\">6 March 2012 &#8211; consolidated all variations to use standard code<\/h4>\r\n<div class=\"changelog_detail\"><p>Mostly in order to make keeping these things up to date a bit easier, I&#8217;ve consolidated all the different variants to use a single version of code, which supports everything. There&#8217;s no real change to the mechanics of the slider, just how it&#8217;s packaged up. Also tidied up two bits of ancillary code, specifically on the scrolling with buttons example.<\/p><\/div>\r\n<h4 class=\"active\">28 February 2012 &#8211; upgrade to jQuery\/UI\/mousewheel<\/h4>\r\n<div class=\"changelog_detail\"><p>I&#8217;ve upgraded all the demos to use jQuery 1.7.1, jQuery UI 1.8.14 and Mousewheel 3.0.6. At the same time I&#8217;ve wrapped the mousewheel code in a conditional to stop any errors if the mousewheel plugin is not loaded, and amended the mousewheel code so that it copes better with variances in the scrolled content &#8211; the previous code resulted in a 5% vertical shift on a mousewheel rotate &#8211; this now varies dependent on the difference in size between the scrolling element and its content.<\/p>\r\n<\/div>\r\n<h4 class=\"active\">10 January 2012 &#8211; tweaks to mousewheel code<\/h4>\r\n<div class=\"changelog_detail\"><p>I&#8217;ve amended the mousewheel code in three examples where the slider set up may be called more than once, as previously repeated calling of the code resulted in very fast mousewheel scrolling. Examples affected are <a href=\"\/demos\/vertical_scrollbar_demo_7_addcontent.htm\">Add Content<\/a>, <a href=\"\/demos\/vertical_scrollbar_demo_10_windowresize.htm\r\n\">Window Resize<\/a> and <a href=\"\/demos\/vertical_scrollbar_demo_12_hiddendivs.htm\">Hidden Divs<\/a>.<\/p>\r\n<\/div>\r\n<h4 class=\"active\">9 November 2011 &#8211; packaged as a plugin<\/h4>\r\n<div class=\"changelog_detail\"><p>Finally packaged this up as a <a href=\"\/blog\/vertical-scrollbar-plugin-using-jquery-ui-slider\/\">plugin<\/a>.<\/p><\/div>\r\n<h4 class=\"active\">1 March 2011 &#8211; improved handling of clicks outside handle and mousewheel<\/h4>\r\n<div class=\"changelog_detail\"><p>In the process of sorting out the Opera issues below, I&#8217;ve made a couple of improvements. Previously, clicks beyond the handle, and beyond the actual slider (which is less high than <code>#slider-wrap<\/code> to accommodate the handle) weren&#8217;t handled very well, and didn&#8217;t work in IE. They do now, by looking at the point at which the click was made and moving the slider appropriately. This replaces the changes made 16 January 2011. I&#8217;ve also simplified the mousewheel code to move <code>#scroll-content<\/code> from the slider by using the <code>change<\/code> option &#8211; see the code above, which replaces original code added 4 August 2010. These changes are now applied to all the demos.<\/p><\/div>\r\n\r\n<h4 class=\"active\">27 February 2011 &#8211; amendments for Opera 11 issues<\/h4>\r\n<div class=\"changelog_detail\"><p>Following a contact from a user reporting difficulties with Opera 11 when resizing the scroll content, I have amended the demos for both this and for the multiple slider. Opera 11 doesn&#8217;t like the scrollbar being absolutely positioned, it seems, so all demos are now updated to position the scrollbar differently. This doesn&#8217;t significantly affect the javascript. This wasn&#8217;t an issue with Opera 9 or Opera 10. Makes a change from IE messing things up I suppose&#8230;<\/p><\/div>\r\n\r\n<h4 class=\"active\">16 January 2011 &#8211; handle clicks at top and bottom of the scrollbar correctly<\/h4>\r\n<div class=\"changelog_detail\"><p><a href=\"#comment-2584\">Ohm-ish points out<\/a> that clicks on the top and bottom of the scrollbar, outside the handle, do not result in a scroll. This is because the slider range is restricted to less than the total height of the scrollbar to allow the handle to fit in.<\/p>\r\n<p>Attempts to use the whole height of the slider work, but then the scrollbar handles doesn&#8217;t track the mouse correctly. Experimentation shows that adding <code>.ui-slider-range<\/code> sorts clicks above the slider range &#8211; this needs the following css:<\/p>\r\n\r\n<pre>.ui-slider-range{position:absolute;width:100%}<\/pre>\r\n\r\n<p>and I&#8217;ve then added a little jQuery to cope with clicks below the slider range:<\/p>\r\n\r\n<pre><span class=\"code_comment\">\/\/position the slider range div at the top of the slider wrap - this ensures clicks above the area through which the handle moves are OK\r\n<\/span>$(\".ui-slider-range\").css({top:-sliderMargin});\r\n\r\n<span class=\"code_comment\">\/\/add a click function to ensure clicks below the area through which the handle moves are OK\r\n<\/span>$(\"#slider-wrap\").click(function(){<span class=\"code_comment\">\/\/this means the bottom of the slider beyond the slider range is clicked, so move the slider right down\r\n<\/span>   $(\"#slider-vertical\").slider(\"value\", 0);\r\n   $('#scroll-content').css({top:-difference});   \r\n})<\/pre>\r\n\r\n<p>This has now been added to all the demos and the code shown above.<\/p><\/div>\r\n\r\n<h4 class=\"active\">15 November 2010 &#8211; ensure the handle height margins are correct<\/h4>\r\n<div class=\"changelog_detail\"><p>Thanks to <a href='#comment-2117'>Thom<\/a> for a suggested addition to ensure the handle height is exactly divisible by two. Without this, browsers other than FF show a 1px gap under the slider handle when it&#8217;s at the bottom of the slide.<\/p><\/div>\r\n\r\n<h4 class=\"active\">8 November 2010 &#8211; allow for the scrollbar not being required<\/h4>\r\n<div class=\"changelog_detail\"><p>Following <a href=\"#comment-2023\">Jorge&#8217;s question<\/a> below, I&#8217;ve amended the code slightly so that the slider is only added if it&#8217;s needed (if the height of <code>#scroll-content<\/code> is greater than the height of <code>#scroll-pane<\/code>), and as part of this added the necessary html for the scroller via jQuery. I&#8217;ve also added a line so the height of the scrollbar is set to the height of <code>#scroll-pane<\/code>.<\/p><\/div>\r\n\r\n<h4 class=\"active\">8 September 2010 &#8211; mouse and scrollbar synced correctly<\/h4>\r\n<div class=\"changelog_detail\"><p>Following <a href=\"#comment-1713\">Rob&#8217;s comment (#5 below)<\/a> I&#8217;ve amended both the css and javascript to fix a problem where the mouse and scrollbar weren&#8217;t in sync. Both the code on this page and the demo are updated.<\/p><\/div>\r\n\r\n<h4 class=\"active\">4 August 2010 &#8211; mousewheel integration<\/h4>\r\n<div class=\"changelog_detail\"><p>Following <a href=\"#comment-1339\">Yves&#8217; comment below<\/a> I thought I&#8217;d have a go with adding mousewheel functionality as well. I used Brandon Aaron&#8217;s <a href=\"http:\/\/plugins.jquery.com\/project\/mousewheel\" title=\"Mousewheel plugin\">mousewheel plugin<\/a>.<\/p>\r\n\r\n<p>Then I&#8217;ve added the following additional javascript:<\/p>\r\n\r\n<pre>\r\n$(\"document\").mousewheel(function(event, delta){\r\n\r\n     var speed = 5;<span class=\"code_comment\">\/\/set the speed of the scroll\r\n<\/span>     var sliderVal = $(\"#slider-vertical\").slider(\"value\");<span class=\"code_comment\">\/\/read current value of the slider\r\n<\/span>\t\t\r\n     sliderVal += (delta*speed);<span class=\"code_comment\">\/\/increment the current value\r\n<\/span> \r\n     $(\"#slider-vertical\").slider(\"value\", sliderVal);<span class=\"code_comment\">\/\/and set the new value of the slider\r\n<\/span>\t\t\r\n     var topValue = -((100-sliderVal)*difference\/100);<span class=\"code_comment\">\/\/calculate the content top from the slider position\r\n<\/span>\t\t\r\n     if (topValue&gt;0) topValue = 0;<span class=\"code_comment\">\/\/stop the content scrolling down too much\r\n<\/span>     if (Math.abs(topValue)&gt;difference) topValue = (-1)*difference;<span class=\"code_comment\">\/\/stop the content scrolling up too much\r\n<\/span>\t\t\r\n     $(\"#scroll-content\").css({top:topValue});<span class=\"code_comment\">\/\/move the content to the new position\r\n<\/span>\r\n     event.preventDefault();<span class=\"code_comment\">\/\/stop any default behaviour\r\n<\/span>\r\n});<\/pre>\r\n\r\n<p>I&#8217;ve kept this code separate from the rest so you can use it as required. It&#8217;s included in the <a href=\"\/demos\/vertical_scrollbar_demo.htm\" title=\"Vertical scrollbar demo\">demo page<\/a>. The mousewheel works anywhere on the page on the demo &#8211; to restrict it to operate only on the scrolling div itself the opening statement would be amended to:<\/p>\r\n\r\n<pre>$(\"#scroll-pane,#slider-wrap\").mousewheel(function(event, delta){...etc<\/pre>\r\n<\/div>\r\n<\/div>\r\n\r\n<script type=\"text\/javascript\">\r\n$(document).ready(function(){\r\n   $('.changelog_detail').hide();\r\n   $('#changelog h4,#variations h4').removeClass('active').append(' <\\span>[<\\span>show details<\\\/span>]<\\\/span>');\r\n   $('#changelog h4 span span,#variations h4 span span').click(function(event){\r\n          if(!event.detail || event.detail==1){\r\n          $(this).parents('h4').next().slideToggle();\r\n          $(this).parents('h4').toggleClass('active');\r\n          $(this).text($(this).text() == 'show details' ? 'hide details' : 'show details');\r\n          return false;\r\n          }\r\n   });\r\n})\r\n<\/script>","protected":false},"excerpt":{"rendered":"<p>Also available as a jQuery plugin. Here&#8217;s my attempt at using jQuery UI Slider as a vertical scrollbar. I began this in 2010 by adapting the code on the jQuery UI site for a horizontal slider. This worked, but seemed unnecessarily complex, so I had another go, starting from the vertical slider code. Since then [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-779","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/779","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/comments?post=779"}],"version-history":[{"count":8,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/779\/revisions"}],"predecessor-version":[{"id":2941,"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/pages\/779\/revisions\/2941"}],"wp:attachment":[{"href":"https:\/\/www.simonbattersby.com\/blog\/wp-json\/wp\/v2\/media?parent=779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}