Events
Handle timepicker events using EventEmitter API or callbacks
EventEmitter API (Recommended)
#The new EventEmitter API provides a clean, type-safe way to handle events:
on(event, handler)
Subscribe to an event. Handler is called every time the event fires. Returns a function that removes this handler. Calling it more than once does nothing.
const picker = new TimepickerUI(input);picker.create();const unsubscribe = picker.on('confirm', (data) => {console.log('Time confirmed:', data);});unsubscribe();
once(event, handler)
Subscribe to an event that fires only once. Returns a function that removes the handler before it runs.
const unsubscribe = picker.once('open', () => {console.log('Opened for the first time');});unsubscribe();
off(event, handler?)
Unsubscribe from an event. Without a handler, every handler for that event is removed.
const handler = (data) => console.log(data);picker.on('confirm', handler);picker.off('confirm', handler);
Available Events
#All events you can subscribe to. These events work in both clock (default) and wheel mode. range:minute:commit is an internal event between the clock and the range plugin and is not part of the public contract. With showSeconds its payload also has seconds. range:get-disabled-time and range:update-disabled are deprecated, never emitted, and will be removed in 5.0.
confirm
Triggered when user confirms time selection.
picker.on('confirm', (data) => {console.log('Hour:', data.hour);console.log('Minutes:', data.minutes);console.log('Seconds:', data.seconds); // only with clock.showSecondsconsole.log('Type:', data.type); // 'AM' or 'PM' (12h mode)console.log('Auto commit:', data.autoCommit);// autoCommit is true when the confirm was triggered// by wheel.commitOnScroll instead of the OK button});
cancel
Triggered when user cancels or closes picker. The payload is empty.
picker.on('cancel', () => {console.log('Cancelled');});
open
Triggered when the picker opens: its markup has been added to the page and filled with the input's value. Fires for user activation and for open().
picker.on('open', (data) => {console.log('Picker opened');console.log(data.hour, data.minutes, data.type);console.log(data.seconds); // only with clock.showSeconds// data.degreesHours and data.degreesMinutes are deprecated});
show
Triggered when the user asks to open the picker: a click on the input or a data-open element, or Enter or Space on it. Empty payload. Your handler (on('show') or callbacks.onShow) runs before the picker renders, so .tp-ui-modal does not exist yet inside it. open fires after the picker is rendered. picker.open() does not emit show. It renders the picker and emits open.
picker.on('show', () => {console.log('User asked to open the picker');// The picker is not rendered yet; use 'open' to reach its markup});
hide
Deprecated. Never emitted. The event and HideEventData will be removed in 5.0.
// Do not use: 'hide' is never emitted.
update
Triggered when time value changes during interaction.
picker.on('update', (data) => {console.log('Time updated:', data.hour, data.minutes);console.log('Seconds:', data.seconds); // only with clock.showSecondsconsole.log('Type:', data.type); // 'AM' or 'PM' (12h mode)});
select:hour
Triggered when hour is selected. In a time field, Enter applies the typed value like blur does: the value is clamped, then select:hour, select:minute or select:second and update are emitted. This holds in every mode. New in 4.7.0.
picker.on('select:hour', (data) => {console.log('Hour selected:', data.hour);});
select:minute
Triggered when minute is selected.
picker.on('select:minute', (data) => {console.log('Minute selected:', data.minutes);});
select:second
Triggered when the seconds field or face is selected, and when the seconds wheel column stops on a value. Needs clock.showSeconds. New in 4.5.0.
picker.on('select:second', (data) => {console.log('Seconds selected:', data.seconds);});
select:period
Triggered when AM or PM is selected (12h mode only). The payload is { type: 'AM' | 'PM' }. New in 4.5.0.
picker.on('select:period', (data) => {console.log('Period selected:', data.type); // 'AM' or 'PM'});
select:am / select:pm
Deprecated. Use select:period. Both still fire next to select:period and will be removed in 5.0.
// Deprecated: listen to 'select:period' insteadpicker.on('select:period', (data) => {if (data.type === 'AM') console.log('AM selected');});
switch:face
Triggered when the clock face switches between hours, minutes and seconds: a click on a field, Enter or Space on it, or the automatic switch after a pick. Clicking the field that is already active does not trigger it. The payload is { face: 'hours' | 'minutes' | 'seconds' }. Use it to follow face changes: in 5.0, select:hour, select:minute and select:second fire only when a value is picked. New in 4.5.0.
picker.on('switch:face', (data) => {console.log('Face shown:', data.face); // 'hours' | 'minutes' | 'seconds'});
switch:view
Triggered when the view changes between the clock and the keyboard: the switch icon (needs ui.viewSwitch) or setView() on an open picker, or setOptions() on an open picker when the call changes ui.initialView. In that last case the event fires once, after the new view is shown. A closed picker emits nothing and opens in the new view. The payload is { view: 'clock' | 'keyboard' } and holds the view the picker switches to. Not emitted in wheel modes.
picker.on('switch:view', (data) => {console.log('View shown:', data.view); // 'clock' | 'keyboard'});
error
Triggered on an invalid input value, on a setValue() string that is not a time, on invalid options (for example a malformed clock.disabledTime.ranges) and when a needed plugin is not registered. With the timezone plugin it also fires once when timezone.default is not in the timezone list, for example timezone.default "Europe/Warsaw" is not in the timezone list, using "Europe/Paris". New in 4.7.0. rejectedHour and rejectedMinute are deprecated and always undefined.
picker.on('error', (data) => {console.error('Error:', data.error);console.log('Input values:', data.inputHour, data.inputMinute);console.log('Input seconds:', data.inputSecond); // only with clock.showSecondsconsole.log('Input type/length:', data.inputType, data.inputLength);});
clear
Triggered when the user clicks the clear button, and by clear().
picker.on('clear', (data) => {console.log('Time cleared. Previous value:', data.previousValue);});
timezone:change
Triggered when the timezone is changed (timezone plugin only).
picker.on('timezone:change', (data) => {console.log('Timezone:', data.timezone);});
range:confirm
Triggered when a time range is confirmed (range mode only). autoCommit is true when wheel.commitOnScroll committed the range instead of the OK button. That happens only while the range is valid, and the picker stays open, also with wheel.hideFooter. New in 4.7.0.
picker.on('range:confirm', (data) => {console.log('From:', data.from, 'To:', data.to);console.log('Duration (whole minutes):', data.duration);console.log('Duration (seconds):', data.durationSeconds); // only with clock.showSecondsconsole.log('Auto commit:', data.autoCommit); // true with wheel.commitOnScroll});
range:change
Triggered when the time of a range end changes by the user (range mode only). The payload has the end that changed (part) and both ends after the change, in the shape of getRange(). A 12h end without AM or PM counts as null, so the event fires once the end is complete, not for each partial step. It is emitted right after the end is written and before range:switch, range:validation and range:confirm, so getRange() inside the handler is final. It is not emitted for setRange(), setValue() or clear(). A time typed in a field is committed on blur, on Enter, or when the ends switch, and that commit emits it. So setActivePart() and setOptions() on an open picker emit it when they first commit a time typed into the active end. The Clear button emits it with part: 'from' and both ends null, before the range:switch back to From. In wheel and compact-wheel, an empty end that becomes active on open or on a tab switch takes the other end's time without this event. Read it with getRange(). New in 4.7.0.
picker.on('range:change', (data) => {console.log('Changed end:', data.part); // 'from' | 'to'console.log('From:', data.from); // { hour, minutes, seconds?, type? } | nullconsole.log('To:', data.to);});
range:switch
Triggered when the active end changes (range mode only): a tab click, setActivePart(), Clear, or the fallback below. A wheel snap never triggers it. After a re-render, for example setOptions() on an open picker, it also fires when the picker keeps To: active is 'to' and disabledTime carries the limit before From. With range.linked and From empty, the picker falls back to From and the payload is active: 'from' with disabledTime: null. Keeping the active end over a re-render is new in 4.7.0.
picker.on('range:switch', (data) => {console.log('Active segment:', data.active); // 'from' | 'to'console.log('Disabled time:', data.disabledTime);});
range:validation
Triggered when the range duration is validated (range mode only).
picker.on('range:validation', (data) => {console.log('Valid:', data.valid, 'Duration:', data.duration);console.log('Duration (seconds):', data.durationSeconds); // only with clock.showSecondsconsole.log('Limits:', data.minDuration, data.maxDuration);});
animation:clock / animation:start / animation:end
Animation coordination events fired around open/close and clock transitions. Empty payloads.
picker.on('animation:start', () => console.log('Animation started'));picker.on('animation:end', () => console.log('Animation finished'));picker.on('animation:clock', () => console.log('Clock animation'));
wheel:scroll:start
Triggered when a wheel column starts scrolling (wheel and compact-wheel modes).
picker.on('wheel:scroll:start', (data) => {console.log('Scroll started on column:', data.column);// data.column: 'hours' | 'minutes' | 'seconds' | 'ampm'});
wheel:scroll:end
Triggered when a wheel column finishes scrolling and snaps to a value (wheel and compact-wheel modes).
picker.on('wheel:scroll:end', (data) => {console.log('Column:', data.column, 'snapped to:', data.value);console.log('Previous value:', data.previousValue);// data.column: 'hours' | 'minutes' | 'seconds' | 'ampm'});
Callback Options (Alternative)
#You can also use callback options in the constructor:
onConfirm
Alternative: pass callback in options.
new TimepickerUI(input, {callbacks: {onConfirm: (data) => {console.log('Confirmed:', data);}}});
onCancel
Triggered when user cancels.
new TimepickerUI(input, {callbacks: {onCancel: (data) => {console.log('Cancelled');}}});
onOpen
Triggered when picker opens.
new TimepickerUI(input, {callbacks: {onOpen: () => {console.log('Opened');}}});
onShow
Triggered when the user asks to open the picker. See the show event above. New in 4.5.0.
new TimepickerUI(input, {callbacks: {onShow: () => {console.log('User asked to open the picker');}}});
onUpdate
Triggered during time changes.
new TimepickerUI(input, {callbacks: {onUpdate: (data) => {console.log('Updated:', data);}}});
onSelectHour
Triggered when hour is selected.
new TimepickerUI(input, {callbacks: {onSelectHour: (data) => {console.log('Hour:', data.hour);}}});
onSelectMinute
Triggered when minute is selected.
new TimepickerUI(input, {callbacks: {onSelectMinute: (data) => {console.log('Minute:', data.minutes);}}});
onSelectSecond
Triggered when the seconds field or face is selected. Needs clock.showSeconds. New in 4.5.0.
new TimepickerUI(input, {clock: { showSeconds: true },callbacks: {onSelectSecond: (data) => {console.log('Seconds:', data.seconds);}}});
onSelectPeriod
Triggered when AM or PM is selected. See the select:period event above. New in 4.5.0.
new TimepickerUI(input, {callbacks: {onSelectPeriod: (data) => {console.log('Period:', data.type); // 'AM' or 'PM'}}});
onSelectAM / onSelectPM
Deprecated. Use onSelectPeriod. Both still work and will be removed in 5.0.
// Deprecated: use onSelectPeriod insteadnew TimepickerUI(input, {callbacks: {onSelectPeriod: (data) => {if (data.type === 'PM') console.log('PM selected');}}});
onSwitchFace
Triggered when the clock face switches between hours, minutes and seconds. See the switch:face event above. New in 4.5.0.
new TimepickerUI(input, {callbacks: {onSwitchFace: (data) => {console.log('Face:', data.face);}}});
onSwitchView
Triggered when the view changes: the switch icon (needs ui.viewSwitch), setView() on an open picker, or setOptions() on an open picker when the call changes ui.initialView. The payload is { view }. New in 4.5.0.
new TimepickerUI(input, {ui: { viewSwitch: true },callbacks: {onSwitchView: (data) => {console.log('View:', data.view);}}});
onError
Triggered on an invalid input value, a setValue() string that is not a time, invalid options or a missing plugin.
new TimepickerUI(input, {callbacks: {onError: (data) => {console.error('Error:', data.error);}}});
onTimezoneChange
Triggered when timezone is changed (timezone plugin only).
new TimepickerUI(input, {callbacks: {onTimezoneChange: (data) => {console.log('Timezone:', data.timezone);}}});
onRangeConfirm
Triggered when a range is confirmed (range mode only).
new TimepickerUI(input, {callbacks: {onRangeConfirm: (data) => {console.log('Range:', data.from, '-', data.to, data.duration);}}});
onRangeChange
Triggered by range:change when the time of a range end changes (range mode only). New in 4.7.0.
new TimepickerUI(input, {callbacks: {onRangeChange: (data) => {console.log(data.part, data.from, data.to);}}});
onRangeSwitch
Triggered when switching between from/to segments (range mode only).
new TimepickerUI(input, {callbacks: {onRangeSwitch: (data) => {console.log('Active segment:', data.active);}}});
onRangeValidation
Triggered on range validation (range mode only).
new TimepickerUI(input, {callbacks: {onRangeValidation: (data) => {console.log('Valid:', data.valid, 'Duration:', data.duration);}}});
onWheelScrollStart / onWheelScrollEnd
Triggered when a wheel column starts scrolling and when it snaps to a value (wheel and compact-wheel modes). New in 4.5.0.
new TimepickerUI(input, {ui: { mode: 'wheel' }, // needs the wheel plugincallbacks: {onWheelScrollStart: (data) => {console.log('Scrolling:', data.column);},onWheelScrollEnd: (data) => {console.log(data.column, 'snapped to', data.value);}}});
onClear
Triggered when the user clicks the clear button, and by clear().
new TimepickerUI(input, {callbacks: {onClear: (data) => {console.log('Cleared:', data.previousValue);}}});
Legacy DOM Events (Removed)
#Removed in v4.0.0
DOM events (e.g., timepicker:confirm) have been removed in v4.0.0. If you're upgrading from v3.x, you must migrate to the EventEmitter API.
The EventEmitter API provides better type safety, error handling, and performance.
// REMOVED in v4.0.0input.addEventListener('timepicker:confirm', (e) => {console.log(e.detail);});// CORRECT - use the EventEmitter API insteadpicker.on('confirm', (data) => {console.log(data);});
Event Data
#Every event has its own payload type, exported from timepicker-ui. Fields marked showSeconds appear only when clock.showSeconds is on. The most important ones:
type OpenEventData = {hour: string;minutes: string;seconds?: string; // showSecondstype?: string; // 'AM' or 'PM' (12h mode)degreesHours: number | null; // deprecated, removed in 5.0degreesMinutes: number | null; // deprecated, removed in 5.0};type ConfirmEventData = {hour?: string;minutes?: string;seconds?: string; // showSecondstype?: string;autoCommit?: boolean; // true when triggered by wheel.commitOnScroll};type UpdateEventData = {hour?: string;minutes?: string;seconds?: string; // showSecondstype?: string;};type SelectSecondEventData = {seconds: string;};type SelectPeriodEventData = {type: 'AM' | 'PM';};type SwitchFaceEventData = {face: 'hours' | 'minutes' | 'seconds';};type CancelEventData = Record<string, never>; // empty payloadtype ClearEventData = {previousValue: string | null;};type ErrorEventData = {error: string;rejectedHour?: string; // deprecated, always undefinedrejectedMinute?: string; // deprecated, always undefinedinputHour?: string | number;inputMinute?: string | number;inputSecond?: string | number; // showSecondsinputType?: string;inputLength?: string | number;};type RangeConfirmEventData = {from: string;to: string;duration: number; // whole minutesdurationSeconds?: number; // showSeconds, exactautoCommit?: boolean; // true when triggered by wheel.commitOnScroll};type SwitchViewEventData = {view: 'clock' | 'keyboard';};type RangeChangeEventData = {part: 'from' | 'to';from: RangeTimeValue | null; // { hour, minutes, seconds?, type? }to: RangeTimeValue | null;};// Also exported: SelectHourEventData, SelectMinuteEventData,// SelectAMEventData and SelectPMEventData (deprecated), ShowEventData,// HideEventData (deprecated),// TimezoneChangeEventData, RangeSwitchEventData,// RangeValidationEventData, WheelScrollStartEventData,// WheelScrollEndEventData
Typed event names
#on(), once() and off() accept a TimepickerEventName, and the handler gets the payload type of that event. A name that is not in the list still compiles, with an unknown payload. Internal events are not part of the type.
import { TimepickerUI, type TimepickerEventName } from 'timepicker-ui';picker.on('range:change', (data) => {data.part; // 'from' | 'to'});const name: TimepickerEventName = 'switch:view';
Complete Example
#const picker = new TimepickerUI(input, {ui: { theme: 'dark' },clock: { type: '12h' }});picker.create();// EventEmitter API (recommended)picker.on('confirm', (data) => {console.log('Time confirmed:', data.hour, data.minutes, data.type);fetch('/api/save-time', {method: 'POST',body: JSON.stringify(data)});});picker.on('cancel', () => {console.log('User cancelled');});picker.on('update', (data) => {document.getElementById('display').textContent =`${data.hour}:${data.minutes} ${data.type || ''}`;});picker.on('select:hour', (data) => {console.log('Hour selected:', data.hour);});picker.on('select:minute', (data) => {console.log('Minute selected:', data.minutes);});picker.on('error', (data) => {console.error('Validation error:', data.error);});// One-time eventpicker.once('open', () => {console.log('Picker opened for the first time');});// Remove event listenerconst updateHandler = (data) => console.log(data);picker.on('update', updateHandler);picker.off('update', updateHandler);