Skip to main content

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​

ScenarioRecommended 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 fire

WindowEventsManager 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​

ConstantValueFired 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​

ConstantValueFired 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​

ConstantValueFired 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.

ConstantValueTouch 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​

ConstantValueFired 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​

ConstantValueFired 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​

ConstantValueFired 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​

ConstantValueFired 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";
ConstantFired when
DialogEventType.ON_OPENDialog becomes visible
DialogEventType.ON_CLOSEDialog is closed

BasePanelHolderEventType​

Extends DialogEventType. Used by panel holder components (tab containers, panel switchers).

ConstantFired when
BasePanelHolderEventType.BEFORE_OPENBefore panel becomes visible
BasePanelHolderEventType.BEFORE_CLOSEBefore panel is hidden
BasePanelHolderEventType.ON_RESIZEHolder resized

ScrollBarEventType​

import { ScrollBarEventType } from "@io-oidis-gui/.../Enums/Events/ScrollBarEventType.js";
ConstantFired when
ScrollBarEventType.ON_ARROWArrow button clicked
ScrollBarEventType.ON_BUTTONScrollbar button area clicked
ScrollBarEventType.ON_TRACKERTracker (thumb) dragged

AudioEventType​

import { AudioEventType } from "@io-oidis-gui/.../Enums/Events/AudioEventType.js";
ConstantFired when
AudioEventType.ON_PLAYPlayback started
AudioEventType.ON_PAUSEPlayback paused
AudioEventType.ON_STOPPlayback stopped
AudioEventType.ON_MUTEAudio muted
AudioEventType.ON_UNMUTEAudio unmuted
AudioEventType.ON_OPENMedia element opened

VideoEventType​

Extends AudioEventType. Adds fullscreen control events.

ConstantFired when
VideoEventType.ON_FULLSCREEN_STARTEntered fullscreen mode
VideoEventType.ON_FULLSCREEN_ENDExited fullscreen mode

FileUploadEventType​

import { FileUploadEventType } from "@io-oidis-gui/.../Enums/Events/FileUploadEventType.js";
ConstantFired when
FileUploadEventType.ON_ABOARDUpload cancelled
FileUploadEventType.ON_UPLOAD_STARTUpload initiated
FileUploadEventType.ON_UPLOAD_CHANGEUpload progress changed
FileUploadEventType.ON_UPLOAD_COMPLETEUpload finished

DirectoryBrowserEventType​

import { DirectoryBrowserEventType } from "@io-oidis-gui/.../Enums/Events/DirectoryBrowserEventType.js";
ConstantFired when
DirectoryBrowserEventType.ON_PATH_REQUESTUser navigates to a path
DirectoryBrowserEventType.ON_DIRECTORY_REQUESTUser opens a directory
DirectoryBrowserEventType.ON_CREATE_DIRECTORY_REQUESTUser requests new directory
DirectoryBrowserEventType.ON_RENAME_REQUESTUser requests rename
DirectoryBrowserEventType.ON_DELETE_REQUESTUser requests delete

Event Handler Signature​

All event handlers share the same signature:

($args : EventArgs, $manager : GuiObjectManager, $reflection : Reflection) : void
ParameterTypePurpose
$argsEventArgs (or subclass)Event data — cast to the concrete subclass for the event type
$managerGuiObjectManagerAccess to GUI element state and registry
$reflectionReflectionRuntime 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​

ClassUsed withKey data
ValueProgressEventArgsAnimation/progressStep, RangeStart, RangeEnd, CurrentValue, DirectionType, ProgressType
ResizeBarEventArgsResizeBar componentResize bar drag data
PanelResizeEventArgsBasePanel resizePanel-specific resize info
NumberPickerEventArgsNumberPicker componentNumeric value change
ProgressBarEventArgsProgressBar componentProgress update data
CropBoxEventArgsCropBox componentCrop area coordinates
DirectoryBrowserEventArgsDirectoryBrowserSelected path/file data
FileUploadEventArgsFileUpload componentUpload 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