Me!

TJ VanToll

Native HTML5 Number Picker and jQuery UI's Spinner - Which to Use?

| Comments

HTML5’s native number picker (<input[type=number]) and jQuery UI 1.9’s spinner can both be used to create inputs for numeric data. So which makes sense for your application? Let’s start with a brief explanation of each.

input[type=number]

HTML5 adds several new valid type attributes for <input> elements. One of them, number, can be used to create a number picker.

<input type="number" />

This will present the user with a number picker in supported browsers, which, as of this writing includes Chrome, Safari, Opera, iOS, Opera Mobile, and Android 4.0+ (full support list). Here’s what the user will see in supported browsers:

Chrome 20:

Chrome

Safari 5.1.7:

Safari

Opera 12.00:

Opera

Opera Mobile 12:

Opera Mobile

iOS 5:

iOS

Android 4.1 (Jelly Bean):

Android

As you can see one of the nicest effects of using [type=number] is that mobile users will automatically be presented with a number pad to aid with entry of numeric data. Unsupported browsers will simply treat the input[type=number] as a normal text input. Firefox has recently added a UI-less version of input[type=number] to their nightly builds so hopefully a fully enabled version will be coming soon.

You can see what your browser does below:

input[type=number] example Open in New Window

Additional Functionality

The native number picker supports min, max, and step attributes to allow you to pick the minimum value of the <input>, the maximum value of the <input>, and the amount the value should be incremented / decremented when the user spins through values (the step attribute defaults to 1 if not specified).

For example, on the <input> below the browser will enforce that the minimum value will be 2, the maximum value will be 20, and the user will step at increments of 2.

<input type="number" min="2" max="20" step="2" />

You can see how this behaves in your browser below:

Min, max, and step attributes example Open in New Window

Just as a word of warning, Android 4.1 and iOS 5 do not support the min, max, or step attributes.

Methods

In addition to the new attributes, supporting browsers also provide 3 JavaScript methods specifically for input[type=number].

  • stepUp(n) - Increment the value of the <input> by n.
  • stepDown(n) - Decrement the value of the <input> by n.
  • valueAsNumber - Retrieve the value of the input as a JavaScript number variable (by default retrieving the value of an <input> returns a string).

jQuery UI Spinner

jQuery UI’s spinner is a new plugin due for jQuery UI’s 1.9 release (currently in beta). The plugin by default looks and behaves much like the native number picker.

jQuery UI spinner example Open in New Window

It also supports setting minimum, maximum, and step values through options rather than attributes.

<input id="spinner" />
<script>
    $(function() {
        $('#spinner').spinner({
            min: 2,
            max: 20,
            step: 2
        });
    });
</script>

Example:

jQuery UI spinner attributes Open in New Window

Above and Beyond

What really sets jQuery UI’s spinner apart from the native picker is that it is extensible, customizable, and it brings a number of extra features. Here are some of the additional things that you can do.

Paging

spinner takes a page option that allows you to define how much the spinner should step when the page down / page up keys are pressed. The example below shows a spinner with a step value of 1 and a page value of 10.

jQuery UI spinner paging Open in New Window

Mousewheel

If you want mousewheel support for a spinner all you need to do is include Brandon Aaron’s mousewheel plugin and you get it automatically! Try it out on any of the spinner demos on this page.

Currency

Ever need to accept currency at certain defined increments? This example shows a spinner that spins through currency values at $25 increments, all with the same clean API.

jQuery UI spinner currency Open in New Window

The formatting is localized through Globalize.js, therefore, if you want to handle different currencies all you need to do is pass in the appropriate culture and include the necessary JavaScript dependencies. Here’s an example of an input that takes Euros.

jQuery UI spinner euros Open in New Window

Time

If you need to accept time data spinner can be used for that as well.

jQuery UI spinner time Open in New Window

The page option discussed earlier is used nicely here to make the up / down keys control the minutes and the page up / page down keys to controls hours. Try it out on the example above.

24-Hour Times

Since the spinner uses Globalize.js, you’re free to use a time system different than the United States’ nonsensical one.

jQuery UI spinner 24-hour time Open in New Window

Time Picker vs. <input type="time">

HTML5 also provides a native time picker (input[type=time]), but, it has nearly no support, does not yet provide localized formatting, and does not provide the stepping/paging functionality that spinner has baked in. In the future it might provide a viable native solution, but for now it’s best to stay away.

Extensible and Customizable

Because spinner is built on top of jQuery UI’s widget factory, it is easily extensible. For example, let’s say you need to build an input that accepts a year in which the modern summer olympics were held. You could do that with the following:

<input />

<script>
    $.widget( "tj.olympicspicker", $.ui.spinner, {
        options: {
            min: 1896,
            max: 2012,
            step: 4
        }
    });
    $(function() {
        $('input').olympicspicker();
    });
</script>
jQuery UI spinner olympics year pickers Open in New Window

Now all your olympics pickers in your code base can share the same code!

<input type="number"> vs. spinner

Although jQuery UI’s spinner is more advanced and customizable, for most simple applications the native number picker will work just fine. If you simply need a field that accepts numeric data there’s no need to bring in spinner as a dependency. However, if you do need the ability to fine tune the behavior and look of the picker, or if you need consistent UI across all browsers, jQuery UI’s spinner provides an excellent API to do so.

To summarize the reasons to use the native picker are:

  • Easy to implement, simply give an <input> a type attribute of number.
  • There are no dependencies, the number picker is native to the browser.
  • Mobile browsers that support the native picker will optimize the touch keyboard for number input.

And the reasons to use jQuery UI’s spinner are:

  • Browser support - The spinner will work all the way back to IE6.
  • Extremely customizable and extensible.
  • Customizable handling of the page up and page down keys.
  • Easily integrated mousewheel support.
  • Built in custom types such as currency and time.
  • Built in i18n support.

Using jQuery UI’s Spinner to Polyfill input[type=number]

Another option is to use the native HTML number picker when it’s available, and fallback to jQuery UI’s spinner when it’s not.

$(function() {
    var input = document.createElement('input');
    input.setAttribute('type', 'number');

    if (input.type == 'text') {
        $('input[type=number]').spinner();
    }
});

The code to detect input[type=number] support was taken from another number picker polyfill by jonstipe. It creates an <input>, changes its type to number, and sees if that change actually took effect to determine whether the browser supports the type. You could also use the Modernizr.inputtypes.number check from Modernizr to achieve the same thing.

The spinner plugin is smart enough to look for the step, min, and max attributes on the <input> so you don’t have to pass those in explictly (thanks @bassistance).

The benefit of this technique is that you get the benefits of the native picker when it’s available, and you can count on having a number picker in all browsers. As a further optimization you could even use a conditional script loader such as yepnope.js to bring in jQuery UI’s required JavaScript and CSS only when you need it.

Using Spinner and Getting a Number Keyboard on Mobile

If you want to use a spinner everywhere AND get a number keyboard on mobile things get a little trickier. Mobile browsers look for an <input> to have type=number to provide the number keyboard. So you think this would be as simple as creating a spinner on a <input[type=number]> node. However, that produces the following on supporting desktop browsers.

Chrome 20:

Chrome

Safari 5.1.7:

Safari

Opera 12.00:

Opera

Obviously the double arrow UI is less than ideal. So to work around this you simply need to hide or destroy one of the sets or controls… right?

Well it turns out hiding the native arrow controls is difficult because Chrome places the control on the inside of the <input> and Safari and Opera place it on the outside. Therefore, if you try to adjust the margin of the <input> so jQuery UI’s controls overlap the native ones it won’t work in a cross browser friendly way.

Therefore the best approach I’ve came up with is to hide the spinner’s arrow controls when the browser creates its own.

$(function() {
    $('input[type=number]').spinner();
    if (Modernizr.input.step) {
        $('.ui-spinner-button').hide();
        $('.ui-spinner-input').css('marginRight', 0);
    }
});

What this does is detect whether the browser supports the step attribute, if it does it removes jQuery UI’s controls. What does the step attribute have to do with the arrow controls? Nothing, except that it just happens that the browsers that support the step attribute also create a native control to do the stepping. Is this going to change in the future? Quite possibly.

So obviously this is not ideal, and probably shouldn’t be used in production code, but it works at the moment. Have a better approach for tackling this problem? Let me know in the comments.

Update (August 26th, 2012)

Commenter amir pointed out the WebKit provides a pseudoclass that you can use to style, and therefore hide the native spin controls.

input[type=number]::-webkit-outer-spin-button {
    display: none; 
}
input[type=number]::-webkit-inner-spin-button {
    display: none;
}

This solves the issue for Webkit, but this remains an issue for Opera and browsers that add input[type=number] support in the future.

Comments