Events System
This page covers the internals of the Oidis GUI event system — how events flow through the framework, what event types and event args are available, and when to use getEvents() directly versus the higher-level EventsBinding API.
For the everyday patterns used in controllers, see Events and Validation.
Two-Level Architecture
The Oidis GUI event system operates on two levels simultaneously:
Global EventsManager (singleton)
A singleton accessible via Loader.getInstance().getHttpResolver().getEvents(). It manages application-wide event dispatching — components fire events into the global manager, and subscribed handlers across the application are notified.
Local ElementEventsManager (per element)
Every GUI component exposes a local ElementEventsManager through .getEvents(). This instance manages event handlers for that specific element and wires them to the native DOM element when Subscribe() is called during rendering.
Native browser event (onclick, onmouseover, …)
│
▼
ElementEventsManager.Subscribe() ← hooks handlers to DOM element
│
▼
Local handler list (per EventType)
│
├──▶ Global EventsManager.FireEvent() ← dispatches to application-wide subscribers
│
└──▶ Additional local callbacks (if setEvent() was used directly)
The key insight: most events registered via .getEvents().setOnXxx() delegate to the global EventsManager, not to a purely local list. Only a subset of events (primarily mouse events on rendered elements) are truly element-local.
EventsBinding vs. getEvents() — When to Use Which
| Scenario | Recommended approach |
|---|---|
Buttons and links in resolver() | EventsBinding.SingleClick() (the only EventsBinding method) |
Inputs in resolver() | .getEvents().setOnChange(), .getEvents().setEvent("oninput", ...) |
| Dynamically created items (DropDown items, list entries) | .getEvents().setOnClick() |
| Table row events | .getEvents() inside BindData third callback |
| Lifecycle events (show, hide, load) | .getEvents().setOnShow(), .getEvents().setOnLoad() |
| Resize, drag, scroll on custom components | .getEvents().setOnResize(), .getEvents().setOnDrag(), etc. |
EventsBinding is a thin wrapper over getEvents() that adds async handler support and enforces the try/catch convention. For all standard controller patterns, prefer EventsBinding. Use getEvents() directly when you need to register events on objects that aren't available as typed panel references.
Page-wide Events — WindowManager.getEvents()
For behaviour that is not tied to one component (a hover card over any element with a data attribute, Esc closing a popup, a click outside), register on the window events manager instead of calling addEventListener (a lint warning):
import { WindowManager } from "@io-oidis-gui/Io/Oidis/Gui/Utils/WindowManager.js";
WindowManager.getEvents().setOnMove(($args : MoveEventArgs) : void => {
const target : Element = <Element>$args.NativeEventArgs()?.target; // element under the pointer
});
WindowManager.getEvents().setOnKeyDown(($args : KeyEventArgs) : void => { /* $args.NativeEventArgs().key */ });
WindowManager.getEvents().setOnClick(($args : MouseEventArgs) : void => { /* $args.NativeEventArgs().target */ });
EventsManager.Subscribe() wires only these native document/window handlers: keydown, keyup, keypress, click, dblclick, contextmenu, mousedown, mouseup, mousemove / touchmove (→ setOnMove), resize and the wheel (→ setOnScroll).
setOnMouseOver / setOnMouseOut on the window never fireWindowEventsManager exposes setOnMouseOver() and setOnMouseOut(), but no native mouseover/mouseout handler is attached to the document, so the handlers are silently never called. Track hover with setOnMove() and $target.closest("[data-…]") instead (see Person Cards).
Handlers are keyed by the SHA-1 of their source text, so registering the same function body twice replaces it rather than adding a second handler; still guard one-time setup with a static flag.
EventType Reference
EventType extends the Commons base EventType. All values are strings.
Lifecycle Events
| Constant | Value | Fired when |
|---|---|---|
EventType.ON_START | "onstart" | Component initialization begins |
EventType.BEFORE_LOAD | "beforeload" | Before data loading starts |
EventType.ON_LOAD | "onload" | Component has loaded its data |
EventType.ON_COMPLETE | "oncomplete" | All rendering and loading is done |
EventType.ON_SHOW | "onshow" | Element becomes visible |
EventType.ON_HIDE | "onhide" | Element becomes hidden |
EventType.ON_FOCUS | "onfocus" | Element gains keyboard focus |
EventType.ON_BLUR | "onblur" | Element loses keyboard focus |
EventType.ON_CHANGE | "onchange" | Value changes (inputs, dropdowns) |
EventType.BEFORE_REFRESH | "beforerefresh" | Window before unload (Commons) |
Mouse Events
| Constant | Value | Fired when |
|---|---|---|
EventType.ON_CLICK | "onclick" | Left mouse button click |
EventType.ON_DOUBLE_CLICK | "ondblclick" | Double click |
EventType.ON_RIGHT_CLICK | "onrightclick" | Right mouse button click |
EventType.ON_MOUSE_OVER | "onmouseover" | Cursor enters element |
EventType.ON_MOUSE_OUT | "onmouseout" | Cursor leaves element |
EventType.ON_MOUSE_DOWN | "onmousedown" | Mouse button pressed |
EventType.ON_MOUSE_UP | "onmouseup" | Mouse button released |
EventType.ON_MOUSE_MOVE | "onmousemove" | Cursor moves over element |
Keyboard Events
| Constant | Value | Fired when |
|---|---|---|
EventType.ON_KEY_PRESS | "onkeypress" | Key pressed (character produced) |
EventType.ON_KEY_DOWN | "onkeydown" | Key pressed down |
EventType.ON_KEY_UP | "onkeyup" | Key released |
Touch Events
Touch events on mouse-based handlers (setOnMouseDown, setOnMouseUp, setOnMouseMove) are automatically unified — touch contacts are internally converted to MouseEventArgs, so a single handler covers both mouse and touch.
| Constant | Value | Touch equivalent of |
|---|---|---|
EventType.ON_TOUCH_START | "ontouchstart" | ON_MOUSE_DOWN |
EventType.ON_TOUCH_END | "ontouchend" | ON_MOUSE_UP |
EventType.ON_TOUCH_MOVE | "ontouchmove" | ON_MOUSE_MOVE |
Resize Events
| Constant | Value | Fired when |
|---|---|---|
EventType.BEFORE_RESIZE | "beforeResize" | Resize is about to start |
EventType.ON_RESIZE | "onresize" | Element resized (end state) |
EventType.ON_RESIZE_START | "onresizeStart" | Resize drag initiated |
EventType.ON_RESIZE_CHANGE | "onresizeChange" | Size changing during drag |
EventType.ON_RESIZE_COMPLETE | "onresizeComplete" | Resize drag released |
Drag Events
| Constant | Value | Fired when |
|---|---|---|
EventType.ON_DRAG | "ondrag" | Element dragged |
EventType.ON_DRAG_START | "ondragStart" | Drag initiated |
EventType.ON_DRAG_CHANGE | "ondragChange" | Position changing during drag |
EventType.ON_DRAG_COMPLETE | "ondragComplete" | Drag released |
EventType.ON_DRAG_OVER | "ondragover" | Dragged element over target |
EventType.ON_DROP | "ondrop" | Element dropped onto target |
Scroll Events
| Constant | Value | Fired when |
|---|---|---|
EventType.ON_SCROLL | "onscroll" | Any scroll direction |
EventType.ON_VERTICAL_SCROLL | "onverticalscroll" | Vertical scroll only |
EventType.ON_HORIZONTAL_SCROLL | "onhorizontalscroll" | Horizontal scroll only |
Other
| Constant | Value | Fired when |
|---|---|---|
EventType.ON_SELECT | "onselect" | Item selected in list or tree |
Specialized Event Type Enums
Components with domain-specific events use their own enum classes, all extending EventType.
DialogEventType
import { DialogEventType } from "@io-oidis-gui/.../Enums/Events/DialogEventType.js";
| Constant | Fired when |
|---|---|
DialogEventType.ON_OPEN | Dialog becomes visible |
DialogEventType.ON_CLOSE | Dialog is closed |
BasePanelHolderEventType
Extends DialogEventType. Used by panel holder components (tab containers, panel switchers).
| Constant | Fired when |
|---|---|
BasePanelHolderEventType.BEFORE_OPEN | Before panel becomes visible |
BasePanelHolderEventType.BEFORE_CLOSE | Before panel is hidden |
BasePanelHolderEventType.ON_RESIZE | Holder resized |
ScrollBarEventType
import { ScrollBarEventType } from "@io-oidis-gui/.../Enums/Events/ScrollBarEventType.js";
| Constant | Fired when |
|---|---|
ScrollBarEventType.ON_ARROW | Arrow button clicked |
ScrollBarEventType.ON_BUTTON | Scrollbar button area clicked |
ScrollBarEventType.ON_TRACKER | Tracker (thumb) dragged |
AudioEventType
import { AudioEventType } from "@io-oidis-gui/.../Enums/Events/AudioEventType.js";
| Constant | Fired when |
|---|---|
AudioEventType.ON_PLAY | Playback started |
AudioEventType.ON_PAUSE | Playback paused |
AudioEventType.ON_STOP | Playback stopped |
AudioEventType.ON_MUTE | Audio muted |
AudioEventType.ON_UNMUTE | Audio unmuted |
AudioEventType.ON_OPEN | Media element opened |
VideoEventType
Extends AudioEventType. Adds fullscreen control events.
| Constant | Fired when |
|---|---|
VideoEventType.ON_FULLSCREEN_START | Entered fullscreen mode |
VideoEventType.ON_FULLSCREEN_END | Exited fullscreen mode |
FileUploadEventType
import { FileUploadEventType } from "@io-oidis-gui/.../Enums/Events/FileUploadEventType.js";
| Constant | Fired when |
|---|---|
FileUploadEventType.ON_ABOARD | Upload cancelled |
FileUploadEventType.ON_UPLOAD_START | Upload initiated |
FileUploadEventType.ON_UPLOAD_CHANGE | Upload progress changed |
FileUploadEventType.ON_UPLOAD_COMPLETE | Upload finished |
DirectoryBrowserEventType
import { DirectoryBrowserEventType } from "@io-oidis-gui/.../Enums/Events/DirectoryBrowserEventType.js";
| Constant | Fired when |
|---|---|
DirectoryBrowserEventType.ON_PATH_REQUEST | User navigates to a path |
DirectoryBrowserEventType.ON_DIRECTORY_REQUEST | User opens a directory |
DirectoryBrowserEventType.ON_CREATE_DIRECTORY_REQUEST | User requests new directory |
DirectoryBrowserEventType.ON_RENAME_REQUEST | User requests rename |
DirectoryBrowserEventType.ON_DELETE_REQUEST | User requests delete |
Event Handler Signature
All event handlers share the same signature:
($args : EventArgs, $manager : GuiObjectManager, $reflection : Reflection) : void
| Parameter | Type | Purpose |
|---|---|---|
$args | EventArgs (or subclass) | Event data — cast to the concrete subclass for the event type |
$manager | GuiObjectManager | Access to GUI element state and registry |
$reflection | Reflection | Runtime type checking via Implements() |
In practice, you rarely need $manager and $reflection in application code. The important one is $args — cast it to the appropriate EventArgs subclass to access event-specific data.
EventArgs Classes
All EventArgs classes extend EventArgs from io-oidis-commons.
MouseEventArgs
Available in: ON_CLICK, ON_DOUBLE_CLICK, ON_RIGHT_CLICK, ON_MOUSE_OVER, ON_MOUSE_OUT, ON_MOUSE_DOWN, ON_MOUSE_UP, ON_MOUSE_MOVE
import { MouseEventArgs } from "@io-oidis-gui/.../Events/Args/MouseEventArgs.js";
component.getEvents().setOnClick(($args : MouseEventArgs) : void => {
const nativeEvent : MouseEvent = $args.NativeEventArgs();
// nativeEvent.clientX, nativeEvent.clientY, nativeEvent.button, etc.
});
Provides direct access to the native MouseEvent via NativeEventArgs().
KeyEventArgs
Available in: ON_KEY_DOWN, ON_KEY_UP, ON_KEY_PRESS
import { KeyEventArgs } from "@io-oidis-gui/.../Events/Args/KeyEventArgs.js";
input.getEvents().setOnKeyDown(($args : KeyEventArgs) : void => {
const code : number = $args.getKeyCode(); // numeric key code
const char : string = $args.getKeyChar(); // string character (shift-aware)
// Also: $args.NativeEventArgs().ctrlKey, .altKey, .shiftKey
});
getKeyChar() handles numpad keys, special symbols, and shift-modified characters via an internal mapping table.
TouchEventArgs
Available in: ON_TOUCH_START, ON_TOUCH_END, ON_TOUCH_MOVE
Provides NativeEventArgs() returning the native TouchEvent. Note: when you use setOnMouseDown(), setOnMouseUp(), or setOnMouseMove(), touch equivalents are automatically registered and touch contacts are converted to MouseEventArgs — so you rarely need to handle TouchEventArgs directly.
MoveEventArgs
Available in: drag events (ON_DRAG, ON_DRAG_START, ON_DRAG_CHANGE, ON_DRAG_COMPLETE)
import { MoveEventArgs } from "@io-oidis-gui/.../Events/Args/MoveEventArgs.js";
dragBar.getEvents().setOnDragChange(($args : MoveEventArgs) : void => {
const x : number = $args.getPositionX(); // current mouse X (scroll-corrected)
const y : number = $args.getPositionY(); // current mouse Y (scroll-corrected)
const dx : number = $args.getDistanceX(); // delta from drag start X
const dy : number = $args.getDistanceY(); // delta from drag start Y
});
Distance values are computed from the drag origin — useful for implementing custom drag-resize behaviors.
ResizeEventArgs
Available in: ON_RESIZE, ON_RESIZE_START, ON_RESIZE_CHANGE, ON_RESIZE_COMPLETE, BEFORE_RESIZE
import { ResizeEventArgs } from "@io-oidis-gui/.../Events/Args/ResizeEventArgs.js";
panel.getEvents().setOnResize(($args : ResizeEventArgs) : void => {
const w : number = $args.Width(); // element width in px
const h : number = $args.Height(); // element height in px
const aw : number = $args.AvailableWidth(); // width minus vertical scrollbar
const ah : number = $args.AvailableHeight(); // height minus horizontal scrollbar
const sbw : number = $args.ScrollBarWidth(); // vertical scrollbar width (0 if hidden)
const sbh : number = $args.ScrollBarHeight(); // horizontal scrollbar height (0 if hidden)
});
ScrollEventArgs
Available in: ON_SCROLL, ON_VERTICAL_SCROLL, ON_HORIZONTAL_SCROLL
import { ScrollEventArgs } from "@io-oidis-gui/.../Events/Args/ScrollEventArgs.js";
import { OrientationType } from "@io-oidis-gui/.../Enums/OrientationType.js";
import { DirectionType } from "@io-oidis-gui/.../Enums/DirectionType.js";
scrollBar.getEvents().setOnScroll(($args : ScrollEventArgs) : void => {
const orientation : OrientationType = $args.OrientationType(); // HORIZONTAL | VERTICAL
const direction : DirectionType = $args.DirectionType(); // UP | DOWN
const position : number = $args.Position(); // 0–100 (percentage)
});
Position is normalized to a 0–100 range regardless of the underlying scroll distance.
MediaEventArgs
Available in audio/video events (ON_PLAY, ON_PAUSE, ON_STOP, ON_MUTE, ON_UNMUTE, etc.)
Wraps the native HTML media event. Access via NativeEventArgs().
Other EventArgs Classes
| Class | Used with | Key data |
|---|---|---|
ValueProgressEventArgs | Animation/progress | Step, RangeStart, RangeEnd, CurrentValue, DirectionType, ProgressType |
ResizeBarEventArgs | ResizeBar component | Resize bar drag data |
PanelResizeEventArgs | BasePanel resize | Panel-specific resize info |
NumberPickerEventArgs | NumberPicker component | Numeric value change |
ProgressBarEventArgs | ProgressBar component | Progress update data |
CropBoxEventArgs | CropBox component | Crop area coordinates |
DirectoryBrowserEventArgs | DirectoryBrowser | Selected path/file data |
FileUploadEventArgs | FileUpload component | Upload progress and file info |
Direct Event Registration
When you need to register events outside of EventsBinding, use getEvents() directly:
// Lifecycle events — show/hide tracking
panel.getEvents().setOnShow(($args : EventArgs) : void => {
// Panel became visible
});
panel.getEvents().setOnHide(($args : EventArgs) : void => {
// Panel was hidden
});
// Keyboard events on an input
textField.getEvents().setOnKeyDown(($args : KeyEventArgs) : void => {
if ($args.getKeyCode() === 13) { // Enter
// Submit on Enter key
}
});
// Drag events on a custom component
dragBar.getEvents().setOnDragStart(($args : MoveEventArgs) : void => {
// Capture start position
});
dragBar.getEvents().setOnDragChange(($args : MoveEventArgs) : void => {
// Update position — $args.getDistanceX(), $args.getDistanceY()
});
Error Handling Requirement
The same try/catch requirement that applies to EventsBinding handlers applies to getEvents() handlers whenever they contain async logic:
component.getEvents().setOnClick(($args : MouseEventArgs) : void => {
try {
// synchronous logic — try/catch still recommended for safety
} catch (ex) {
LogIt.Error("Handler failed", ex);
}
});
Preventing Default Browser Behavior
When an <a> element has a hash-route href (e.g., #/settings) but you want to handle the click with custom logic instead of triggering navigation, use PreventDefault() on the event args.
Why not javascript:void(0)? A semantic href is cleaner — it gives the link proper appearance, accessibility, and right-click behavior. javascript:void(0) is an anti-pattern.
HTML pattern — use a real hash route as href, bind the element to a Button:
// In innerHtml()
<a href="#/targetRoute" data-oidis-bind="${this.linkButton}">Link text</a>
Handler with EventsBinding (preferred in controllers):
import { EventArgs } from "@io-oidis-commons/Io/Oidis/Commons/Events/Args/EventArgs.js";
EventsBinding.SingleClick($instance.linkButton, async ($args : EventArgs) : Promise<void> => {
$args.PreventDefault();
// Custom navigation or logic here
});
Handler with getEvents() (used in panels, e.g., innerCode()):
this.linkButton.getEvents().setOnClick(($args : EventArgs) : void => {
$args.PreventDefault();
// Custom logic here
});
Reference implementation: TwoFactorChallengePanel.ts in io-oidis-hub-gui — uses <a href="#/goToPersonalCode"> with $args.PreventDefault() to switch between 2FA and personal code views without triggering hash navigation.
AnimationThreadPool
An internal mechanism that manages requestAnimationFrame-based animation loops for CORE components (opacity transitions, progress bars, animated values). Solution developers do not call this directly — it is invoked indirectly when using animated component features like opacity changes or progress animations.
See also: Events and Validation | Panels and Controllers | User Controls