Javascript Controls - The Spin Control

Skill

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.

GetContainer

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).

SetBackgroundColor

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.

SetButtonColor

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).

SetFontColor

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.

SetBorderColor

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.

GetAccelerationCollection

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).

GetCount

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

GetIndex

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

RemoveIndex

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

Clear

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

Add

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;
   width:530px;height:40px;">

  <div id="printOut" style="position:absolute;left:290px;top:10px;"></div>
</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.SetMaxValue(9999);
spinCtrl.SetMinValue(32);
spinCtrl.GetContainer().style.position = 'absolute';
spinCtrl.GetContainer().style.left = '15px';
spinCtrl.GetContainer().style.top = '10px';
spinCtrl.AttachValueChangedListener(spinCtrlPrintOut);
spinCtrl.AttachValueChangedListener(spinCtrlSizeBox);
spinCtrl.SetCurrentValue(40);

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');
el.appendChild(spinCtrl.GetContainer());
spinCtrl.StartListening();

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:

spinCtrl.SetBackgroundColor('#EDFCC3');
spinCtrl.SetBorderColor('#148238');
spinCtrl.SetButtonColor('#148238');
spinCtrl.SetFontColor('#555533');

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;
}

.spinInput
{
  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!

reply

Parvaneh
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.

reply

Rohi
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!

reply

Sufiyan
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.

reply

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.

reply

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.

reply

covuk77
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.

reply

Ed
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?

reply

Abubakar Ibrahim
02/26/2008 - 05:18

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

reply

Mshadows
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

reply

Jerone
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

reply

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.

reply

Scott
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?

reply

Anonymous
09/19/2008 - 20:58

Tks & rgds. that code is greate!

reply

Anonymous
10/08/2008 - 18:23

Very nice, thank you!

reply

Abby
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

reply

Raviraj
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,
raviraj

reply

mohammed
02/03/2009 - 07:00

hi
To add disable function,add this to javascript file

  this.SetDisabled = function(boolValue){
        if(boolValue==false){
                this.SetBackgroundColor('#FFFFFF');
                this.SetBorderColor('#A5ACB2');
                this.SetButtonColor('#000000');
                this.SetFontColor('#000000');
                this.StartListening();
        }else{
                this.SetBackgroundColor('#CCCCC');
                this.SetBorderColor('#AAAAAA');
                this.SetButtonColor('#AAAAAA');
                this.SetFontColor('#AAAAAA');
                this.StopListening();
        }
        _textBox.disabled=boolValue;
  }

Now you can use this line to disable spinCtrl:

spinCtrl.SetDisabled(true);

reply

Anthony
03/10/2009 - 19:03

Hi,

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.

reply

Daniel
03/27/2009 - 17:30

Hello,

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?

reply

Sreejith
06/10/2009 - 07:56

Excellent work. Great controll!

reply

Mark
06/11/2009 - 16:16

This Control doesn't appear to work properly in safari

reply

MIke
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.

Thanks.

reply

MIke
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.

reply

Michael
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?

reply

Anonymous
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?

reply

http://hs.linkaty.net
07/18/2010 - 13:25

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

You saved me a lot of time :)

Thank you.

reply

covuk77
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.

reply

Add Comment

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

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors