Javascript Controls - The Spin Control


Javascript Controls - The Spin Control

Posted in:

Today we have a new little javascript ui control to talk about - although the only thing little about it is its actual size on the page. It is a fully featured spin control, or, as some people call it, a numeric up-down control. We have been doing a number of tutorials on javascript user interface elements, and as you may have noticed, they can get quite long. So we thought it was time to take a different tact - where we package up the component as much as possible and instead of spending so much time on how it works, we will spend more time on how to use it. Don't worry though, we will still go into detail on parts of the code that we think are interesting or complex.

And below is your spin control. It is modeled after the standard Windows spin control, so none of the behavior should be surprising. Just by looking at the controls, you probably noticed that they are themeable - you can set the border, background, button, and font color (both using css and in code). You can also set the width, down to a minimum of 25 pixels. Features that might not be immediately noticeable: you can set the minimum, maximum and initial value, and you can also set how much a single up or down click will move the value (the increment amount).

Note: The leftmost spin control controls the height of this box.

You can also do more complex things with how much you want the control to increment - you can give it a set of acceleration values, very similar to the Windows spin control. So you can say things like "after the button is held down for 5 seconds, increment by 10 at a time, and after the button is held down for 10 seconds, increment by 25 a time" - and you can set as many of those type of rules as you want.

The other cool thing about this control is that instead of a single callback function that gets called when the value changes (like most of our other javascript components), this spin control introduces something new. You can 'attach' and 'detach' functions to the 'ValueChanged' event on the spin control, using the attach and detach methods on the spin control object. So, for instance, the leftmost spin control in the example below has two functions attached to its value changed event - one to update the text to the right of the controls, and the other to control the height of the div surrounding the spin controls.

Oh, and by the way, the scroll wheel works too :)

Ok, enough about the features - lets start looking at how to use the features. We are going to start by going over the public functions on the spin control object:

function SpinControl()
  this.GetContainer = function()

  this.GetCurrentValue = function()

  this.SetCurrentValue = function(value)

  this.GetMaxValue = function()

  this.SetMaxValue = function(value)

  this.GetMinValue = function()

  this.SetMinValue = function(value)

  this.GetIncrement = function()

  this.SetIncrement = function(value)

  this.GetWidth = function()

  this.SetWidth = function(value)

  this.SetBackgroundColor = function(color)
  this.SetButtonColor = function(color)
  this.SetFontColor = function(color)
  this.SetBorderColor = function(color)

  this.StartListening = function()
  this.StopListening = function()
  this.AttachValueChangedListener = function(listener)
  this.DetachValueChangedListener = function(listener)

  this.GetAccelerationCollection = function()

Most of these function names are pretty self-explanatory, but I'll give a short sentence or two on each of them.


This function returns the html element that contains the control - in the case of the spin control, it is a div. This element is what you add to the DOM when you want to add a spin control to the page (we will go over how to do that in a little bit).

Get And Set CurrentValue

These two functions do exactly what you might expect - one returns the current value of the spin control, and the other sets the value of the spin control. By default, this value starts off as 1. If you set a value through here, it is still constrained by the max/min set on the control, so if you have a max set of 10 and you try to set the value of the spin control to 20, the value will actually be set to 10.

Get And Set MaxValue

These two functions let you get and set the maximum value for the spin control. By default, the maximum value is 100.

Get And Set MinValue

These two functions let you get and set the minimum value for the spin control. By default, the minimum value is 0.

Get And Set Increment

These two functions let you get and set the increment value for the spin control. By default, the increment value is 1. The increment value is always used for the initial movement of the spin control value when the user clicks on the up or down buttons. If there is no acceleration set (we will get into how to set acceleration later), the spin control will continue to modify the current value by the increment if the user holds down the up or down buttons. As you might expect, the up button adds the increment to the current value, and the down button subtracts the increment.

Get And Set Width

These two functions let you get and set the width of the spin control. By default, the minimum value is 50, and if the minimum width is 25 pixels (i.e., if you pass in a value less than 25, the width will be set to 25).


This function, as you might expect, sets the background color of the spin control. Using this function sets the color for a particular spin control - you can also modify the default background color in the spin control style sheet (which we will take a look at in a little while). The default background color is white.


This function lets you set the color of the up and down buttons on the spin control. As with the background color, you can set the color for all spin controls by modifying the style sheet (the default color is black). For IE6 users, sadly, the buttons will always be black, because we use a transparent png background image to accomplish the styling (and there is no way to make IE6 do background image transparency correctly, even using the filter hack).


Here, you can set the font color. By default, it is black, and you can change the default (as well as the font family and size) in the style sheet.


And the last style function, setting the border color. By default, this is a light grey. Just as a side note, the reason that these style functions are exposed (instead of just changing the styles on the html elements directly) is that most of these style functions changes values on multiple elements, and so it is a nice way to encapsulate the necessary changes.

StartListening and StopListening

These functions attach and detach all the needed events for the spin control to work. When the spin control is first created, it is not listening - after you add it to the DOM, you need to call the StartListening to enable the control. The reason it does not listen initially is that until it is added to the DOM, event attaching does not work. Yeah, we know it is an annoyance, and we are trying to find a clean way around it - for now, youll just have to call StartListening after you add the control.

AttachValueChangedListener and DetachValueChangedListener

These two functions attach and detach functions to the ValueChanged event. The argument is the function that you want to attach (or detach). When the event occurs, the attached functions are called with two arguments - the spin control that is sending the argument, and the new value.


Now we get to the one actually complicated getter. This function returns a reference to the SpinControlAccelerationCollection for the spin control. What in the world is a SpinControlAccelerationCollection, you ask? Well, it is how you set up accelerations for the spin control. So lets take a look at how to work with this new object:

function SpinControlAccelerationCollection()
  this.GetCount = function()
  this.GetIndex = function(index)
  this.RemoveIndex = function(index)
  this.Clear = function()
  this.Add = function(spinControlAcceleration)

These functions just let you interact with the collection, adding and removing acceleration objects (which we will talk about in a moment).


This function simply returns the current number of SpinControlAcceleration objects in the collection.


This function returns the SpinControlAcceleration at an index into the collection. If the index is invalid, it returns null.


This function removes whatever is at the given index from the collection. If it is an invalid index, nothing happens.


This just clears all the SpinControlAcceleration out of the collection, so you are left with an empty collection.


And this is how you add a SpinControlAcceleration object to the collection. This function doesn't simple add the object to the end of the collection - the collection is always sorted based on when the acceleration is supposed to occur. So if you add an acceleration that is supposed to occur after 5 seconds, and then you add one that is supposed to occur after 2.5 seconds, the second one will be before the first in the collection. So now lets take a look at the pretty simple SpinControlAcceleration object itself:

function SpinControlAcceleration(increment, milliseconds)
  this.GetIncrement = function()
  this.GetMilliseconds = function()  

This object just holds two values - an amount to increment, and a time. Essentially, it means that when the given amount of time has passed, start incrementing using the given increment value. Don't worry if this doesn't quite make sense yet - we are about to dive into how to set up an instance of the spin control with some acceleration.

Remember the examples from above? Well, lets take a look at the code to set up those up. First, the leftmost spin control:

First, the initial html code (i.e., the place in the page where the spin control is going to be added):

<div id="spinCtrlContainer"
   style="position:relative;border:1px solid black;

  <div id="printOut" style="position:absolute;left:290px;top:10px;"></div>

A div with a border, and a div to hold that text that gets printed when the spin control changes. Nothing that special. So now for the javascript:

var spinCtrl = new SpinControl();
spinCtrl.Tag = 'left';
spinCtrl.GetContainer().style.position = 'absolute';
spinCtrl.GetContainer().style.left = '15px';
spinCtrl.GetContainer() = '10px';

This is most of the setup code - as you can see, we are setting the max value, the min value, and some position information on the container. We set a "Tag" on the control just so we can easily identify it later. We attach two functions to the value changed listener, and we will take a look at the code for those in a moment. Finally, we set the initial value.

Thats most of the general setup code - now lets look at the code for setting up the acceleration:

spinCtrl.GetAccelerationCollection().Add(new SpinControlAcceleration(1, 500));
spinCtrl.GetAccelerationCollection().Add(new SpinControlAcceleration(5, 1750));
spinCtrl.GetAccelerationCollection().Add(new SpinControlAcceleration(10, 3500));
spinCtrl.GetAccelerationCollection().Add(new SpinControlAcceleration(40, 7000));
spinCtrl.GetAccelerationCollection().Add(new SpinControlAcceleration(80, 10000));

What does this mean? Well, say you pressed down on the up arrow. First, it will increment by 1 - the default increment value for the spin control (which we did not change). Then, after 500 milliseconds, it will start incrementing 1 at a time (approximately 6-7 times per second). Then when 1.75 seconds have passed (since you initially pressed the up arrow) it will start incrementing 5 at a time. Then at 3.5 seconds it will switch to 10 at a time, followed by 40 at a time after 7 seconds. And finally, after 10 seconds have passed, it will increment at 80 at a time, and it will continue at that speed until the spin control reaches its max value or you release the up arrow.

The next thing we have to do is actually add the control to the page:

var el = document.getElementById('spinCtrlContainer');

And finally, we need to define those two functions we attached to the value changed event:

function spinCtrlPrintOut(sender, newVal)
  document.getElementById('printOut').innerHTML =
    'The ' + sender.Tag + ' spin control is now ' + newVal +'.';

function spinCtrlSizeBox(sender, newVal)
  document.getElementById('spinCtrlContainer').style.height = newVal + 'px';

And there you go! That is all you need to do to get that first example up and happy. Lets take a quick look at the second example, and see how to do the theming:

One way to do the theming (and the way it was done for this example) is in the javascript code:


The other way to do the theming (and this would apply to all spin controls on the page) is to change the style sheet:

.spinInput, .spinContainer
  /* Change this to modify the default
   * spin control background color*/

  background-color: #FFFFFF;

.spinLeftRightEdge, .spinTopBottomEdge
  position: absolute;
  overflow: hidden;
  /* Change this to modify the default
   * spin control border color*/

  background-color: #A5ACB2;

  position: absolute;
  top: 1px;
  left: 2px;
  height: 18px;
  border: 0px;
  /* Change these to modify the default spin
   * control font, font color, and font size*/

  color: Black;
  font-size: 9pt;
  font-family: Arial;

.spinUpBtn, .spinUpBtnHover, .spinUpBtnPress, .spinDownBtn,
.spinDownBtnHover, .spinDownBtnPress
  position: absolute;
  width: 15px;
  height: 8px;
  right: 2px;
  background-image: url('spin_control_buttons.png');
  background-repeat: no-repeat;
  /* Change this to modify the default button color*/
  background-color: #000000;

There is a bunch more css in the actual css file for the spin control (of course :P), but what is shown above is the important stuff for theming. Hey, I even threw in some comments!

And that is all there is to it. Here is the source code download link again, which is a zip file that includes the javascript, the css, the button images, and a small example html page. If you have any questions on how to use the control, or any questions on the inner workings, please leave a comment.

Eliza Brock
10/30/2007 - 21:35

Awesome scroll-wheel action!


01/19/2008 - 23:22

Amazing SpinCtrl.Please add the keyup event hook for _textbox to the StartListening function.

hookEvent(_textBox, 'keyup', BoxChange);

in this way when user changes the text box the spin buttons are aware of that and also user can not enter characters other than digits.


02/07/2008 - 00:14

It's really awesome.

Suggestion: It would be much appreciated if you combine the keyboard event like up arrow and down arrow event with the spin control, without which it is only partial.

Do update!


11/14/2008 - 07:17

Is there any update on Rohi's comment. I also need a same kind of functionality (ie. Keyboard up and down arrow keys event's integration with SpinControl). I need it urgently.


Mark Beaty
02/12/2008 - 16:51

Hey, this is a really nice component. I've modified it slightly to allow specification of the id attribute value since I want to be able to submit multiple spinner controls via form submittal.


Mark Beaty
02/12/2008 - 16:53

Looks like my markup didn't come through. Again...

I’ve modified the javascript slightly to allow specification of the input tag id attribute value since I want to be able to submit multiple spinner controls via a form. Hope that makes sense.


08/26/2010 - 03:28

Hi, I'm very new to this. Great code!
How can I move the counter to include it in a table?
Please help. Thanks.


02/20/2008 - 17:02

Neat control. However, I found that if you add the DTD to example.html in the source that you can download,


you'll lose the bottom line of the spin control. Is there a way to fix that?


Abubakar Ibrahim
02/26/2008 - 05:18

your site is great. it's a centre for alot of resource!


04/15/2008 - 10:26

how set or get a value on a control that i create??
for example;

I create a control with tag='Control1'
and i want to change is value with a function in a JS..

please help


05/17/2008 - 09:33

This is a great script, but I do like to know what browser this is tested and known to work on. There's also no license specified.
Adding the mouse up/down suggestion would totally finish it.

gr J


The Reddest
05/19/2008 - 08:09

We usually test the Javascript code under all the major browsers - IE7, FF, Opera, and Safari. All of our code is licensed under BSD, we just haven't gotten around to stating that anywhere.


06/26/2008 - 10:36

I love the control, and want to know if I can override the click events of the up and down buttons to use my own methods. I have some AJAX methods that query various web services, and they return the "Next" and "Previous" available value for the control. I'd like to be able to use your spin control to layout the textbox and buttons, but use my own methods to calculate the highest or lowest value to display in the textbox.

Is this possible?


09/19/2008 - 20:58

Tks & rgds. that code is greate!


10/08/2008 - 18:23

Very nice, thank you!


12/12/2008 - 06:11

“For IE6 users, sadly, the buttons will always be black”

Its work in Firefox but in IE 6, it does not render a black button rather it renders just white background.

Any ideas on how this can be fixed


12/19/2008 - 07:11

hi ,

thanks for proving the information with example .. and please help me how can i use this in multiple text box’s and can u please mail me clock example using spin controls like in system.please please ..

thnaks and regards,


02/03/2009 - 07:00

To add disable function,add this to javascript file

  this.SetDisabled = function(boolValue){

Now you can use this line to disable spinCtrl:



04/11/2011 - 14:12

How do I fetch a reference to an EXISTING spincontrol?

I have a input checkbox, elsewhere on the form - when I check it - I need to disable the spin control. I want to use this method (by Mohammed), but I cannot see how I get a reference to an existing spin control.


03/10/2009 - 19:03


The spin box looks great! But I do not find anyway to connect it to an HTML form or to make it workable with an HTML form.

Is there a complete working example with a textarea ID or textarea name clearly showing it is connected to a form?

I appreciate your help.


03/27/2009 - 17:30


Really nice control.

When I use it I noticed that if you manually mouse click to increment too quickly the some clicks are lost. (I believe this is how users would click the buttons in my application.)

For example on the green middle control above if you set it to zero and the left click the increment button quickly nearly half of the increments are lost i.e. ten clicks result in a value of 5 or 6.

Is there any configuration to address this?


06/10/2009 - 07:56

Excellent work. Great controll!


06/11/2009 - 16:16

This Control doesn't appear to work properly in safari


06/27/2009 - 10:22

Please provide a real world example for beginners. The example you provide does not use this control in a form, which is where it will be used. I can display it on the page and it looks great, but i still cant use it for anything practical.



06/27/2009 - 11:11

FOR beginners having trouble using this spin control in a form check out jquery spin control. It is very easy to implement. I got it up and running in a form in about 10 minutes. it might not have all the bells and whistles as this one but it is usable in a form. good luck.


11/04/2009 - 19:26

So, if I've generated several spin controls in one form, each with their own Tag, and I want to validate the value of each control's input when the user click's the Submit button, how do I go back through each of the spin controls and get their current values? Plus, they're in a table where the number of rows is dynamic, and not known at development time.

Can you give me some direction on this, please?


01/19/2010 - 03:50

I note that in Internet Explorer, when you press the Enter key, the value is not processed, but when you press the Enter key in Firefox, it is.

How do I get this feature to work in Internet Explorer?

07/18/2010 - 13:25

Thank you.
Thank you.
Thank you.
Thank you.

You saved me a lot of time :)

Thank you.


08/26/2010 - 03:28

Hi, I'm very new to this. Great code!
How can I move the counter to include it in a table?
Please help. Thanks.


11/17/2010 - 17:49

Thanks. Nice Code. Is it possible to link this functionality to drive an array of text. I am looking to replace a drop down list of values with a spinning function like this one. One would be able to just rotate through the list values instead of the normal drop down. The current value would then drive other content on the page. Any clues on how to achieve this or if there is another possible solution? Any info would be greatly appreciated.


Jim Andrews
01/30/2011 - 02:25

Many thanks for your fabulous spin control! Best one on the planet, so far as I can tell.

I'm using it in . You can see em if you click "Options". They work like a charm. And are nicely editable with the keyboard.

At the moment, I'm replacing some of my other code with some of yours from this site from another example: your drag code. Initially I used some other site's drag code (for the images) and it works fine but not at all in Opera. I note your drag code works in Opera.

I also note that the drag code uses 3 of the core functions from the spin control example: hookEvent, unhookEvent, and cancelEvent.

Anyway, again, many thanks!


05/04/2011 - 15:06

Hi, I am getting the click event twice in IE 8 only - which causes the spin to increment twice. Works fine in FF. Any quick guesses what I am doing wrong? (because clearly your examples work)


05/04/2011 - 15:18

To update: I do have 4 spins on one page like so:

            <div id="spinCtrlContainer1" class="spinContainer"></div>
            <div style="float:left">&nbsp;</div>
            <div id="spinCtrlContainer2" class="spinContainer"></div>
            <div style="float:left">&nbsp;</div>


 <div id="spinCtrlContainer3" class="spinContainer"></div>
            <div style="float:left">&nbsp;</div>
            <div id="spinCtrlContainer4" class="spinContainer"></div>
            <div style="float:left">&nbsp;</div>


var spinCtrl1 = new SpinControl();
var spinCtrl2 = new SpinControl();
var spinCtrl3 = new SpinControl();
var spinCtrl4 = new SpinControl();

function InitSpins()
    spinCtrl1.Tag = 'startHr';
    spinCtrl1.SetCurrentValue(<?php echo (empty($rsEventToLoad['Events']['EventStartHour'])) ? 9 : $rsEventToLoad['Events']['EventStartHour']; ?>);
    var el = document.getElementById('spinCtrlContainer1');

    spinCtrl2.Tag = 'startMin';
    spinCtrl2.SetCurrentValue(<?php echo $rsEventToLoad['Events']['EventStartMin']; ?>); // will default to 0 if empty
    el = document.getElementById('spinCtrlContainer2');
    spinCtrl3.Tag = 'endHour';

    spinCtrl3.SetCurrentValue(<?php echo (empty($rsEventToLoad['Events']['EventEndHour'])) ? 5 : $rsEventToLoad['Events']['EventEndHour']; ?>);
    el = document.getElementById('spinCtrlContainer3');
    spinCtrl4.Tag = 'endMin';

    spinCtrl4.SetCurrentValue(<?php echo $rsEventToLoad['Events']['EventEndMin']; ?>);  // will default to 0 if empty
    el = document.getElementById('spinCtrlContainer4');

function spinCtrl1Marshal(sender, newVal)

function spinCtrl2Marshal(sender, newVal)

function spinCtrl3Marshal(sender, newVal)

function spinCtrl4Marshal(sender, newVal)


11/01/2011 - 10:42

How do you attach a listener to a control already on a page?


12/07/2011 - 22:16

Just wondering if it's a design decision or a bug that when you click on the control and use '2 finger' scrolling on the mac (which, I assume, is like the wheel), that the increments are in very small units (.00x or so) even though I have explicitly called SetIncrement(1).

I'm trying to use this as a SpinControl for integer-only values and it's not working as I expected in this one condition.


- J -


Michael Mabin
06/09/2012 - 14:32

I was able to overcome this by adding the following code to the StartListening method:

hookEvent(_upButton, 'mousewheel',UpPress);
hookEvent(_downButton, 'mousewheel', DownPress);


Naiyer Aazam
12/10/2011 - 04:24

How to get value after submitting the form.


02/04/2012 - 11:45

THANK YOU!!! Great control!


04/17/2012 - 08:00

This really is a great ui control. By way of the tutorial I've been able to configure it ina wide variety of ways. HOWEVER, I'm totally stumped as to how to add a text label to the spin object. For example...

Month: [12]  Day: [25]  Year: [2012]

The placement of a text label attached to the spin control seems like a very simple thing that was overlooked. I'm baffled...

It's a shame that there is no way for us visitors to discuss it in an open forum -- share ideas, pose problems, present solutions, etc. I see LOTS of questions and ZERO answers.

It's cool that SOTC got this to work as well as it does and that they shared it with us in the form of a very comprehensible tutorial, but the spin control in real world situations seems almost clunky, at best... but mostly because it is presented as "we got it to work, here it is -- now you're on your own". :/


06/28/2012 - 23:37

Very nice and flexible spinner, thankyou so much for it :)

For anyone who wants, just add this to bottom of your page to make sure characters other than numbers cannot be inputted.

<script type="text/javascript">
function valid_num(evt)
var charCode = (evt.which) ? evt.which : event.keyCode;
return (charCode > 31 && (charCode < 48 || charCode > 57)) ? false : true;
spininputarr = document.getElementsByClassName('spinInput');
for(var ai in spininputarr){spininputarr[ai].setAttribute("onkeydown", "return valid_num(event);");}


10/30/2012 - 09:28

Someone has a working example?


Add Comment

Put code snippets inside language tags:
[language] [/language]

[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.
This question is for testing whether you are a human visitor and to prevent automated spam submissions.