Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Fetching contributors…
Cannot retrieve contributors at this time
664 lines (404 sloc) 37.5 KB
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="">
<title><![CDATA[Category: HTML5 | TJ VanToll]]></title>
<link href="" rel="self"/>
<link href=""/>
<name><![CDATA[TJ VanToll]]></name>
<generator uri="">Octopress</generator>
<title type="html"><![CDATA[maxlength Constraint Validation Oddities in Major Browsers]]></title>
<link href=""/>
<content type="html"><![CDATA[<p>The <code>maxlength</code> attribute has been around in browsers for a long time. When provided all browsers prevent the user entering a value that exceeds the <code>maxlength</code>.</p>
<p>For example you cannot type more than 2 characters in the textbox below:</p>
<p><input type="text" maxlength="2" /></p>
<h3>Constraint Validation</h3>
<p><a href="">Constraint validation</a> is an HTML5 spec that provides native client side form validation in the browser. As part of its <a href="">API</a>, all <code>&lt;input&gt;</code> and <code>&lt;textarea&gt;</code> elements have a <code>validity.tooLong</code> property that is <code>true</code> when the length of the <code>value</code> exceeds the <code>maxlength</code>.</p>
<p>But if the browser prevents this then why does the property exist?</p>
<h3>Prefilled value Attribute</h3>
<p>Assume that you're filling the <code>value</code> of form elements with information from a database and you end up with something like this:</p>
<p><code>&lt;input type="text" maxlength="1" value="ABC" /&gt;</code></p>
<p>How will the browser handle this?</p>
<p>All browsers will prevent entry of additional characters, but they do not trim excess characters already present. Additionally all browsers will allow a form containing the above input to submit. <em>Note: Opera is the only browser to set the <code>validity.tooLong</code> property to <code>true</code> in this situation. Despite this, it does not prevent form submission.</em></p>
<p>Why is submission not prevented? The key is in the <a href="">specification</a>:</p>
<blockquote><p>Constraint validation: If an element has a maximum allowed value length, its dirty value flag is true, its value was last changed by a user edit (as opposed to a change made by a script), and the code-unit length of the element's value is greater than the element's maximum allowed value length, then the element is suffering from being too long.</p></blockquote>
<p>The <a href="">dirty flag</a> essentially means the the user has changed the value of an element. Therefore, in order to be <code>tooLong</code> the element must have been last interacted with by a user edit.</p>
<h3>Actually Triggering tooLong</h3>
<p>So let's take another approach. What happens if you have the same input:</p>
<p><code>&lt;input type="text" maxlength="1" value="ABC" /&gt;</code></p>
<p>...remove one character, then submit? You can try it for yourself below:</p>
<iframe style="width: 100%; height: 180px;" src=",html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>Upon edit IE10 and Chrome will set the <code>validity.tooLong</code> property to <code>true</code> and prevent form submission. If the user attempts to submit a form after removing the "C" they will see the following in those browsers:</p>
<p><img src="/images/posts/2012-10-17/Chrome.png" title="Chrome" alt="Chrome" />
<img src="/images/posts/2012-10-17/IE10.png" title="IE10" alt="IE10" /></p>
<p>Firefox, Safari, and Opera incorrectly handle this situation and allow the form to be submitted anyways.</p>
<title type="html"><![CDATA[Using jQuery UI's Slider to Polyfill HTML5's input[type=range]]]></title>
<link href=""/>
<content type="html"><![CDATA[<p><a href="">jQuery UI's slider plugin</a> looks and behaves much like the browser's native <code>input[type=range]</code> control; therefore it makes an excellent choice for <a href="">polyfilling</a> the native behavior.</p>
<h3>How to do it</h3>
<p>The main issue is that the slider must be built on a block level node, not an <code>&lt;input&gt;</code>. Therefore you must create another container node (in this case a <code>&lt;div&gt;</code>) to create the slider from then hide the original <code>&lt;input&gt;</code>.</p>
<p>Here's the script that I used.</p>
<p>``` javascript Polyfill input[type=range] with jQuery UI's Slider
$(function() {</p>
<pre><code>//Determine whether the browser natively supports input[type=range].
//If you're using Modernizr this is equivalent to Modernizr.inputtypes.range
var input = document.createElement('input');
input.setAttribute('type', 'range');
var rangeSupport = input.type != 'text';
if (!rangeSupport) {
var $input, $slider;
$('input[type=range]').each(function(index, input) {
$input = $(input);
//Create a new div, turn it into a slider, and set its attributes based on
//the attributes of the input. If the input doesn't possess those attributes
//use jQuery UI's defaults.
$slider = $('&lt;div /&gt;').slider({
min: parseInt($input.attr('min'), 10) || 0,
max: parseInt($input.attr('max'), 10) || 100,
value: parseInt($input.attr('value'), 10) || 0,
step: parseInt($input.attr('step'), 10) || 1,
slide: function(event, ui) {
//Keep the value of the input[type=range] in sync with the slider.
//Append the slider after the input and hide the input. The user will only
//interact with the slider.
<p>This approach handles the most common use cases but it isn't perfect. For example, if you want to disable the <code>&lt;input type="range"&gt;</code> you'll have to disable the slider as well.</p>
<p>There is a <a href="">feature request to add support for this natively in the slider plugin</a> itself, but it's been sitting inactive for quite some time now. Hopefully over time <code>input[type=range]</code> support will become ubiquitous enough that we can drop these workarounds.</p>
<title type="html"><![CDATA[HTML5 Form Validation - Start Using it in Production Applications]]></title>
<link href=""/>
<content type="html"><![CDATA[<p>Forms suck, yet, they make or break the most crucial online transactions. Building web forms sucks as well; the APIs and lack of customizability has confused and frustrated people for years. As a byproduct an abundance of horribly unusable forms have been created for everyone to suffer through.</p>
<p>Therefore <em>anything</em> that makes this process easier should be greeted with joy and enthusiasm.</p>
<p>HTML5 does just this by including a built in <a href="">client side form validation mechanism</a> designed to make implementing client side validation powerful, seamless, and most importantly - easy to implement.</p>
<p>Great! Despite this, HTML5 form validation is a topic relegated to presentations and demos; I personally have yet to fill out a web form in the wild that actually makes use of it.</p>
<h3>Browser Support</h3>
<p>One reason people avoid HTML5 form validation is lack (or perceived lack) of browser support for the new APIs. However the <a href="">list of supported browsers</a> now includes the latest version of every major browser, including:</p>
<li>IE 10</li>
<li>Firefox 4+</li>
<li>Chrome 10+</li>
<li>Safari 5+</li>
<li>Opera 10+</li>
<li>Opera Mobile</li>
<li>Chrome for Android</li>
<li>Firefox for Android</li>
<h3>What to do in unsupported browsers?</h3>
<p>This is <em>the</em> problem. Despite browser support being relatively good, for most sites there are still going to be a substantial amount of users with browsers that simply do not support the new APIs. Therefore, if you are intending to support these users you have to start forking your code to support both browsers with native support and browsers without it.</p>
<p>While this can absolutely be done, it's time consuming and a bit of a nuisance. It's faster and easier to simply ditch the native validation and use your own. In this case you don't have to worry about multiple code paths; every user in every browser will hit the same codepath and get the same experience.</p>
<h3>What if we did nothing?</h3>
<p>But what if we took a new approach to this problem - simply don't do client side validation in unsupported browsers, at all. This is advantageous for a number of reasons.</p>
<p>1) <strong>No dual maintenance</strong>. One thing that has always bothered me about doing validation on both the client and server side is that you're validating the #1 principle of software development - <a href="'t_repeat_yourself"><strong>D</strong>on't <strong>R</strong>epeat <strong>Y</strong>ourself</a>.</p>
<p>2) <strong>No dependencies</strong>. If you only use native browser APIs to provide client side validation you don't have to worry about maintaining plugin or library dependencies that might not be maintained.</p>
<p>3) <strong>Faster and easier</strong>. The browser APIs are simple and easy to use. Want to make a field required? <a href="">Add the <code>required</code> attribute</a>. Want to make a field only accept email address? <a href="">Add <code>type=email</code> to your <code>&lt;input&gt;</code> field</a>.</p>
<p>4) <strong>Future Friendly</strong>. Although currently a number of older browsers (namely IE &lt;= 9) do not support the new APIs, eventually they all will. Therefore, eventually all users will hit the client side validation as intended.</p>
<h3>But, you can't just not validate data... right?</h3>
<p>Of course you have to validate client submitted data, but you already need to be doing that on the server side anyways. What this approach requires you to do is simply return formatted error messages from your server side processing and <a href="">display them in a usability friendly way</a>. You're likely doing that already.</p>
<h3>What About Polyfills?</h3>
<p><a href="">Polyfills</a> are great and a number of <a href="">HTML5 form validation polyfills</a> exist. My problem with polyfills in this case is that they add a dependency that I believe is unnecessary.</p>
<p>HTML5 provides native solutions to validating client side data and most all modern browsers support it. Yet, most people are still relying on the JavaScript hacks we've been using for well over a decade now.</p>
<p>It's time. Come to the dark side.</p>
<title type="html"><![CDATA[HTML5 Form Validation - Showing All Error Messages]]></title>
<link href=""/>
<content type="html"><![CDATA[<p><a href="">Browsers that support HTML5 form validation</a> have one thing in common; if a <code>&lt;form&gt;</code> is submitted and has errors on multiple fields, the browser will only display the first error to the user.</p>
<p>Turns out the spec leaves the specific means of handling multiple errors up to the browser itself:</p>
<p><blockquote><p>Report the problems with the constraints of at least one of the elements given in unhandled invalid controls to the user. User agents may focus one of those elements in the process, by running the focusing steps for that element, and may change the scrolling position of the document, or perform some other action that brings the element to the user's attention.</p></p><p><p>User agents may report more than one constraint violation. User agents may coalesce related constraint violation reports if appropriate (e.g. if multiple radio buttons in a group are marked as required, only one error need be reported).</p><footer><strong>HTML5 Specification</strong> <cite><a href=''>;</a></cite></footer></blockquote></p>
<p>The key part here being that user agents (i.e. browsers) <strong>MAY</strong> report more than one constraint violation (i.e. error). Turns out they all decided not to.</p>
<p>You can see this in your browser below (assuming it <a href="">supports HTML5 form validation</a> and is not Safari, more on that later). Both fields are <code>required</code>, but if you submit the form you will only see an error for the first field.</p>
<pre class="codepen" data-type="result" data-href="FBGvu" data-user="tjvantoll" data-host=""><code></code></pre>
<script async src=""></script>
<p>Here's what it looks like on supported browsers if you attempt to submit this empty <code>&lt;form&gt;</code>:</p>
<h5>Chrome 21</h5>
<p><img src="/images/posts/2012-08-05/Chrome.png" title="Chrome" alt="Chrome" /></p>
<h5>Firefox 14</h5>
<p><img src="/images/posts/2012-08-05/Firefox.png" title="Firefox" alt="Firefox" /></p>
<h5>Opera 12</h5>
<p><img src="/images/posts/2012-08-05/Opera.png" title="Opera" alt="Opera" /></p>
<p>As you can see, all three only give an error for the first field. Firefox at least has the decency to put a red border around all fields with invalid data by default.</p>
<p>The one noticeable browser missing from the list above is Safari. Even though Safari supports the constraint validation API, the validation itself is turned off.</p>
<p>From a usability perspective showing the users only the first error message is bad. Imagine how frustrating it would be to continually correct errors just to be presented with the next error in the sequence. If you've ran into a form such as this before you know what I'm talking about.</p>
<p>Luckily, browsers provide a <a href="">constraint validation API</a> that can be used to provide this functionality.</p>
<h3>Using the Validation API</h3>
<p>All dom nodes now possess a <a href="">willValidate</a> property that indicates whether the node is a candidate for form validation.</p>
<p>Nodes in which <code>willValidate</code> is <code>true</code> also have a <code>validity</code> property. The <code>validity</code> property resolves to a <a href="">ValidityState object</a> which contains information about whether the field has validation errors, as well as the error message the browser will display to the user.</p>
<p>To make things even easier browsers provide an <a href="">:invalid pseduoselector</a> that can be used to select all elements with validation errors. Let's see how this can be leveraged to show all error messages.</p>
<h3>The Code</h3>
<p>Here's how I accomplished this with a jQuery dependent script.</p>
<p>``` html
<pre><code>&lt;ul class="errorMessages"&gt;&lt;/ul&gt;
&lt;label for="name"&gt;Name:&lt;/label&gt;
&lt;input type="text" required /&gt;
&lt;label for="comments"&gt;Comments:&lt;/label&gt;
&lt;textarea id="comments" required&gt;&lt;/textarea&gt;
&lt;input type="submit" value="Submit" /&gt;
$(function() {
var createAllErrors = function() {
var form = $(this);
var errorList = $('ul.errorMessages', form);
var showAllErrorMessages = function() {
//Find all invalid fields within the form.
form.find(':invalid').each(function(index, node) {
//Find the field's corresponding label
var label = $('label[for=' + + ']');
//Opera incorrectly does not fill the validationMessage property.
var message = node.validationMessage || 'Invalid value.';
.append('<li><span>' + label.html() + '</span> ' + message + '</li>');
$('input[type=submit]', form).on('click', showAllErrorMessages);
$('input[type=text]', form).on('keypress', function(event) {
//keyCode 13 is Enter
if (event.keyCode == 13) {
<p>You can see the results in your browser below:</p>
<pre class="codepen" data-type="result" data-href="eLvlf" data-user="tjvantoll" data-host=""><code></code></pre>
<p>Here's how it looks in Chrome 21:</p>
<p><img src="/images/posts/2012-08-05/Chrome-full.png" title="Chrome" alt="Chrome" /></p>
<p>A couple things to note:</p>
<p>1) If a user attempts to submit a form and gets validation errors, a <code>submit</code> event is never fired for the <code>&lt;form&gt;</code>. Therefore, instead of listening for <code>submit</code> on the <code>&lt;form&gt;</code>, I instead listen for a <code>click</code> on the <code>&lt;input type="submit"&gt;</code>. Since the user is also able to submit the form pressing enter in text inputs, I attach a <code>keypress</code> listener to them to ensure the same logic runs.</p>
<p>2) In my example I start each error message with the contents of the field's <code>&lt;label&gt;</code>. This is because the messages for each field are often identical. An alternative approach would be to use another constraint validation API method, <a href="">setCustomValidity</a> to set a completely custom message.</p>
<p>3) The <code>:invalid</code> selector will return nothing in all browsers that do not support the constraint validation API. Therefore this code will simply do nothing in those browsers.</p>
<p>4) Opera incorrectly does not fill the <code>validationMessage</code> property. Therefore the check <code>var message = node.validationMessage || 'Invalid value.'</code> is necessary so a message is displayed for Opera.</p>
<p>5) I do nothing to style the individual fields based on whether they have valid data. The HTML5 spec provides a number of CSS hooks to do this and I would recommend reading <a href="">CSS Pseudo-Classes and HTML5 Forms</a> from <a href="">html5 Doctor</a> if you're interested in including such styling.</p>
<h3>That's a Lot of Code to Do Something Simple</h3>
<p>Yep. While browser support is getting to be quite good for HTML5 forms the implementations themselves are still a bit buggy. Nevertheless, this approach will work for displaying all validation errors to the end user.</p>
<p>If you are interested in making the code above work in all browsers one option you have is to polyfill the functionality for unsupported browsers. One robust choice is the <a href="">webshims</a> library.</p>
<p>To make webshims work with the code above all you need to do is add <code>$.webshims.polyfill('forms');</code>. The maintainer, <a href="">@aFarkas</a> was even kind of enough to provide me with a live example showing this - <a href=""></a>.</p>
<h3>Update (September 5th, 2012)</h3>
<p>Per some <a href="">critique on Github</a> from <a href="">@aFarkas</a> I've made the following changes:</p>
<li>Updated the example code.
<li>Removed a hack I had in place for Safari.</li>
<li>Switched to use the <code>:invalid</code> pseudoselector to find all invalid fields within a form.</li>
<li>Make the script handle multiple <code>&lt;form&gt;</code> elements in one DOM.</li>
<li>Added the above section on using webshim to polyfill this behavior for all browsers.</li>
<title type="html"><![CDATA[Native HTML5 Number Picker and jQuery UI's Spinner - Which to Use?]]></title>
<link href=""/>
<content type="html"><![CDATA[<p><a href="">HTML5's native number picker</a> (<code>&lt;input[type=number]</code>) 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.</p>
<p>HTML5 adds several new valid <code>type</code> attributes for <code>&lt;input&gt;</code> elements. One of them, <code>number</code>, can be used to create a number picker.</p>
&lt;input type="number" /&gt;
<p>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+ (<a href="">full support list</a>). Here's what the user will see in supported browsers:</p>
<h5>Chrome 20:</h5>
<p><img src="/images/posts/2012-07-15/Chrome.png" title="Chrome" alt="Chrome" /></p>
<h5>Safari 5.1.7:</h5>
<p><img src="/images/posts/2012-07-15/Safari.png" title="Safari" alt="Safari" /></p>
<h5>Opera 12.00:</h5>
<p><img src="/images/posts/2012-07-15/Opera.png" title="Opera" alt="Opera" /></p>
<h5>Opera Mobile 12:</h5>
<p><img alt="Opera Mobile" title="Opera Mobile" src="" style="height: 250px;" /></p>
<h5>iOS 5:</h5>
<p><img alt="iOS" title="iOS" src="" style="height: 200px;" /></p>
<h5>Android 4.1 (Jelly Bean):</h5>
<p><img alt="Android" title="Android" src="" style="height: 200px;" /></p>
<p>As you can see one of the nicest effects of using <code>[type=number]</code> 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 <code>input[type=number]</code> as a normal text input. Firefox has <a href="">recently added a UI-less version</a> of <code>input[type=number]</code> to their nightly builds so hopefully a fully enabled version will be coming soon.</p>
<p>You can see what your browser does below:</p>
<iframe style="width: 100%; height: 120px;" src=",html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<h4>Additional Functionality</h4>
<p>The native number picker supports <code>min</code>, <code>max</code>, and <code>step</code> attributes to allow you to pick the minimum value of the <code>&lt;input&gt;</code>, the maximum value of the <code>&lt;input&gt;</code>, and the amount the value should be incremented / decremented when the user spins through values (the <code>step</code> attribute defaults to <code>1</code> if not specified).</p>
<p>For example, on the <code>&lt;input&gt;</code> below the browser will enforce that the minimum value will be <code>2</code>, the maximum value will be <code>20</code>, and the user will step at increments of <code>2</code>.</p>
&lt;input type="number" min="2" max="20" step="2" /&gt;
<p>You can see how this behaves in your browser below:</p>
<iframe style="width: 100%; height: 120px;" src=",html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>Just as a word of warning, Android 4.1 and iOS 5 do not support the <code>min</code>, <code>max</code>, or <code>step</code> attributes.</p>
<p>In addition to the new attributes, supporting browsers also provide 3 JavaScript methods specifically for <code>input[type=number]</code>.</p>
<li><code>stepUp(n)</code> - Increment the <code>value</code> of the <code>&lt;input&gt;</code> by <code>n</code>.</li>
<li><code>stepDown(n)</code> - Decrement the <code>value</code> of the <code>&lt;input&gt;</code> by <code>n</code>.</li>
<li><code>valueAsNumber</code> - Retrieve the <code>value</code> of the <code>input</code> as a JavaScript <code>number</code> variable (by default retrieving the <code>value</code> of an <code>&lt;input&gt;</code> returns a <code>string</code>).</li>
<h3>jQuery UI Spinner</h3>
<p>jQuery UI's <code>spinner</code> 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.</p>
<iframe style="width: 100%; height: 120px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>It also supports setting minimum, maximum, and step values through options rather than attributes.</p>
<p>``` html jQuery UI Spinner
<input id="spinner" /></p>
$(function() {
min: 2,
max: 20,
step: 2
<iframe style="width: 100%; height: 120px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<h3>Above and Beyond</h3>
<p>What really sets jQuery UI's <code>spinner</code> 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.</p>
<p><code>spinner</code> takes a <code>page</code> option that allows you to define how much the <code>spinner</code> should step when the page down / page up keys are pressed. The example below shows a <code>spinner</code> with a <code>step</code> value of <code>1</code> and a <code>page</code> value of <code>10</code>.</p>
<iframe style="width: 100%; height: 150px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>If you want mousewheel support for a <code>spinner</code> all you need to do is include <a href="">Brandon Aaron's mousewheel plugin</a> and you get it automatically! Try it out on any of the <code>spinner</code> demos on this page.</p>
<p>Ever need to accept currency at certain defined increments? This example shows a <code>spinner</code> that spins through currency values at $25 increments, all with the same clean API.</p>
<iframe style="width: 100%; height: 150px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>The formatting is localized through <a href="">Globalize.js</a>, therefore, if you want to handle different currencies all you need to do is pass in the appropriate <code>culture</code> and include the necessary JavaScript dependencies. Here's an example of an input that takes Euros.</p>
<iframe style="width: 100%; height: 150px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>If you need to accept time data <code>spinner</code> can be used for that as well.</p>
<iframe style="width: 100%; height: 150px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>The <code>page</code> 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.</p>
<h3>24 Hour Times</h3>
<p>Since the <code>spinner</code> uses Globalize.js, you're free to use a time system different than the United States' nonsensical one.</p>
<iframe style="width: 100%; height: 150px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<h3>Time Picker vs. <code>&lt;input type="time"&gt;</code></h3>
<p>HTML5 also provides a native time picker (<code>input[type=time]</code>), but, it has <a href="">nearly no support</a>, does not yet provide localized formatting, and does not provide the stepping/paging functionality that <code>spinner</code> has baked in. In the future it might provide a viable native solution, but for now it's best to stay away.</p>
<h4>Extensible and Customizable</h4>
<p>Because <code>spinner</code> is built on top of <a href="">jQuery UI's widget factory</a>, 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:</p>
<p>```javascript Extending spinner
<input /></p>
$.widget( "tj.olympicspicker", $.ui.spinner, {
options: {
min: 1896,
max: 2012,
step: 4
$(function() {
<iframe style="width: 100%; height: 150px;" src=",js,html/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<p>Now all your olympics pickers in your code base can share the same code!</p>
<h3><code>&lt;input type="number"&gt;</code> vs. <code>spinner</code></h3>
<p>Although jQuery UI's <code>spinner</code> 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 <code>spinner</code> 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 <code>spinner</code> provides an excellent API to do so.</p>
<p>To summarize the reasons to use the native picker are:</p>
<li>Easy to implement, simply give an <code>&lt;input&gt;</code> a <code>type</code> attribute of <code>number</code>.</li>
<li>There are no dependencies, the number picker is native to the browser.</li>
<li>Mobile browsers that support the native picker will optimize the touch keyboard for number input.</li>
<p>And the reasons to use jQuery UI's <code>spinner</code> are:</p>
<li>Browser support - The <code>spinner</code> will work all the way back to IE6.</li>
<li>Extremely customizable and extensible.</li>
<li>Customizable handling of the page up and page down keys.</li>
<li>Easily integrated mousewheel support.</li>
<li>Built in custom types such as currency and time.</li>
<li>Built in i18n support.</li>
<h3>Using jQuery UI's Spinner to Polyfill <code>input[type=number]</code></h3>
<p>Another option is to use the native HTML number picker when it's available, and fallback to jQuery UI's <code>spinner</code> when it's not.</p>
<p>``` javascript Using jQuery UI to Polyfill input[type=number]
$(function() {</p>
<pre><code>var input = document.createElement('input');
input.setAttribute('type', 'number');
if (input.type == 'text') {
<p>The code to detect <code>input[type=number]</code> support was taken from <a href="">another number picker polyfill by jonstipe</a>. It creates an <code>&lt;input&gt;</code>, changes its <code>type</code> to <code>number</code>, and sees if that change actually took effect to determine whether the browser supports the type. You could also use the <code>Modernizr.inputtypes.number</code> check from <a href="">Modernizr</a> to achieve the same thing.</p>
<p>The <code>spinner</code> plugin is smart enough to look for the <code>step</code>, <code>min</code>, and <code>max</code> attributes on the <code>&lt;input&gt;</code> so you don't have to pass those in explictly (<a href="">thanks @bassistance</a>).</p>
<p>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 <a href="">yepnope.js</a> to bring in jQuery UI's required JavaScript and CSS only when you need it.</p>
<h3>Using Spinner and Getting a Number Keyboard on Mobile</h3>
<p>If you want to use a <code>spinner</code> everywhere AND get a number keyboard on mobile things get a little trickier. Mobile browsers look for an <code>&lt;input&gt;</code> to have <code>type=number</code> to provide the number keyboard. So you think this would be as simple as creating a <code>spinner</code> on a <code>&lt;input[type=number]&gt;</code> node. However, that produces the following on supporting desktop browsers.</p>
<h5>Chrome 20:</h5>
<p><img src="/images/posts/2012-07-15/Chrome-Dual.png" title="Chrome" alt="Chrome" /></p>
<h5>Safari 5.1.7:</h5>
<p><img src="/images/posts/2012-07-15/Safari-Dual.png" title="Safari" alt="Safari" /></p>
<h5>Opera 12.00:</h5>
<p><img src="/images/posts/2012-07-15/Opera-Dual.png" title="Opera" alt="Opera" /></p>
<p>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?</p>
<p>Well it turns out hiding the native arrow controls is difficult because Chrome places the control on the inside of the <code>&lt;input&gt;</code> and Safari and Opera place it on the outside. Therefore, if you try to adjust the <code>margin</code> of the <code>&lt;input&gt;</code> so jQuery UI's controls overlap the native ones it won't work in a cross browser friendly way.</p>
<p>Therefore the best approach I've came up with is to hide the <code>spinner</code>'s arrow controls when the browser creates its own.</p>
<p>``` javascript Number keyboard for a spinner
$(function() {</p>
if (Modernizr.input.step) {
$('.ui-spinner-input').css('marginRight', 0);
<p>What this does is detect whether the browser supports the <code>step</code> attribute, if it does it removes jQuery UI's controls. What does the <code>step</code> attribute have to do with the arrow controls? Nothing, except that it just <em>happens</em> that the browsers that support the <code>step</code> attribute also create a native control to do the stepping. Is this going to change in the future? Quite possibly.</p>
<p>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.</p>
<h3>Update (August 26th, 2012)</h3>
<p>Commenter amir pointed out the WebKit provides a pseudoclass that you can use to style, and therefore hide the native spin controls.</p>
<p>``` css
input[type=number]::-webkit-outer-spin-button {</p>
<pre><code>display: none;
input[type=number]::-webkit-inner-spin-button {</p>
<pre><code>display: none;
<p>This solves the issue for Webkit, but this remains an issue for Opera and browsers that add <code>input[type=number]</code> support in the future.</p>
Jump to Line
Something went wrong with that request. Please try again.