> 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:

SpinningWheel.open();

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');

	SpinningWheel.setCancelAction(cancel);
	SpinningWheel.setDoneAction(done);

	SpinningWheel.open();
}

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

function cancel() {
	alert('cancelled!');
}

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');
SpinningWheel.addSlot(values);

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

Limitations

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

/Reactions

    • Author: Mike
    • Posted on: 2009/08/25
    • At: 16:57

    Any helpthe above would be HUGELY appreciated
    : )

  • @mike: syntax to open a link from JS is window.location.href="http://example.com";. You probably want to pass the selected values to the page. Something like window.location.href="http://example.com?value=" + results.value[0]; should work.

    • Author: Mike
    • Posted on: 2009/08/27
    • At: 10:45

    That’s great – thanks for the help Matteo!

    • Author: ska_iit
    • Posted on: 2009/09/01
    • At: 18:08

    This is really wonderful Iphone WebApp for date picker and any other selection u need. I made it work perfectly with iwebkit and it works like charm. Please contact me if you need any help on how to use it with iwebkit. I made few changes to create a proper date picker with only the valid date selection possible.

      • Author: dp
      • Posted on: 2010/09/17
      • At: 11:28

      hello ska_lit, i am working on an iWebKit site and want to integrate iScroll as well. i would really appreciate any sample code or help you’ve shared with others on how you got them to work together?

  • @ska_iit: if you want to share a mod to this script please send it to me (hint: matteo at-this-domain), I’ll be glad to publish it (please remember that I release all the scripts under the MIT license).

    • Author: matt
    • Posted on: 2009/09/08
    • At: 16:52

    I am interested in knowing how to validate the days in a month

    for instance if sept is picked only show 30 days
    or october show 31 days, and so on

    is this possible with this script?

    • Author: Dave
    • Posted on: 2009/09/09
    • At: 03:45

    Hi, Great piece of work!

    Any advice on getting this to work in jQTouch framework? Would look great on a project I am working on.

    Dave

    • Author: Dave
    • Posted on: 2009/09/09
    • At: 03:46

    Ska, can you share your tweaks?

    • Author: matt
    • Posted on: 2009/09/09
    • At: 13:06

    @Ska

    i would also like to see your tweaks, thanks

    • Author: ska_iit
    • Posted on: 2009/09/09
    • At: 18:11

    Thx guys for showing interest… please let me know how and where to send the updated sample?

      • Author: bjornhart
      • Posted on: 2011/02/01
      • At: 21:15

      Hi ska_itt

      Will you please send me your code for making a proper date picker with only the valid date selection possible.
      david (at) bjornhart.dk, thanks!

    • Author: matt
    • Posted on: 2009/09/09
    • At: 18:54

    can send me an email at, mylivingweb (at) gmail.com

    i would greatly appreciate it.

    matt

    • Author: Dave
    • Posted on: 2009/09/09
    • At: 19:26

    ska_iit

    please send to dfafard – gmail

    thanks!
    Dave

    • Author: ska_iit
    • Posted on: 2009/09/10
    • At: 01:00

    Hi Matteo,
    I sent one test email to you, let me know if you received it.. then I will send the code also.

    Dave, Matt
    I sent the sample code to the above given email addresses.
    -SKA

    • Author: Dave
    • Posted on: 2009/09/10
    • At: 03:18

    Thanks SKA …

    Still no luck for me in the jQtouch framework.

    Dave

    • Author: ska_iit
    • Posted on: 2009/09/10
    • At: 18:01

    I have not tried with the JQtouch..i made it work with the iwebkit only

    • Author: Santangeli
    • Posted on: 2009/09/13
    • At: 23:14

    I’m curious , does anyone make it work in a UIWebView ?

  • Santangell – I’ve tried it in a UIWebView and it works fine. I’ve also seen it it this app:
    http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=325676875&mt=8

    • Author: joej
    • Posted on: 2009/09/18
    • At: 21:22

    love this, its great, question, built in scroll wheel for iphone allows you to touch instead of dragging the bar like here, curious when you’ll have the ability to touch the wheel selection item instead of dragging the frame. again, love your work, its just awesome, shows web apps can do as much if not more than embedded, gonna try this script with pre and android to see how they react aswell, keep up good work

    • Author: Julian
    • Posted on: 2009/11/03
    • At: 23:15

    Doesn’t work with jqtouch (http://www.jqtouch.com/)

    • Author: Erich
    • Posted on: 2009/11/06
    • At: 18:48

    Wonderful script!

    Any ideas on how to turn off the magnifying glass if you touch and hold?

    • Author: Thomas Alexander
    • Posted on: 2009/11/14
    • At: 06:11

    wow tat was awesome , it really helped me

    • Author: Bruce T
    • Posted on: 2009/12/02
    • At: 19:27

    This is wonderful, any plans on getting it to work with JQTouch as well? it works somewhat when the jqtouch.min.css file isn’t added, but not at all when it is. Still a great piece of work!

  • @Bruce T, I’d like to build a completely native and independent framework for iphone and android (no jquery, iui, whatever dependency). The iScroll is just the base for it.

    • Author: Jack Shieh
    • Posted on: 2009/12/07
    • At: 17:51

    The component is great! I modified the script a little so that SW can function within my iui based iPhone app.

    • Author: john barbic
    • Posted on: 2009/12/10
    • At: 00:37

    Julian & Bruce T,

    I’ve been working with jqtouch as well and the conflict is easily fixed with a few tweaks to the css. Note that one of the first selectors in the jtouch.css hides all elements under the body tag, so what we have to do is reinforce a couple selectors in spinningwheel.css to make sure certain element styles wont get stomped on.

    The primary one is here:

    #sw-wrapper {
    position:absolute; z-index:1000;
    left:0;
    width:100%;
    display:inline !important;
    min-height: 0px !important;
    font-family:helvetica, sans-serif;
    background:rgba(0,0,0,0.7);
    text-align:left;
    }

    You must add display:inline !important; and min-height: 0px !important; otherwise the container will inherit these values from jtouch.css and you don’t want that.

    The other change is here:

    #sw-slots li {
    border-top: 0px solid black !important;
    color:#000000 !important;
    padding:0 8px;
    height:44px;
    overflow:hidden;
    font:bold 24px/44px Helvetica,sans-serif;
    }

    Again, you must add border-top: 0px solid black !important; and color:#000000 !important; or they will get inherited from theme.css (at least they will in the apple theme) and that makes the list items get out of sync and look all messed up.

    With these two tweaks you will be able to see the control slide up into place and operate as advertised. You will still have to wire your cancel and done actions of course, but this will get you on your way.

    • Author: brian
    • Posted on: 2009/12/10
    • At: 10:38

    Hi guys

    i have a tough one, (although its probably something easy that i’m missing) i need the keys to show the 0 for example

    Original code
    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’ };

    Tried this
    var months = { 01: ‘Jan’, 02: ‘Feb’, 03: ‘Mar’, 04: ‘Apr’, 05: ‘May’, 06: ‘Jun’, 07: ‘Jul’, 08: ‘Aug’, 09: ‘Sep’, 10: ‘Oct’, 11: ‘Nov’, 12: ‘Dec’ };

    but results.keys.join(‘-’) still outputs 2009-1-1 instead of 2009-01-01 as you can see i need it this way for a sql query

    any help would be great

    Brian

    • Author: Jack Shieh
    • Posted on: 2009/12/14
    • At: 20:36

    Hi Brian,

    You can use pop() to get the value from results.keys and check the value to see if it is greater than 10 to add ’0′ if necessary.

    • Author: riko
    • Posted on: 2009/12/17
    • At: 10:42

    The tweak works fine on jqtouch, thanks a lot !!!!

    • Author: Michael
    • Posted on: 2009/12/26
    • At: 05:49

    http://www.technetra.com/2009/08/10/countdown-iphone-webapp/ explains the two changes needed for iUI:

    #sw-wrapper { display:block; }

    and

    body[orient="portrait"] #sw-frame { bottom:112px; }
    body[orient="landscape"] #sw-frame { bottom:8px; }

    This is fantastic, thanks!

    • Author: Brian S.
    • Posted on: 2010/01/08
    • At: 02:15

    Hey guys, i’m having one problem that maybe you can help with. I used this script on a text box input, but it comes up behind the default iphone keyboard and the user must first know to cancel the keyboard to use the SW. is there any way to stop the default keyboard from coming up?

    any help would be a life saver

    Brian

    • Author: venkat
    • Posted on: 2010/01/08
    • At: 13:13

    hey… gr8 script as always… a lifesaver.
    1 prob though… i am calling it on click of a button and it comes up fine bt the prob is, when it loads, it comes up without any images ie pure numbers and stuff… then in a fraction of second css loads up and the SW is up to go… i just wanna know if its possible that we dont show up an ugly SW coz its ruining all the beauty of it…
    Thanx

    • Author: Daniel J. Pinter
    • Posted on: 2010/01/09
    • At: 15:00

    Is it possible to have a true “wheel” where the data reaches the upper limit and restarts the lower limit? Any chance of including the clicky sound?

    btw, I LOVE THIS SCRIPT!

    • Author: vivek
    • Posted on: 2010/01/10
    • At: 15:15

    Hi

    Here is few step to make it works in Safari , Chrome & Android browsers.

    Just replace event names
    touchstart -> mousedown
    touchmove -> mousemove
    touchend -> mouseup

    Also
    e.targetTouches[0].clientX – >e.clientX
    e.targetTouches[0].clientY -> e.clientY

    • Author: vivek
    • Posted on: 2010/01/10
    • At: 18:42

    Hi

    If you want integrate with iUi .Add the following to iui.css

    body > #sw-wrapper {
    display : block;
    min-height :0px;
    }

    body[orient="landscape"] > #sw-wrapper {

    min-height: 100px;
    }

  • @venkat: yes it would be possible to add some preloading. I’m a bit busy at the moment but that is a feature that I’m going to add to the iScroll and the spinning wheel is actually based on iScroll. So as soon as iScroll v3 will be ready we will have a better spinning wheel as well.

    @Daniel J. Pinter: it is possible to have an “infinite” loop. We can do it in two ways. The first one is by replicating the content and keeping the control in 2D. The second would be to turn the control 3D. I’d like to try the second option, but there’s a lot of coding involved.

    • Author: Jon Phillips
    • Posted on: 2010/01/13
    • At: 23:27

    A great, great script – thanks so much! Works great on the iphone, and I’m trying to get it working on Android (Nexus One). If I run the script as-is, the wheels scroll great, but all other events on the screen don’t work – Cancel, Done, and my menu buttons are “dead.” I blindy tried the code changes in comment 84, but that brought no joy. I’m wondering if I need to ADD mouse events instead of replacing the touch events, and add them for the Cancel and Done buttons? Or is some lockScreen function not getting undone? As you can tell, my JavaScript skills suck … anyone figured out the Android secret?

    Many thanks.

    • Author: Peter
    • Posted on: 2010/01/14
    • At: 12:29

    I am having terrible trouble getting this to work with jQtouch. From what I’ve read in the comments quite a few people have gotten it to work with the provided solutions. I’d really love to inspect there implementation of the Spinning Wheel. Can I please see what you’ve done to get the Spinning Wheel working with jQTouch.

    • Author: Jon Phillips
    • Posted on: 2010/01/14
    • At: 15:02

    Regarding JQTouch, make sure to get the latest build (131 or 132?). The create a #jqt namespace that allows Spinning Wheel to work … and did it primarily for Spinning Wheel.

    • Author: Peter
    • Posted on: 2010/01/14
    • At: 16:52

    Okay, I’m going to load up build 132 and see how it goes, will report back. Will also try and create the #jqt namespace. (ps. do you have an example of your where you have it working with jqtouch, would be extremely helpful, thanks).

    • Author: Peter
    • Posted on: 2010/01/14
    • At: 17:17

    I was using the 109 build.

    • Author: Peter
    • Posted on: 2010/01/14
    • At: 18:19

    Thanks. That worked great.

    • Author: Jann
    • Posted on: 2010/01/14
    • At: 23:14

    I would think 24px would be a bit much. I changed it to 18px for the font #sw-slots li and it works much better…especially on longer items.

    Did you just choose 24px or were you guessing? (please take no offense…just asking)

    • Author: Viji
    • Posted on: 2010/01/18
    • At: 21:59

    Hi ska_iit, Can you e-mail the updates that you made for iWebKit, please..
    E:Mail: vijishvanya at gmail
    Thanks

    • Author: Jann
    • Posted on: 2010/01/18
    • At: 23:39

    Any chance to add a “select” feature on click … like in the apple spinning wheel? Ie: If you touch a selection and release, it will auto-scroll that selection into the “selected area” gray bar and that way you do not have to scroll up.

    For instance, say you have
    1
    2
    3

    showing in spinning wheel and 2 is highlighted. Say you touched BUT DID NOT SPIN 3. 3 *should* (according to the “apple” spinning wheel on the iphone) checkmark itself. (in this case not a checkmark, but 3 should become the selected item.

    Any chance?

    thx

    Jann

  • @Jann: that is a nice feature, easy to add. I’ll definitely consider that for inclusion.

    • Author: Viji
    • Posted on: 2010/01/19
    • At: 20:15

    Hi,
    I’m looking at demo at http://cubiq.org/dropbox/sw/ on my iPhone.
    I see the page, but when I click on any button like “Birth date” nothing happens. Do I need anything else?
    Thanks

    • Author: Viji
    • Posted on: 2010/01/19
    • At: 21:31

    oops..For some reason I had JavaScript off on my iPhone Safari Settings. The demo shows good now. Great script. Thanks

    • Author: shobhit
    • Posted on: 2010/01/20
    • At: 15:16

    @matt:
    can you please tell how i can i test spinning wheel on windows OS ,tried to load on palm emultor and couple of simulator didnt worked

  • Social comments and analytics for this post…

    This post was mentioned on Twitter by caolu: http://bit.ly/h9NOt
    It’s beautify U/I…