timepicker-ui
4.7.0GitHub
timepicker-ui
API Reference / Events

Events

Handle timepicker events using EventEmitter API or callbacks

#

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.

index.ts
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.

index.ts
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.

index.ts
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.

index.ts
picker.on('confirm', (data) => {
console.log('Hour:', data.hour);
console.log('Minutes:', data.minutes);
console.log('Seconds:', data.seconds); // only with clock.showSeconds
console.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.

index.ts
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().

index.ts
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.

index.ts
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.

index.ts
// Do not use: 'hide' is never emitted.

update

Triggered when time value changes during interaction.

index.ts
picker.on('update', (data) => {
console.log('Time updated:', data.hour, data.minutes);
console.log('Seconds:', data.seconds); // only with clock.showSeconds
console.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.

index.ts
picker.on('select:hour', (data) => {
console.log('Hour selected:', data.hour);
});

select:minute

Triggered when minute is selected.

index.ts
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.

index.ts
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.

index.ts
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.

index.ts
// Deprecated: listen to 'select:period' instead
picker.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.

index.ts
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.

index.ts
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.

index.ts
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.showSeconds
console.log('Input type/length:', data.inputType, data.inputLength);
});

clear

Triggered when the user clicks the clear button, and by clear().

index.ts
picker.on('clear', (data) => {
console.log('Time cleared. Previous value:', data.previousValue);
});

timezone:change

Triggered when the timezone is changed (timezone plugin only).

index.ts
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.

index.ts
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.showSeconds
console.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.

index.ts
picker.on('range:change', (data) => {
console.log('Changed end:', data.part); // 'from' | 'to'
console.log('From:', data.from); // { hour, minutes, seconds?, type? } | null
console.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.

index.ts
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).

index.ts
picker.on('range:validation', (data) => {
console.log('Valid:', data.valid, 'Duration:', data.duration);
console.log('Duration (seconds):', data.durationSeconds); // only with clock.showSeconds
console.log('Limits:', data.minDuration, data.maxDuration);
});

animation:clock / animation:start / animation:end

Animation coordination events fired around open/close and clock transitions. Empty payloads.

index.ts
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).

index.ts
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).

index.ts
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.

index.ts
new TimepickerUI(input, {
callbacks: {
onConfirm: (data) => {
console.log('Confirmed:', data);
}
}
});

onCancel

Triggered when user cancels.

index.ts
new TimepickerUI(input, {
callbacks: {
onCancel: (data) => {
console.log('Cancelled');
}
}
});

onOpen

Triggered when picker opens.

index.ts
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.

index.ts
new TimepickerUI(input, {
callbacks: {
onShow: () => {
console.log('User asked to open the picker');
}
}
});

onUpdate

Triggered during time changes.

index.ts
new TimepickerUI(input, {
callbacks: {
onUpdate: (data) => {
console.log('Updated:', data);
}
}
});

onSelectHour

Triggered when hour is selected.

index.ts
new TimepickerUI(input, {
callbacks: {
onSelectHour: (data) => {
console.log('Hour:', data.hour);
}
}
});

onSelectMinute

Triggered when minute is selected.

index.ts
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.

index.ts
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.

index.ts
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.

index.ts
// Deprecated: use onSelectPeriod instead
new 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.

index.ts
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.

index.ts
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.

index.ts
new TimepickerUI(input, {
callbacks: {
onError: (data) => {
console.error('Error:', data.error);
}
}
});

onTimezoneChange

Triggered when timezone is changed (timezone plugin only).

index.ts
new TimepickerUI(input, {
callbacks: {
onTimezoneChange: (data) => {
console.log('Timezone:', data.timezone);
}
}
});

onRangeConfirm

Triggered when a range is confirmed (range mode only).

index.ts
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.

index.ts
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).

index.ts
new TimepickerUI(input, {
callbacks: {
onRangeSwitch: (data) => {
console.log('Active segment:', data.active);
}
}
});

onRangeValidation

Triggered on range validation (range mode only).

index.ts
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.

index.ts
new TimepickerUI(input, {
ui: { mode: 'wheel' }, // needs the wheel plugin
callbacks: {
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().

index.ts
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.

index.ts
// REMOVED in v4.0.0
input.addEventListener('timepicker:confirm', (e) => {
console.log(e.detail);
});
// CORRECT - use the EventEmitter API instead
picker.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:

index.ts
type OpenEventData = {
hour: string;
minutes: string;
seconds?: string; // showSeconds
type?: string; // 'AM' or 'PM' (12h mode)
degreesHours: number | null; // deprecated, removed in 5.0
degreesMinutes: number | null; // deprecated, removed in 5.0
};
type ConfirmEventData = {
hour?: string;
minutes?: string;
seconds?: string; // showSeconds
type?: string;
autoCommit?: boolean; // true when triggered by wheel.commitOnScroll
};
type UpdateEventData = {
hour?: string;
minutes?: string;
seconds?: string; // showSeconds
type?: string;
};
type SelectSecondEventData = {
seconds: string;
};
type SelectPeriodEventData = {
type: 'AM' | 'PM';
};
type SwitchFaceEventData = {
face: 'hours' | 'minutes' | 'seconds';
};
type CancelEventData = Record<string, never>; // empty payload
type ClearEventData = {
previousValue: string | null;
};
type ErrorEventData = {
error: string;
rejectedHour?: string; // deprecated, always undefined
rejectedMinute?: string; // deprecated, always undefined
inputHour?: string | number;
inputMinute?: string | number;
inputSecond?: string | number; // showSeconds
inputType?: string;
inputLength?: string | number;
};
type RangeConfirmEventData = {
from: string;
to: string;
duration: number; // whole minutes
durationSeconds?: number; // showSeconds, exact
autoCommit?: 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.

index.ts
import { TimepickerUI, type TimepickerEventName } from 'timepicker-ui';
picker.on('range:change', (data) => {
data.part; // 'from' | 'to'
});
const name: TimepickerEventName = 'switch:view';

Complete Example

#
index.ts
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 event
picker.once('open', () => {
console.log('Picker opened for the first time');
});
// Remove event listener
const updateHandler = (data) => console.log(data);
picker.on('update', updateHandler);
picker.off('update', updateHandler);
Esc