jQuery UI Widgets
VS
HTML5
TJ VanToll
Overlapping Functionality
<div class="box">
<h6>input[type="date"] vs. Datepicker</h6>
<input type="date">
<input id="datepicker-main">
</div>
<div class="box">
<h6>input[type="number"] vs. Spinner</h6>
<input type="number">
<input id="spinner-main">
</div>
<div class="box">
<h6>input[type="range"] vs. Slider</h6>
<div class="left">
<input type="range">
</div>
<div class="right">
<div id="slider-main"></div>
</div>
</div>
<div class="box">
<h6>[datalist] vs. Autocomplete</h6>
<input list="datalist-main">
<datalist id="datalist-main">
<option>Baboon</option>
<option>Badger</option>
<option>Barnacle</option>
<option>Bat</option>
<option>Bear</option>
<option>Beetle</option>
<option>Bird</option>
<option>Buffalo</option>
<option>Butterfly</option>
</datalist>
<input id="autocomplete-main">
</div>
<div class="box">
<h6>progress vs. Progressbar</h6>
<progress></progress>
<div id="progressbar-main"></div>
</div>
<script>
$('#datepicker-main').datepicker();
$('#spinner-main').spinner();
$('#slider-main').slider();
$('#autocomplete-main').autocomplete({
source: ['Baboon', 'Badger', 'Barnacle', 'Bat', 'Bear', 'Beetle', 'Bird', 'Buffalo', 'Butterfly']
});
$('#progressbar-main').progressbar({ value: false });
</script>
- input[type=date] vs. datepicker
- input[type=number] vs. spinner
- input[type=range] vs. slider
- <datalist> vs. autocomplete
- <progress> vs. progressbar
Outline
- The past
-
Compare widgets
- input[type="date"] vs. datepicker
- input[type="number"] vs. spinner
- input[type="range"] vs. slider
- What should you do today
- The future
How did we get here?
Limited Number of Form Controls
Form Controls
<input type="text"> <input type="password">
<input type="radio"> <input type="checkbox">
<input type="file"> <input type="hidden">
<input type="button"> <input type="image">
<input type="reset"> <input type="submit">
<button></button>
<select><option>option</option></select>
<textarea></textarea>
We figured out JavaScript
We made better UIs
<input type="text" id="datepicker-first">
<script>
$( "#datepicker-first" ).datepicker();
</script>
They got standardized
Outline
- The past
-
Compare widgets
- input[type="date"] vs. datepicker
- input[type="number"] vs. spinner
- input[type="range"] vs. slider
- What should you do today
- The future
Datepickers
<input type="date">
<input type="text" id="datepicker-1">
<script>
$( "#datepicker-1" ).datepicker();
</script>
Why use input[type=date]?
min / max / step attributes
<form>
<input type="date" id="date-1"
min="2014-03-03" max="2014-03-20" step="2">
</form>
Hooks into the constraint validation API
<form>
<input type="date" id="date-2">
<button type="submit">Submit</button>
</form>
<script>
var date = document.getElementById( "date-2" );
date.addEventListener( "change", function() {
if ( /7/.test( date.value ) ) {
date.setCustomValidity( "NO SEVENS!" );
} else {
date.setCustomValidity( "" );
}
});
</script>
<datalist> Integration
<input type="date" id="date-3" list="dates-3">
<datalist id="dates-3">
<option>2014-01-01</option>
<option>2014-10-31</option>
<option>2014-12-25</option>
</datalist>
The user agent can optimize the means of input.
iOS 7
Chrome for Android
Limitations of input[type=date]
- Lack of style-ability
- Lack of customizability
- Lack of extensibility
- Browser Support
Min and max dates
<input type="text" id="date-min-max">
<script>
$( "#date-min-max" ).datepicker({
minDate: new Date( 2014, 01, 01 ),
maxDate: new Date( 2014, 01, 10 )
});
</script>
Restrict Available Days
<input type="text" id="datepicker-3">
<script>
$( "#datepicker-3" ).datepicker({
beforeShowDay: $.datepicker.noWeekends
});
</script>
Themeable
<input type="text" id="themeable">
<script>
$( "#themeable" ).datepicker();
</script>
Show Multiple Months
<input type="text" id="datepicker-2">
<script>
$( "#datepicker-2" ).datepicker({
numberOfMonths: 3
});
</script>
Animate in / out
<input type="text" id="datepicker-4">
<script>
$( "#datepicker-4" ).datepicker({
showAnim: "fade",
duration: 500
});
</script>
Programmatic Control
<input type="text" id="datepicker-5">
<button id="show-5">Show</button>
<button id="hide-5">Hide</button>
<script>
$( "#datepicker-5" ).datepicker();
$( "#show-5" ).on( "click", function() {
$( "#datepicker-5" ).datepicker( "show" );
});
$( "#hide-5" ).on( "click", function() {
$( "#datepicker-5" ).datepicker( "hide" );
});
</script>
Inline Datepickers
<div id="ui-inline"></div>
<script>
$( "#ui-inline" ).datepicker();
</script>
Just HTML on the DOM
<!-- Simplified example of markup -->
<div class="ui-datepicker">
<div class="ui-datepicker-header">
<a class="ui-datepicker-prev"></a>
<a class="ui-datepicker-next"></a>
<div class="ui-datepicker-title"></div>
</div>
<table class="ui-datepicker-calendar">
<!-- etc -->
</table>
</div>
Just HTML on the DOM
<div id="inline-datepicker"></div>
<script>
$('#inline-datepicker').datepicker();
</script>
Comparison
-
input[type=date]
- Easy
- Dependency free
- Ties into other native features
- Mobile-optimized input
-
jQuery UI's datepicker
- Customizable
- Extensible
- Themeable
- More Functionality
- Browser Support (IE7+)
Can we get the best of both worlds?
The Problem
<input type="date" id="both-pickers">
<script>
$( "#both-pickers" ).datepicker();
</script>
Shadow DOM to the Rescue?
Customizing Pseudo-Elements
<style>
#date-4::-webkit-inner-spin-button {}
#date-4::-webkit-calendar-picker-indicator {}
</style>
<input type="date" id="date-4">
Getting it to Work
<style>
#date-5::-webkit-inner-spin-button,
#date-5::-webkit-calendar-picker-indicator {
display: none;
}
</style>
<input type="date" id="date-5">
<script>
$( "#date-5" ).datepicker({
dateFormat: "yy-mm-dd"
});
</script>
Available Pseudo-Elements
-
WebKit
- ::-webkit-datetime-edit
- ::-webkit-datetime-edit-fields-wrapper
- ::-webkit-datetime-edit-text
- ::-webkit-datetime-edit-month-field
- ::-webkit-datetime-edit-day-field
- ::-webkit-datetime-edit-year-field
- ::-webkit-inner-spin-button
- ::-webkit-calendar-picker-indicator
-
Presto
-
Trident / Gecko
- No input[type="date"] support
Presto
iOS 6 (Same in 7)
Chrome for Android
Can we get the best of both worlds?
No
Using datepicker as a polyfill
<input type="date" id="date">
<script>
if ( !Modernizr.inputtypes.date ) {
$( "#date" ).datepicker({
dateFormat: "yy-mm-dd"
});
}
</script>
Outline
- The past
-
Compare widgets
- input[type="date"] vs. datepicker
- input[type="number"] vs. spinner
- input[type="range"] vs. slider
- What should you do today
- The future
Number Pickers
<input type="number">
<input type="text" id="spinner">
<script>
$( "#spinner" ).spinner();
</script>
Why use input[type=number]?
Chrome for Android
<input type="number">
iOS 7
<input type="number">
iOS 7
<input type="number" pattern="[0-9]*">
IE10 Mobile
<input type="number">
Opera Mobile (pre-Blink)
<input type="number" min="2" max="8" step="2">
min / max / step attributes
<input type="number" id="number-1"
min="0" max="10" step="2">
Hooks into the constraint validation API
<form>
<input type="number" id="number-2" min="0"
max="10" step="2">
<button type="submit">Submit</button>
</form>
<datalist> Integration
<input type="number" id="number-3" list="numbers-3">
<datalist id="numbers-3">
<option>40</option>
<option>41</option>
<option>42</option>
</datalist>
Limitations of input[type=number]
- Lack of style-ability
- Lack of customizability
- Lack of extensibility
- Browser Support
min / max / step attributes
<input type="text" id="spinner-attrs"
min="2" max="10" step="2">
<script>
$( "#spinner-attrs" ).spinner();
</script>
Themeable
<input type="text" id="spinner-theme">
<script>
$( "#spinner-theme" ).spinner();
</script>
Paging
<input type="text" id="spinner-1">
<script>
$( "#spinner-1" ).spinner({
page: 10
});
</script>
Currency + i18n
<input type="text" id="spinner-2">
<script>
$( "#spinner-2" ).spinner({
min: 25,
max: 2500,
step: 25,
numberFormat: "C",
culture: "en"
});
</script>
Time Picker
$.widget( "ui.timespinner", $.ui.spinner, {
options: {
// seconds
step: 60 * 1000,
// hours
page: 60
},
_parse: function( value ) {
if ( typeof value === "string" ) {
// already a timestamp
if ( Number( value ) == value ) {
return Number( value );
}
return +Globalize.parseDate( value );
}
return value;
},
_format: function( value ) {
return Globalize.format( new Date(value), "t" );
}
});
Time Picker Example
<input type="text" id="spinner-4"
value="08:00 PM">
<script>
Globalize.culture( "en" );
$( "#spinner-4" ).timespinner();
</script>
Just HTML on the DOM
<input id="crazy-spinner" value="0">
<label for="dance-height">Bounce Amount (in pixels):</label>
<input id="dance-height" value="10" min="0" step="10">
<script>
$( "#crazy-spinner" ).spinner().spinner( "widget" ).attr( "id", "crazy-spinner-container" );
var update = function() {
var height = $( "#dance-height" ).val(),
rule = '@-webkit-keyframes bounce-input { ' +
'0% { top: -' + height + 'px; } ' +
'50% { top: 0; } ' +
'100% { top: ' + height + 'px; } ' +
'}',
rule2 = rule.replace( "-webkit-", "" );
$( "#crazy-spinner-container" ).hide();
var s = document.createElement( "style" );
s.innerHTML = rule + ' ' + rule2;
document.getElementsByTagName( "head" )[ 0 ].appendChild( s );
setTimeout(function() {
$( "#crazy-spinner-container" ).show();
});
}
$( "#dance-height" ).spinner({
spin: update
}).on( "change", update ).spinner( "widget" ).attr( "id", "dance-height-widget" );
</script>
Can we get the best of both worlds?
Both Number Spinners?
<input type="number" id="spinner-both">
<script>
$( "#spinner-both" ).spinner();
</script>
Shadow DOM to the Rescue!
<input type="number" id="number-4">
<style>
#number-4::-webkit-outer-spin-button,
#number-4::-webkit-inner-spin-button {
display: none;
}
</style>
<script>
$( "#number-4" ).spinner();
</script>
Firefox 28
Can we get the best of both worlds?
No
Using spinner as a polyfill
<input type="number" id="number-picker">
<script>
if ( !Modernizr.inputtypes.number ) {
$( "#number-picker" ).spinner();
}
</script>
Outline
- The past
-
Compare widgets
- input[type="date"] vs. datepicker
- input[type="number"] vs. spinner
- input[type="range"] vs. slider
- What should you do today
- The future
Range Pickers
<input type="range">
<div id="slider"></div>
<script>
$( "#slider" ).slider();
</script>
iOS 7
<input type="range">
Chrome for Android
<input type="range">
IE10, IE11
<input type="range">
min / max / step attributes
<input type="range" id="range-1"
min="0" max="100" step="20" value="0">
<output>0</output>
<script>
$( "#range-1" ).on( "change", function() {
$( this ).next( "output" )
.val( $( this ).val() );
});
</script>
Datalist Integration
<input type="range" list="range-options">
<datalist id="range-options">
<option value="10"></option>
<option value="40"></option>
<option value="70"></option>
</datalist>
Datalists in IE10, IE11
Pseudo-Elements
-
Gecko
- ::-moz-range-track
- ::-moz-range-thumb
-
Trident
- ::-ms-fill-lower
- ::-ms-fill-upper
- ::-ms-thumb
- ::-ms-ticks-after
- ::-ms-ticks-before
- ::-ms-track
- ::-ms-tooltip
-
WebKit
- ::-webkit-slider-runnable-track
- ::-webkit-slider-thumb
Range pseudo-elements
<input type="range" id="pseudo-range">
<style>
#pseudo-range::-webkit-slider-runnable-track {
background: blue;
}
#pseudo-range::-webkit-slider-thumb {
outline: 5px solid green;
}
</style>
min / max / step
<div id="slider-attrs"></div>
<script>
$( "#slider-attrs" ).slider({
min: 0,
max: 100,
step: 20
});
</script>
Themeable
<div id="slider-theme"></div>
<script>
$( "#slider-theme" ).slider();
</script>
Multiple Handles
<div id="slider-3"></div>
<script>
$( "#slider-3" ).slider({
values: [10, 40, 70]
});
</script>
Ranges
<div id="slider-4"></div>
<script>
$( "#slider-4" ).slider({
range: true,
min: 0,
max: 100,
values: [20, 80]
});
</script>
Animation
<div id="slider-5"></div>
<script>
$( "#slider-5" ).slider({
animate: true
});
</script>
Just HTML on the DOM
<div id="crazy-control"></div>
<div id="crazy-slider"></div>
<script>
$( "#crazy-slider" ).slider();
$( "#crazy-control" ).slider({
min: 0.1,
max: 5,
step: 0.1,
value: 2.5,
slide: function(event, ui) {
var rule = '.present #crazy-slider { -webkit-animation: spin-slider ' + ui.value + 's infinite linear; }';
rule2 = '.present #crazy-slider { animation: spin-slider ' + ui.value + 's infinite linear; }';
$( "#crazy-slider" ).hide();
var s = document.createElement( "style" );
s.innerHTML = rule + rule2;
document.getElementsByTagName( "head" )[ 0 ].appendChild( s );
setTimeout(function() {
$( "#crazy-slider" ).show();
});
}
});
</script>
Outline
- The past
-
Compare widgets
- input[type="date"] vs. datepicker
- input[type="number"] vs. spinner
- input[type="range"] vs. slider
- What should you do today
- The future
To Summarize...
Native Features
- Easy
- Dependency Free
- User-Agent Optimized Input
- Hooks into Other Native Functionality
- Lack of browser support
- Not styleable / themeable
- Not customizable
- Not extensible
JS Widgets vs. Native Elements
You have to pick one or the other.
What to pick?
-
Native Element
- Simple
- High % mobile users
-
JS Widgets
- Non-trivial requirements
- Comprehensive browser support
Fallback - Option 1
<input type="date" id="date">
<script>
if ( !Modernizr.inputtypes.date ) {
$( "#date" ).datepicker({
dateFormat: "yy-mm-dd"
});
}
</script>
Fallack - Option 2
<input type="date" id="date">
<script>
if ( !Modernizr.inputtypes.date ) {
$( "#date" ).after( "<p>In yy-mm-dd format</p>" );
}
</script>
Outline
- The past
-
Compare widgets
- input[type="date"] vs. datepicker
- input[type="number"] vs. spinner
- input[type="range"] vs. slider
- What should you do today
- The future
Solutions?
<input type="date" ui="false">
Future Solutions
Add a new shadow root to an <input>
(Chrome 31+).
<style>
#shadow-date {
height: 1em;
width: 10em;
}
</style>
<input type="date" id="shadow-date">
<script>
var root = document.getElementById( "shadow-date" )
.webkitCreateShadowRoot();
$( "#shadow-date" ).datepicker({
dateFormat: "yy-mm-dd",
onSelect: function( dateText ) {
root.innerHTML = dateText;
}
});
</script>
Future Solutions
- Custom elements are part of web components specification.
- Eventually, custom form elements will be possible.
- Not part of the specification yet.
How jQuery UI / Kendo UI can help
stepUp() / stepDown()
<input type="number" id="number-step" value="1">
<script>
document.getElementById( "number-step" ).stepUp( 3 );
</script>
Range Orientation
<input type="range" id="range-orientation">
<style>
#range-orientation {
/* -webkit-appearance: slider-vertical; */
width: 10px;
height: 100px;
}
</style>
Sane Orientation API
<div id="slider-2"></div>
<script>
$( "#slider-2" ).slider({
orientation: "vertical"
});
</script>
jQuery UI Usage