Add time-picker to a text input. AutoInit() starts every
.time-picker except those marked no-autoinit.
The clock is inline, not a modal. The picker appends it to the input's parent, and it stays
visible. There is no openByDefault flag.
<div class="field">
<input type="text" class="time-picker" id="lunchtime">
<label for="lunchtime">Lunchtime</label>
</div>
Wrap the input in its own .field (or another small parent). The
picker appends the clock inside that parent.
To pick a time, click an hour on the dial, then a minute. With the default
autoSubmit: true, choosing the minute writes the input
(HH:MM AM or HH:MM PM).
You can also type in the digital fields. Enter in any of them writes the typed time. Digital edits use native input events, including paste. When focus leaves a field after an invalid edit, the field restores the last valid value. An empty field takes the current hour, minute or second and leaves the selected AM/PM period as it is.
Click the host input or press Enter in it to reload its current value into the clock. Changing the active clock field, or editing its value, cancels an older delayed clock update.
The clock hand's canvas fades with
--md-sys-motion-duration-short3, which defaults to 150ms.
Override that token to change the CSS fade. The JavaScript
duration option and its view and clock-reset timers stay
separate; a CSS override does not reschedule them. Reduced motion
disables the fade, including when the token has a nonzero override.
Initialization
The IIFE bundle exposes Expressive.Timepicker.
Call init yourself when you need options other than the defaults,
or let Expressive.AutoInit() start every .time-picker.
Unlike Datepicker, the default options already show the clock.
document.addEventListener('DOMContentLoaded', function() {
const elems = document.querySelectorAll('.time-picker');
const instances = Expressive.Timepicker.init(elems, {
// specify options here
});
});
You can also pass per-instance options through AutoInit:
Expressive.AutoInit(document.body, {
Timepicker: { twelveHour: false }
});
Display
By default the clock is a .timepicker-container in the page.
displayPlugin: 'modal' puts it in a dialog instead. There is no
autoClose option.
To render the clock into a specific element instead of the
input's parent, pass a selector or a DOM node as container.
Modal dialog
Set displayPlugin: 'modal' to open the clock in a modal
<dialog class="display-modal">, drawn as a basic
dialog over a scrim. It opens when the user clicks the input or confirms it with
Enter. The dialog takes the input's label as its name, and focus moves
to the hour field.
Ok, or Enter in a field, writes the time.
Cancel, Escape or a press on the scrim closes the dialog without changing the
input and calls onCancel. Focus then goes back to the
input. A modal clock always shows Cancel and Ok, so it sets
autoSubmit: false for you.
Expressive.Timepicker.init(elem, {
displayPlugin: 'modal'
});
In a selective Sass build, add "dialog" and
"modal-display" to $components.
Docked popover
Set displayPlugin: 'docked' to wrap the clock in a
.display-docked popover. The popover appears when the user clicks the input or
confirms it with Enter, and hides when they click outside.
Expressive.Timepicker.init(elem, {
displayPlugin: 'docked'
});
The popover stays inside the input's parent and accounts for the parent's
positioning and scroll offsets. You can use positioned wrappers
such as .field directly.
displayPluginOptions is optional and takes margin (default
5), transition (10),
duration (250), and align
('left').
Options
| Name | Type | Default | Description |
|---|---|---|---|
duration |
Number | 350 |
Transition between the hours and minutes views, in milliseconds. |
container |
Element or String | null |
Element or selector to render the clock into. When omitted, the picker appends the clock to the input's parent. |
showClearBtn |
Boolean | false |
Show a Clear button in the footer. |
autoSubmit |
Boolean | true |
If true, choosing a minute writes the input. If false, Ok and Cancel buttons are added. |
defaultTime |
String | 'now' |
Initial time. 'now' or a 'HH:MM' or 'HH:MM:SS' string (optionally with AM/PM). |
fromNow |
Number | 0 |
Millisecond offset added to 'now'. |
i18n |
Object | See below | Labels for Cancel, Clear, Ok, Hours, Minutes and Seconds. |
twelveHour |
Boolean | true, or the language's |
If true, use a 12-hour clock with AM/PM. If false, use 24-hour hours on two rings. With a lang on or around the input, it defaults to the clock that language uses. |
showSeconds |
Boolean | false |
Add a seconds field after the minutes and write HH:MM:SS. The dial still sets hours and minutes only. |
minTime |
String | null |
The earliest time a person can choose, as 24-hour HH:MM or HH:MM:SS. Without showSeconds it rounds up to a whole minute. |
maxTime |
String | null |
The latest time a person can choose, as 24-hour HH:MM or HH:MM:SS. Without showSeconds it rounds down to a whole minute. |
vibrate |
Boolean | true |
Vibrate the device when the clock hand changes value. |
onSelect |
Function | null |
Called when a time is chosen on the dial. Receives (hour, minute). |
onInputInteraction |
Function | null |
Called when the input is clicked or confirmed with Enter. |
onDone |
Function | null |
Called once when Ok is activated by click, Enter or Space. The button is created when autoSubmit is false. |
onCancel |
Function | null |
Called once when Cancel is activated by click, Enter or Space. The button is created when autoSubmit is false. |
displayPlugin |
String | null |
Set to 'docked' for a click-to-open popover, or 'modal' for a dialog with Cancel and Ok. |
displayPluginOptions |
Object | null |
Options for the docked plugin: margin, transition, duration, align. The modal plugin takes none. |
dialRadius |
Number | 135 |
Radius of the dial, in pixels. The clock face is drawn at twice this size inside .timepicker-plate, which CSS fixes at 260px. Change the plate's width and height in step with this option. |
outerRadius |
Number | 105 |
Distance from the center to the minutes and the outer hour ring, in pixels. |
innerRadius |
Number | 70 |
Distance from the center to the inner hour ring of a 24-hour clock, in pixels. |
tickRadius |
Number | 20 |
Radius of the circle that marks the selected value, in pixels. Each .timepicker-tick is 40px in CSS, twice the default, so resize the ticks in step with this option. |
Internationalization
Pass a partial i18n object. Missing keys keep the English defaults. The hours, minutes and seconds keys give the inputs their accessible names.
The AM/PM buttons expose their selected state and support Enter and Space. The hour, minute and second fields are spin buttons. Up and Down step them by one, and Page Up and Page Down step by five, wrapping round. The hand follows the hour and minute. Hours step round the day, so in 12-hour time, passing 12 switches AM and PM. Up from 11 AM comes 12 PM. Tab moves between the fields, the AM/PM buttons and the footer.
The dial itself takes pointer input only. The spin buttons set the same values from the keyboard and for a screen reader, so a focusable dial would only add a second Tab stop for them.
| Key | Default |
|---|---|
cancel |
'Cancel' |
clear |
'Clear' |
done |
'Ok' |
hours | 'Hours' |
minutes | 'Minutes' |
seconds | 'Seconds' |
12-hour and 24-hour
Set twelveHour: false for a 24-hour dial, with hours
1 to 12 on the inner ring and 13 to 00 on the outer ring. The written value is then
HH:MM with no meridian. The intro clock above is
12-hour with AM/PM.
twelveHour defaults to true. With a lang
on or around the input, it defaults to the clock that language uses. That is 24-hour for
de or fr, and 12-hour for en-US.
Expressive.Timepicker.init(document.getElementById('timepicker-24'), {
twelveHour: false
});
Seconds
Set showSeconds: true to add a third field after the minutes. It is a spin button named by i18n.seconds. It steps and takes typed values like the minute field, and an emptied field takes the current second. The written value becomes HH:MM:SS, with AM or PM on a 12-hour clock. A value read without seconds, such as 14:30, opens at zero seconds.
The dial keeps its hours and minutes views, as the Material 3 time picker does. You set seconds in the field only. Under autoSubmit, choosing a minute on the dial writes the time with the seconds the field shows.
Expressive.Timepicker.init(document.getElementById('timepicker-seconds'), {
showSeconds: true
});
A type="time" input gets 24-hour HH:MM:SS. Give it step="1" so the browser doesn't flag the seconds as a step mismatch.
<input type="time" step="1" class="time-picker" id="alarm">
Time range
Set minTime and maxTime to bound the times a person can choose. Pass them as 24-hour 'HH:MM' or 'HH:MM:SS' strings. Either one alone bounds one end of the day. The range can't cross midnight. If minTime is after maxTime, the picker logs a warning and ignores both. Without showSeconds, the picker writes whole minutes, so minTime rounds up and maxTime rounds down to a minute.
An hour stays available while any minute of it is in range. With 09:00 to 17:30, the hour 17 is available and its minutes stop at 30. The picker dims dial ticks outside the range, and the hand won't stop on them. Releasing on one changes nothing and fires no select.
In the fields, Up and Down stop at the last allowed value instead of wrapping. The picker refuses a typed time outside the range. Each field's aria-valuemin and aria-valuemax follow the allowed values. On a 12-hour clock, the picker disables the AM or PM button when no time in that half of the day is allowed.
A value outside the range, in the input or defaultTime, opens at the nearest bound. So does a minute that an hour change leaves outside the range. For example, choosing 17 at 16:45 gives 17:30.
Expressive.Timepicker.init(document.getElementById('timepicker-range'), {
minTime: '09:00',
maxTime: '17:30'
});
The picker doesn't check a value typed straight into the input. On a type="time" input, also set min and max, so the browser flags a value outside the range.
<input type="time" min="09:00" max="17:30" class="time-picker" id="appointment">
Methods
All methods are called on the plugin instance. You can get the instance like this:
const instance = Expressive.Timepicker.getInstance(elem);
.showView();
Show the hours or minutes face.
String: 'hours' or 'minutes'.
instance.showView('hours');
.done();
Write the current time to the input, with seconds when showSeconds is on. Pass a truthy argument to clear the input instead.
instance.done();
instance.done(true);
.clear();
Clear the input. Same as done(true).
instance.clear();
.destroy();
Destroy the plugin instance, remove the clock, and tear down its event handlers. The method also cancels pending automatic submission and clock updates, so the destroyed instance cannot write to the input.
instance.destroy();
Properties
| Name | Type | Description |
|---|---|---|
el |
Element | The input the plugin was initialized with. |
options |
Object | The options the instance was initialized with. |
time |
String | The last value written by done(), without the AM/PM suffix. It ends in :SS when showSeconds is on. |
hours |
Number | The hour currently shown on the clock. |
minutes |
Number | The minute currently shown on the clock. |
seconds |
Number | The second of the time on the clock. The seconds field shows it when showSeconds is on. |
amOrPm |
String | 'AM' or 'PM' when twelveHour is true. |
currentView |
String | 'hours' or 'minutes'. |