> Spinning wheel on webkit for iPhone/iPod touch

Slot Machine

I’m more and more amazed by the power of mobile webkit. I don’t love the iPhone that much, but as a web developer the beauty of CSS transitions, animations and transforms can’t pass unnoticed. This time I’m giving away a widget that resembles the native Picker View (UIPickerView) functionality but entirely built on javascript.

First things first. Have a look at the demo page or, if you don’t have your device at hand, watch the screencast I baked for you.

Please note that the script is in beta phase, I am publishing it to receive feedback and suggestions. In the next few days we should have a stable version.

Update 2009/03/16: We are out of beta, the script is now stable and ready for production (hope so :) ). In the zip I also included a minified (9kb) version of the script to save some bits of precious bandwidth.

Update 2009/04/19: the script is now compatible with full screen mode apps. It seems that in full screen mode preventDefault() and stopPropagation() placed in touchStart event are not enough to block the propagation of the touch event. Adding preventDefault() to touchMove event solved the problem.

Update 2009/06/18: the script has been updated for OS3.0 compatibility.

Update 2009/07/09: it turned out that Apple is well aware of the webkit refresh bug, fortunately they are kind enough to offer a workaround. All we have to do is to use translate3d instead of translate and all our animations will be fluid as never before! Go get the latest version of the script!

How to use the script

The widget is composed of two parts: the stylesheet and the javascript. No HTML is needed as all the elements are created by the script on the fly. Include both the JS and the CSS into your page and you are ready to spin. You’ll be also surprised to see that the spinning wheel itself is built with just two images, while other three images are needed for the header and buttons. The PNGs altogether are 4.3kb.

The code does not need initialization on window load. You cannot have more than one picker at a time, so the SpinningWheel object is unique and it is created as soon as you include the JS file.

The first thing you need to do is to define the slots with:

SpinningWheel.addSlot(obj values, str styles, str defaultValue)

values is in the form of: { key: value, key2: value, ... }. Keys are the identifiers that won’t be shown in the picker (think of them as the value parameter in the <option value="foo">bar</option> tag). Values are the labels printed on the slots.
styles is a list of space separated predefined styles to be applied to the slot. The available values are:

  • right, align text inside the slot to the right;
  • readonly, the slot can’t be spun;
  • shrink, shrink the slot width to the minimum possible.

The first element of the slot will be selected if no defaultValue is defined.

When all the slots have been created, set the default actions for the cancel and done buttons.

SpinningWheel.setCancelAction( function(){ } );
SpinningWheel.setDoneAction( function() { } );

Finally show the picker:


Voila, the Picker View is ready for countless hours of spinning pleasure.

To get the actual selected values call:

var result = SpinningWheel.getSelectedValues();

result.keys will be filled with an array of the selected keys while result.values will hold the list of the selected values (or labels).

Let’s wrap everything together.

function swExample() {
	var numbers = { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9 };
	SpinningWheel.addSlot(numbers, 'right');
	SpinningWheel.addSlot(numbers, 'right');
	SpinningWheel.addSlot({ separator: '.' }, 'readonly shrink');
	SpinningWheel.addSlot(numbers, 'right');
	SpinningWheel.addSlot({ Kg: 'Kg', Lb: 'Lb', St: 'St' }, 'shrink');



function done() {
	var results = SpinningWheel.getSelectedValues();
	alert('values:' + results.values.join(', ') + ' - keys: ' + results.keys.join(', '));

function cancel() {

Look at the demo for more examples.

Create custom styles

I preconfigured for you three styles for the slots, but you can add as many as you need. Say you want a slot with center aligned text. Add the following to the stylesheet:

#sw-slots .sw-center { text-align:center; }

To apply the style create the slot like this:

SpinningWheel.addSlot(values, 'center');

A piece of cake.

By default the slots try to fit their content. Slots with long text will be wider than ones with short text. (Same as the <table /> cell elements). With custom style you can override this behavior. If you have two slots and you want them to be exactly the same width you may add the following style:

#sw-slots .sw-50percent { width:50%; }

and create the slots with:

SpinningWheel.addSlot(values, '50percent');

You don’t need to apply the style to both slots as the second will fit the remaining space (or the other 50% of the screen width).


None that I can tell if not those imposed by the device small CPU. All animations are hardware accelerated, the “birth date” example in the demo creates more than one hundred elements and all animations are pretty fluid.

The script is also compatible with both landscape and portrait mode and you can freely switch from one to the other while the spinning wheel is opened. (That is more than what the native Picker View has to offer).

Note that once closed the spinning wheel is completely inaccessible and all variables will be null or undefined. So basically you can’t programmatically query the SpinningWheel object while it is not visible.

I’m now working on code optimization to reduce memory usage, I hope to release a stable version as soon as possible. The code is now stable, please leave your comments and suggestions.

As always released under MIT license for all your coding needs.

Download the script

/Share the joy


    • Author: Tammy
    • Posted on: 2011/06/15
    • At: 17:28

    This looks great!
    Can it be used for the blackberry as well?


    • Author: Andrew
    • Posted on: 2011/06/19
    • At: 22:28

    I can’t get it to work after upgrading to the latest version of JQTouch (Beta 3). Does anyone have a fix for this?

      • Author: tortorse
      • Posted on: 2011/09/01
      • At: 12:04

      Same problem.
      I has modified some css,it looks ok,but i can not choose any item.

        • Author: Hong
        • Posted on: 2011/09/26
        • At: 09:21

        Same here. All ideas are welcome.

    • Author: Patrick
    • Posted on: 2011/06/24
    • At: 09:33

    very nice! It looks great!
    Just a request: when spinning the wheel, a ‘click’ sound will be also good as in the Iphone’s calendar.

  • I have modified the tapUp event to allow for cancelling of the event (for validation purposes).
    This allows me to return true/false from the done/cancel events to prevent the spinner from closing (in the event of a validation issue).

    tapUp: function(e) {
    var ok = true;

    if (e.currentTarget.id == ‘sw-cancel’) {
    ok = this.cancelAction();
    } else {
    ok = this.doneAction();

    if (ok) {
    else {

    • Author: Mike
    • Posted on: 2011/07/01
    • At: 22:07

    Anyone know if I can use this as a date AND time picker?

    I’m trying to do the following, but without any success…I still only get 3 slots.

    function openDateTime() {
    var now = new Date();
    var minutes = { };
    var hours = { };
    var days = { };
    var years = { };
    var months = { 1: ‘Jan’, 2: ‘Feb’, 3: ‘Mar’, 4: ‘Apr’, 5: ‘May’, 6: ‘Jun’, 7: ‘Jul’, 8: ‘Aug’, 9: ‘Sep’, 10: ‘Oct’, 11: ‘Nov’, 12: ‘Dec’ };

    for( var i = 0; i < 60; i += 1 ) {
    minutes[i] = i;

    for( var i = 1; i < 24; i += 1 ) {
    hours[i] = i;

    for( var i = 1; i < 32; i += 1 ) {
    days[i] = i;

    for( i = now.getFullYear()-10; i < now.getFullYear()+1; i += 1 ) {
    years[i] = i;

    SpinningWheel.addSlot(years, 'right', now.getFullYear());
    SpinningWheel.addSlot(months, '', now.getMonth()+1);
    SpinningWheel.addSlot(days, 'right', now.getDate());
    //SpinningWheel.addSlot(hours, 'right' 1);
    //SpinningWheel.addSlot(minutes, '', 0)



      • Author: Mike
      • Posted on: 2011/07/01
      • At: 22:10


      SpinningWheel.addSlot(years, ‘right’, now.getFullYear());
      SpinningWheel.addSlot(months, ”, now.getMonth()+1);
      SpinningWheel.addSlot(days, ‘right’, now.getDate());
      SpinningWheel.addSlot(hours, ‘right’, now.getHours());
      SpinningWheel.addSlot(minutes, ”, now.getMinutes())

      I still only get 3 columns.

        • Author: Mike
        • Posted on: 2011/07/28
        • At: 02:05

        Disregard this comment. Please see the comment below on 2011/07/28 at 02:02.

    • Author: nughett
    • Posted on: 2011/07/04
    • At: 15:24

    Hello, really nice script you’ve got here. I wanted to ask, is there a way to show the picker right after the window is loaded and place it on specific position? Because I want it permanently on my page right after it loads up and it should be in the middle of the page.


      • Author: BarS
      • Posted on: 2011/07/11
      • At: 08:26

      Here same thing. Would like to position the spinner where I like it, and I want to provide my own button for using the value of the spinner. How to switch off the button header ?

      • Author: ifriz
      • Posted on: 2011/08/17
      • At: 18:08

      I second this request.

    • Author: Erica
    • Posted on: 2011/07/13
    • At: 16:07

    So sorry for my newbishness,

    I want to include this in my Hybrid app. I have started programming my Hybrid app with the iWebKit framework because I am in the process of getting the iPhone developers licence. Please let me know if this is the wrong order of doing things, I am very new to this.

    So I understand that this widget requires no HTML. But all the pages created with HTML. How do I include the Javascript into the HTML file? I have tried including the JS file, the CSS file, and a function called openDateWheel which is written based off of the openBirthDate in the demo. But I cannot get it to work. Please help.

    • Author: Mark
    • Posted on: 2011/07/21
    • At: 10:10

    Nice work, but when I play around with it for a while on iPhone 4 … it freezes, i just open the demo page, scroll a bit, and it freezes… is there any explanation for this behaviour?

    • Author: Mihai
    • Posted on: 2011/07/26
    • At: 04:43

    Spinning is way too fast on my iPhone 3GS with iOS 4.

    The native picker has a mode in which it shows day of week, day, month, time, am/pm indicator. Further, if I scroll it down past December and into January of the year after, it correctly updates the year. These would be nice features for your date picker.

    • Author: Joe
    • Posted on: 2011/07/27
    • At: 16:06

    Great tool! I am using this as a date picker and want to add a third button between “Cancel” and “Done” that is for “Today”. Clicking the button would return today’s date. I’m having trouble adding code in the .js file that would actually be called when the new “Today” button would be clicked. I’ve attempted copying the “DoneAction” (and modifying), but not having luck.

    Any suggestions for adding a third button ?

    • Author: Mike
    • Posted on: 2011/07/28
    • At: 02:02

    Tip: If you’d like to create a date-time picker using this script, it can be done. By default, the output isn’t a correctly formatted date. So, what you need to do is build the results variable manually instead of joining them with a single separator.

    Here’s the code I use to build a date-time in the format of day-month-year(space)hour:minute.

    function DateDone() {
    var results = SpinningWheel.getSelectedValues();
    var day = results.values[0];
    var month = results.values[1];
    var year = results.values[2];
    var hour = results.values[4];
    var minute = results.values[5];
    var datetime = day + '-' + month + '-' + year + ' ' + hour + ':' + minute;
    document.getElementById('Date').value = datetime;

    You just need to change the order of the variables constructing datetime to suit your needs.

    Also, notice that I skip results.values[3]. This is because I have a visual separator in my spinning wheel to separate the date and time values. Currently, it’s a forward slash (/) but I’m looking for a way to just use a null slot instead because I like the way it looks better than without any kind of separator and better than any other separator I’ve tested. That is:

    SpinningWheel.addSlot(years, 'right', now.getFullYear());
    SpinningWheel.addSlot('', 'right', '');
    SpinningWheel.addSlot(hours, 'right', now.getHours());

    However, this presently confuses the spinning wheel into displaying a date different from what it outputs. Specifically, it outputs the date below what is indicated. So, if 27-Jul-2011 19:30 is indicated, it ends up outputting 28-Aug-2012 20:31.

    If anyone has any ideas, I’d appreciate some feedback. You can reach me at highflight at gmail dot (.) com.

    • Author: Saq
    • Posted on: 2011/08/01
    • At: 17:23

    Struggling with getting this to display in a custom position (top of screen) and still be functional. Anyone had any luck with this?

    I can re-position it easily enough but once I do slots stop scrolling. Any pointers greatly appreciated.

    Thanks for sharing this with us Matteo!

    • Author: Umair
    • Posted on: 2011/08/08
    • At: 16:02

    I wonder if I can modify your plugin for other browsers like IE, FF, Chorme and Safari easily or do I need to write one from scratch?

    • Author: Max
    • Posted on: 2011/08/09
    • At: 15:48

    For some reason this scroller flies from top of the screen on Android 2.2.

    Someone has the same bug?

    • Author: Dee
    • Posted on: 2011/08/19
    • At: 09:01

    @Matteo: This widget works very well in iphone and Android phones. I tested it out in an Android tablet (Samsung Galaxy Tab 10.1), and the wheels wont spin. Are tablets not supported ?

    • works on eeepad. must be a samsung specific bug

    • Author: Steve
    • Posted on: 2011/08/24
    • At: 01:05

    How do you put the result into an input field?
    Changing this: results to:

    does not work.


    • Author: Steve
    • Posted on: 2011/08/24
    • At: 01:06

    Changing this: results to:

    does not work. (I added spaces before and after the to make them show up here.)


    • Author: Steve
    • Posted on: 2011/08/24
    • At: 01:08


    does not work.


    • Author: Steve
    • Posted on: 2011/08/24
    • At: 01:08

    Rats! I can’t get the code to show up.

    • Author: Guy
    • Posted on: 2011/08/31
    • At: 20:25

    Any easy way to implment it inline? I was thinking to change the document.body.addChild to some container but it still not showing well.

    Great work though, thanks

    • Author: Raoul
    • Posted on: 2011/09/04
    • At: 13:38

    Very nice script. Here, a video of my adaptation for personnal use and non yet active website, in the second part of the video:


    Quiete easy to enter into the code, thanks to your explanation inside it. Dealing now to try to have slots with icons instead of text. That will be more sexy.
    The other things to be done is to retain the selcted language and to change it inside the slot. Definitely not so complex.
    And maybe from this script, could be done a full adaptation for list scrolling… Just for the pleasure. Will keep u up to date.
    Thanks again.

    • Author: Sam
    • Posted on: 2011/09/15
    • At: 03:24

    Thank Cubiq, all your scripts are magical, looking forward your next releases

    • Author: Matthew
    • Posted on: 2011/09/15
    • At: 05:38

    Is it possible to use this spinning wheel library inline rather than have it as a popup? It would nice if it had two more functions defined show() and hide() so that it could used as an inline component. Does anyone know if someone has forked this library with this functionality?

    • Author: Gavin
    • Posted on: 2011/09/19
    • At: 14:13

    This is great but how is the positioning of the wrapper determined? I’m trying to use it on my web app and the wrapper appears halfway off the bottom of the screen so it’s unsable.

    Is there and easy way to move it up?

  • Hi,
    I am using jQtouch with spinnig wheel;
    while at the same pannel and my spinning wheel is still showing i would like, using javascript, to trigger the cancel button ,so as to hide the wheel just before leaving the pannel .
    Is ther any way to do that ??
    knowing that i tried sw.close() that hides the wheel but next time i come to the same pannel my sw.open() wont show the wheel any more,

    Thanks in advance for your reply

    • Author: Chris Walker
    • Posted on: 2011/09/26
    • At: 17:59

    This is awesome, not wanting full browser capability but its easy to make the done/cancel buttons at least close it on a browser:

    in handleEvent add:

    else if (e.type == ‘click’) {
    if(e.target.id == ‘sw-cancel’ || e.target.id == ‘sw-done’) {

    and in create() add:

    document.getElementById(‘sw-cancel’).addEventListener(‘click’, this, false);
    document.getElementById(‘sw-done’).addEventListener(‘click’, this, false);

    • Author: sam200745
    • Posted on: 2011/10/01
    • At: 21:12

    this spinnig wheel doesnot dispatch a change event,
    how can i get that the dates in the control has changed?

    • Author: Tony
    • Posted on: 2011/10/17
    • At: 11:36

    Is there anyone successful with creating loop around wheel like the iphone date time picker?

    Any suggestion or guide will be very very appreciated.

    Thanks a lot in advance

      • Author: mh
      • Posted on: 2011/11/17
      • At: 05:45

      I second this request

    • Author: Yuval
    • Posted on: 2011/10/21
    • At: 22:25

    I can’t make the defaultValue parameter work right.

    I try giving it one of the values in the values object – but it still starts from the first value when the wheel is shown…

    (this is great work, by the way).

    • Author: Sezgin
    • Posted on: 2011/10/24
    • At: 17:45


    How to select today ?

    • Author: Ben Bart
    • Posted on: 2011/11/09
    • At: 19:59

    I modified this so that when you update a slot, you can reload that data in other columns. This might be helpful to someone.

    For example, if there are only 28 days in a month, you could have a check for each month and update the wheel accordingly with a check.

    SpinningWheel.slotEl[0].addEventListener(‘webkitTransitionEnd’, checkSlot1ForUpdate, false);

    Because I wanted this to use the transitionEnd event, I added it to the core library to fire when the wheel changed, but doesn’t animate. (For example, when you get it exactly in the right spot, it doesn’t need to move the wheel at all.)

    So, within the scrollEnd function, add an Else statement

    // The drag session was too short
    if (scrollDistance -this.cellHeight / 1.5) {
    if (this.slotEl[this.activeSlot].slotYPosition % this.cellHeight) {
    this.scrollTo(this.activeSlot, Math.round(this.slotEl[this.activeSlot].slotYPosition / this.cellHeight) * this.cellHeight, ’100ms’);
    // dispatch a custom event because the slotY position won’t animate… this allows me to listen for webkit animation events outside of the class
    var newEvent = document.createEvent(“WebKitAnimationEvent”);
    newEvent.initWebKitAnimationEvent(“webkitTransitionEnd”, true, true, window, 0);

    Then, outside the class. I record the positions, and reload the data, close the wheel, and reopen it.

    function checkSlot1ForUpdate(e)

    if(SpinningWheel.getSelectedValues().keys[0] != currentFirstSlot)
    currentFirstSlot = SpinningWheel.getSelectedValues().keys[0];
    currentSecondSlot = 0;

    var currentSlotPosition = window.getComputedStyle(SpinningWheel.slotEl[0]).webkitTransform;
    currentSlotPosition = new WebKitCSSMatrix(currentSlotPosition).m42;

    updateLetters(sectionNames[currentFirstSlot], cubeNames[0]);

    SpinningWheel.slotEl[0].removeEventListener(‘webkitTransitionEnd’, checkSlot1ForUpdate, false);
    SpinningWheel.slotEl[1].removeEventListener(‘webkitTransitionEnd’, checkSlot2ForUpdate, false);
    SpinningWheel.animationEnabled = false;
    SpinningWheel.addSlot(slot1Array, ‘fixedWidthRight’);
    SpinningWheel.addSlot(slot2Array, ‘fixedWidthRight’);


    SpinningWheel.setPosition(0, currentSlotPosition);

    SpinningWheel.animationEnabled = true;
    SpinningWheel.slotEl[0].addEventListener(‘webkitTransitionEnd’, checkSlot1ForUpdate, false);
    SpinningWheel.slotEl[1].addEventListener(‘webkitTransitionEnd’, checkSlot2ForUpdate, false);

      • Author: Ben Bart
      • Posted on: 2011/11/09
      • At: 19:59

      Formatting sucks. Sorry!

    • Author: alex
    • Posted on: 2011/11/18
    • At: 13:57

    SpinningWheel.getSelectedValues(); it doesn’t work inside ‘Function done’

    How is possible?!


      • Author: yogesh
      • Posted on: 2011/11/29
      • At: 07:21

      Having a same issue…

      Did you found any solution?

        • Author: yogesh
        • Posted on: 2011/11/29
        • At: 08:14

        Finally I got solution of it.

        If you have same issue please make sure you have not add any prototype methods to Array like

        Array.prototype.someMethod = function(){


        this will break code at line 264 as

        262 line have code like

        for (i in this.slotEl) {…

        • Author: Jeff Barclay
        • Posted on: 2011/12/29
        • At: 03:44

        I’m having the same issue in iphone with phonegap, worked a few months ago, but now getselectedvaules() inside done no longer works. don’t think i have any prototype methods, as my code has not changed…

    • Author: Russ
    • Posted on: 2011/11/19
    • At: 19:16

    great job on the Spinning Wheel. Do you have any plans to add a ‘Next’ button to the spinning wheel that function like a native drop-down box? That would be a nice function to have. I would be happy to do that myself if only I had the javascript skills. :)

    • Author: Guy
    • Posted on: 2011/11/24
    • At: 18:05

    Any way to provide a button that makes the wheel spin and stop randomly on some value?

    Any help/tip would be appricated

    • Author: Vishal
    • Posted on: 2011/11/29
    • At: 07:40

    The moment I open the spinning wheen , after selecting the values I press on Done my UI gets blocked after tapping it for a longer time my UI is fine..? any suggestions

    • Author: LouMazz
    • Posted on: 2011/12/02
    • At: 21:14

    Any way to get this to work with desktop Safari?
    Works nicely on i-devices, but cannot respond to mouse
    events on the desktop.

    • Author: Fred Cox
    • Posted on: 2011/12/16
    • At: 15:24

    I have made an updated version of this with support for desktop browsers and firefox.
    It can also be used within a container.
    You’ll find it on github…


      • Author: LouMazz
      • Posted on: 2012/01/10
      • At: 17:25

      Duh. I just finished getting my mods to run on Safari, and THEN found this. Moral: always check first!

      • Author: Matt
      • Posted on: 2012/02/07
      • At: 23:38

      Hey Fred, great work, How do you set the container for the spinning wheel?

      i.e I have an element and I want it to go in there

    • Author: Praveen
    • Posted on: 2011/12/19
    • At: 11:30

    I am facing totally different behavior on android device. On android device values in slot are either in reverse order or jumbled.

    • Author: Praveen
    • Posted on: 2011/12/21
    • At: 09:06

    Hi! I resolved the issue. It wasn’t spinwheel fault its android platform which smartly sorts the json object which knowing the developers intention :(

    • Author: John
    • Posted on: 2012/01/14
    • At: 18:07

    Is there anyway to have multiple spinning wheels on a page rather than just 1-for instance i have a departure and arrival dates and need to have the user choose two different dates.

    • Author: Jason
    • Posted on: 2012/01/23
    • At: 23:32

    Just wondering what it would take to add Header Title for each Slot. So If I was to have 3 slots for example, Slot 1 title = Score, Slot 2 Title = Sand, and slot 3 = Green.

    Maybe to add the title it would look something like this

    var numbers = { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9 };
    SpinningWheel.addSlot(numbers, ‘right’,default, TITLE);

    I am thinking the title would be place between the header and the spinning wheel.

    Any help how to do this would be helpful.


    • Author: Alex
    • Posted on: 2012/01/26
    • At: 02:13

    I am trying to get this to work on the iPad but can’t seem to change the window location of the sw-wrapper.

    • Hi Alex, for ipad check my mod amenoske.com/ipadspinningwheel/ it transforms the spinnigwheel into a popover, not perfect but works. I include both and use one or another depending the target device.

    • Author: newbie
    • Posted on: 2012/02/14
    • At: 08:43

    How can we apply spinner inline ? Is there any option to apply picker to a particular div, so it will act more like an inline component rather than a sheet.

  • I have added extensions to the spinningwheel to support date, time and custom wheels out of the box. The date wheel I have extended has all the logic one would expect of a standard date picker. Check it out at SpinningWheel extensions

    • Author: lee
    • Posted on: 2012/03/07
    • At: 09:25

    Thanks for your Good Solution.
    I’ve applied your spinning wheel to my app, before.
    Now i’m working on new app with spinning wheel.
    How can i apply to new app in landscape mode ?

    • Author: Jai
    • Posted on: 2012/03/19
    • At: 21:29

    Anyone, here integrated successfully in a sencha touch application. I am finding difficult to make this as a resusable one to use it in sencha touch.

    • Author: De
    • Posted on: 2012/04/11
    • At: 15:33

    awesome.. Tested on blackberry playbook! Im doing an app for bb playbook and this comes in handy.. Thank you so much

    • Author: asmodeus
    • Posted on: 2012/04/13
    • At: 15:53

    ups! I’ve just updated to Android 4.0.3 and SpinningWheel doesn’t work anymore in an web app wrapped with phonegap, ┬┐someone with the same problem?

      • Author: asmodeus
      • Posted on: 2012/04/13
      • At: 16:49

      ups! again: my fault, works great!