Compose Multiplatform foundation library for building web UIs with type-safe HTML DSL, CSS-in-Kotlin, and event handling, compiled for WebAssembly/JavaScript target
—
Synthetic event system providing type-safe event handling for all DOM events with proper event delegation, lifecycle management, and seamless integration with Compose's reactive system.
import org.jetbrains.compose.web.events.*
import org.jetbrains.compose.web.attributes.*
import androidx.compose.runtime.*Core event interfaces and base functionality providing consistent event handling across all DOM events.
/**
* Base class for all synthetic events with common properties and methods
*/
abstract class SyntheticEvent<out Element> {
/** The element that triggered the event */
val target: Element
/** The element that the event listener is attached to */
val currentTarget: Element
/** Whether the event bubbles up the DOM tree */
val bubbles: Boolean
/** Whether the event can be cancelled */
val cancelable: Boolean
/** Whether the default action has been prevented */
val defaultPrevented: Boolean
/** The event phase (capturing, at target, bubbling) */
val eventPhase: Short
/** Whether the event is trusted (generated by user action) */
val isTrusted: Boolean
/** Timestamp when the event was created */
val timeStamp: Double
/** Event type string */
val type: String
/**
* Prevent the default action associated with the event
*/
fun preventDefault()
/**
* Stop the event from bubbling up the DOM tree
*/
fun stopPropagation()
/**
* Stop the event from bubbling and prevent other listeners on the same element
*/
fun stopImmediatePropagation()
}
/**
* Interface for event listener registration scope
*/
interface EventsListenerScope<TElement> {
fun addEventListener(type: String, listener: (SyntheticEvent<TElement>) -> Unit)
}Comprehensive mouse event handling with detailed position and button information.
/**
* Mouse event with position and button details
*/
interface SyntheticMouseEvent : SyntheticEvent<Element> {
/** Horizontal coordinate relative to the viewport */
val clientX: Double
/** Vertical coordinate relative to the viewport */
val clientY: Double
/** Horizontal coordinate relative to the entire document */
val pageX: Double
/** Vertical coordinate relative to the entire document */
val pageY: Double
/** Horizontal coordinate relative to the target element */
val offsetX: Double
/** Vertical coordinate relative to the target element */
val offsetY: Double
/** Horizontal coordinate relative to the screen */
val screenX: Double
/** Vertical coordinate relative to the screen */
val screenY: Double
/** Mouse button that was pressed (0: left, 1: middle, 2: right) */
val button: Short
/** Bitmask of all pressed mouse buttons */
val buttons: Short
/** Whether Alt key was pressed */
val altKey: Boolean
/** Whether Ctrl key was pressed */
val ctrlKey: Boolean
/** Whether Meta key was pressed (Cmd on Mac, Windows key on PC) */
val metaKey: Boolean
/** Whether Shift key was pressed */
val shiftKey: Boolean
/** Related target element (for mouseover/mouseout events) */
val relatedTarget: Element?
}Mouse Event Handlers:
/**
* Click event (mouse button press and release)
*/
fun AttrsScope<*>.onClick(listener: (SyntheticMouseEvent) -> Unit)
/**
* Double-click event
*/
fun AttrsScope<*>.onDoubleClick(listener: (SyntheticMouseEvent) -> Unit)
/**
* Mouse button press
*/
fun AttrsScope<*>.onMouseDown(listener: (SyntheticMouseEvent) -> Unit)
/**
* Mouse button release
*/
fun AttrsScope<*>.onMouseUp(listener: (SyntheticMouseEvent) -> Unit)
/**
* Mouse enters element (does not bubble)
*/
fun AttrsScope<*>.onMouseEnter(listener: (SyntheticMouseEvent) -> Unit)
/**
* Mouse leaves element (does not bubble)
*/
fun AttrsScope<*>.onMouseLeave(listener: (SyntheticMouseEvent) -> Unit)
/**
* Mouse enters element or its children (bubbles)
*/
fun AttrsScope<*>.onMouseOver(listener: (SyntheticMouseEvent) -> Unit)
/**
* Mouse leaves element or its children (bubbles)
*/
fun AttrsScope<*>.onMouseOut(listener: (SyntheticMouseEvent) -> Unit)
/**
* Mouse moves over element
*/
fun AttrsScope<*>.onMouseMove(listener: (SyntheticMouseEvent) -> Unit)
/**
* Context menu event (right-click)
*/
fun AttrsScope<*>.onContextMenu(listener: (SyntheticMouseEvent) -> Unit)Usage Examples:
Button({
onClick { event ->
console.log("Button clicked at (${event.clientX}, ${event.clientY})")
if (event.ctrlKey) {
console.log("Ctrl+Click detected")
}
}
onMouseEnter { event ->
// Change visual state on hover
event.target.style.backgroundColor = "lightblue"
}
onMouseLeave { event ->
// Reset visual state
event.target.style.backgroundColor = ""
}
}) {
Text("Interactive Button")
}
Div({
onMouseMove { event ->
// Track mouse position
updateMousePosition(event.offsetX, event.offsetY)
}
onContextMenu { event ->
event.preventDefault() // Prevent default context menu
showCustomContextMenu(event.clientX, event.clientY)
}
}) {
Text("Mouse tracking area")
}Keyboard event handling with key identification and modifier key support.
/**
* Keyboard event with key and modifier information
*/
interface SyntheticKeyboardEvent : SyntheticEvent<Element> {
/** The key value (human-readable key name) */
val key: String
/** The physical key code */
val code: String
/** Legacy key code (deprecated but sometimes needed) */
val keyCode: Int
/** Whether Alt key was pressed */
val altKey: Boolean
/** Whether Ctrl key was pressed */
val ctrlKey: Boolean
/** Whether Meta key was pressed */
val metaKey: Boolean
/** Whether Shift key was pressed */
val shiftKey: Boolean
/** Whether the key is being held down (for keydown) */
val repeat: Boolean
/** Location of the key on the keyboard */
val location: Int
}Keyboard Event Handlers:
/**
* Key press down
*/
fun AttrsScope<*>.onKeyDown(listener: (SyntheticKeyboardEvent) -> Unit)
/**
* Key release
*/
fun AttrsScope<*>.onKeyUp(listener: (SyntheticKeyboardEvent) -> Unit)
/**
* Key press (deprecated, use keydown instead)
*/
fun AttrsScope<*>.onKeyPress(listener: (SyntheticKeyboardEvent) -> Unit)Usage Examples:
TextInput(
value = inputValue,
attrs = {
onKeyDown { event ->
when (event.key) {
"Enter" -> {
if (event.ctrlKey) {
// Ctrl+Enter: submit form
submitForm()
} else {
// Enter: new line
handleEnterKey()
}
}
"Escape" -> {
// Cancel operation
cancelEdit()
}
"Tab" -> {
if (event.shiftKey) {
// Shift+Tab: previous field
focusPreviousField()
}
// Tab is handled by browser
}
}
}
onKeyUp { event ->
// Handle key release if needed
if (event.key == "Control") {
hideShortcutHints()
}
}
}
)
// Global keyboard shortcuts
Div({
tabIndex(0) // Make focusable
onKeyDown { event ->
if (event.ctrlKey) {
when (event.key) {
"s" -> {
event.preventDefault()
saveDocument()
}
"z" -> {
event.preventDefault()
if (event.shiftKey) redo() else undo()
}
}
}
}
}) {
// Application content
}Focus and blur events for managing element focus states and accessibility.
/**
* Focus event
*/
interface SyntheticFocusEvent : SyntheticEvent<Element> {
/** Related target element (element losing/gaining focus) */
val relatedTarget: Element?
}Focus Event Handlers:
/**
* Element gains focus
*/
fun AttrsScope<*>.onFocus(listener: (SyntheticFocusEvent) -> Unit)
/**
* Element loses focus
*/
fun AttrsScope<*>.onBlur(listener: (SyntheticFocusEvent) -> Unit)
/**
* Element gains focus (bubbles)
*/
fun AttrsScope<*>.onFocusIn(listener: (SyntheticFocusEvent) -> Unit)
/**
* Element loses focus (bubbles)
*/
fun AttrsScope<*>.onFocusOut(listener: (SyntheticFocusEvent) -> Unit)Usage Examples:
TextInput(
value = inputValue,
attrs = {
onFocus { event ->
// Highlight field on focus
event.target.style.borderColor = "blue"
showFieldHelp()
}
onBlur { event ->
// Validate field on blur
event.target.style.borderColor = ""
validateField(inputValue)
hideFieldHelp()
}
}
)Events specific to form elements and form interactions.
/**
* Input event for real-time input changes
*/
interface SyntheticInputEvent : SyntheticEvent<Element> {
/** Input value for input elements */
val value: String
/** Input data for composition events */
val data: String?
/** Input type information */
val inputType: String
}
/**
* Change event for input value changes (on blur/commit)
*/
interface SyntheticChangeEvent : SyntheticEvent<Element> {
/** Changed value */
val value: String
}
/**
* Form submission event
*/
interface SyntheticSubmitEvent : SyntheticEvent<HTMLFormElement>
/**
* Form reset event
*/
interface SyntheticResetEvent : SyntheticEvent<HTMLFormElement>Form Event Handlers:
/**
* Input value changes (real-time)
*/
fun AttrsScope<HTMLInputElement>.onInput(listener: (SyntheticInputEvent) -> Unit)
fun AttrsScope<HTMLTextAreaElement>.onInput(listener: (SyntheticInputEvent) -> Unit)
/**
* Input value committed (on blur or enter)
*/
fun AttrsScope<HTMLInputElement>.onChange(listener: (SyntheticChangeEvent) -> Unit)
fun AttrsScope<HTMLSelectElement>.onChange(listener: (SyntheticChangeEvent) -> Unit)
/**
* Form submission
*/
fun AttrsScope<HTMLFormElement>.onSubmit(listener: (SyntheticSubmitEvent) -> Unit)
/**
* Form reset
*/
fun AttrsScope<HTMLFormElement>.onReset(listener: (SyntheticResetEvent) -> Unit)
/**
* Invalid input (validation failure)
*/
fun AttrsScope<HTMLInputElement>.onInvalid(listener: (SyntheticEvent<HTMLInputElement>) -> Unit)Usage Examples:
Form({
onSubmit { event ->
event.preventDefault() // Prevent default form submission
if (validateForm()) {
submitFormData()
}
}
onReset { event ->
resetFormState()
}
}) {
TextInput(
value = emailValue,
attrs = {
type(InputType.Email)
required()
onInput { event ->
// Real-time validation
emailValue = event.value
validateEmail(event.value)
}
onChange { event ->
// Final validation on commit
if (event.value.isNotEmpty()) {
checkEmailAvailability(event.value)
}
}
onInvalid { event ->
// Handle validation failure
showValidationError("Please enter a valid email")
}
}
)
}Touch event handling for mobile and touch-enabled devices.
/**
* Touch event with touch point information
*/
interface SyntheticTouchEvent : SyntheticEvent<Element> {
/** List of all touch points */
val touches: TouchList
/** List of touch points that changed */
val changedTouches: TouchList
/** List of touch points on the current target */
val targetTouches: TouchList
/** Whether Alt key was pressed */
val altKey: Boolean
/** Whether Ctrl key was pressed */
val ctrlKey: Boolean
/** Whether Meta key was pressed */
val metaKey: Boolean
/** Whether Shift key was pressed */
val shiftKey: Boolean
}
/**
* Individual touch point
*/
interface Touch {
/** Unique identifier for the touch */
val identifier: Int
/** Target element */
val target: Element
/** Touch coordinates */
val clientX: Double
val clientY: Double
val pageX: Double
val pageY: Double
val screenX: Double
val screenY: Double
/** Touch area */
val radiusX: Double
val radiusY: Double
/** Touch pressure */
val force: Double
}Touch Event Handlers:
/**
* Touch starts
*/
fun AttrsScope<*>.onTouchStart(listener: (SyntheticTouchEvent) -> Unit)
/**
* Touch moves
*/
fun AttrsScope<*>.onTouchMove(listener: (SyntheticTouchEvent) -> Unit)
/**
* Touch ends
*/
fun AttrsScope<*>.onTouchEnd(listener: (SyntheticTouchEvent) -> Unit)
/**
* Touch cancelled
*/
fun AttrsScope<*>.onTouchCancel(listener: (SyntheticTouchEvent) -> Unit)Clipboard operation events for copy, cut, and paste operations.
/**
* Clipboard event
*/
interface SyntheticClipboardEvent : SyntheticEvent<Element> {
/** Clipboard data */
val clipboardData: DataTransfer?
}Clipboard Event Handlers:
/**
* Copy operation
*/
fun AttrsScope<*>.onCopy(listener: (SyntheticClipboardEvent) -> Unit)
/**
* Cut operation
*/
fun AttrsScope<*>.onCut(listener: (SyntheticClipboardEvent) -> Unit)
/**
* Paste operation
*/
fun AttrsScope<*>.onPaste(listener: (SyntheticClipboardEvent) -> Unit)CSS animation and transition events.
/**
* CSS animation event
*/
interface SyntheticAnimationEvent : SyntheticEvent<Element> {
/** Animation name */
val animationName: String
/** Elapsed time */
val elapsedTime: Double
/** Pseudo element */
val pseudoElement: String
}
/**
* CSS transition event
*/
interface SyntheticTransitionEvent : SyntheticEvent<Element> {
/** Property name that transitioned */
val propertyName: String
/** Elapsed time */
val elapsedTime: Double
/** Pseudo element */
val pseudoElement: String
}Animation Event Handlers:
/**
* Animation starts
*/
fun AttrsScope<*>.onAnimationStart(listener: (SyntheticAnimationEvent) -> Unit)
/**
* Animation ends
*/
fun AttrsScope<*>.onAnimationEnd(listener: (SyntheticAnimationEvent) -> Unit)
/**
* Animation iteration
*/
fun AttrsScope<*>.onAnimationIteration(listener: (SyntheticAnimationEvent) -> Unit)
/**
* Transition ends
*/
fun AttrsScope<*>.onTransitionEnd(listener: (SyntheticTransitionEvent) -> Unit)Events for audio and video elements.
/**
* Media is ready to play
*/
fun AttrsScope<HTMLAudioElement>.onCanPlay(listener: (SyntheticEvent<HTMLAudioElement>) -> Unit)
fun AttrsScope<HTMLVideoElement>.onCanPlay(listener: (SyntheticEvent<HTMLVideoElement>) -> Unit)
/**
* Media starts playing
*/
fun AttrsScope<HTMLAudioElement>.onPlay(listener: (SyntheticEvent<HTMLAudioElement>) -> Unit)
fun AttrsScope<HTMLVideoElement>.onPlay(listener: (SyntheticEvent<HTMLVideoElement>) -> Unit)
/**
* Media pauses
*/
fun AttrsScope<HTMLAudioElement>.onPause(listener: (SyntheticEvent<HTMLAudioElement>) -> Unit)
fun AttrsScope<HTMLVideoElement>.onPause(listener: (SyntheticEvent<HTMLVideoElement>) -> Unit)
/**
* Media ends
*/
fun AttrsScope<HTMLAudioElement>.onEnded(listener: (SyntheticEvent<HTMLAudioElement>) -> Unit)
fun AttrsScope<HTMLVideoElement>.onEnded(listener: (SyntheticEvent<HTMLVideoElement>) -> Unit)
/**
* Volume changes
*/
fun AttrsScope<HTMLAudioElement>.onVolumeChange(listener: (SyntheticEvent<HTMLAudioElement>) -> Unit)
fun AttrsScope<HTMLVideoElement>.onVolumeChange(listener: (SyntheticEvent<HTMLVideoElement>) -> Unit)Global events for window and document-level interactions.
/**
* Window/element resize
*/
fun AttrsScope<*>.onResize(listener: (SyntheticEvent<Element>) -> Unit)
/**
* Scroll event
*/
fun AttrsScope<*>.onScroll(listener: (SyntheticEvent<Element>) -> Unit)
/**
* Content loaded
*/
fun AttrsScope<*>.onLoad(listener: (SyntheticEvent<Element>) -> Unit)
/**
* Loading error
*/
fun AttrsScope<*>.onError(listener: (SyntheticEvent<Element>) -> Unit)
/**
* Before page unload
*/
fun AttrsScope<*>.onBeforeUnload(listener: (SyntheticEvent<Element>) -> Unit)
/**
* Page unload
*/
fun AttrsScope<*>.onUnload(listener: (SyntheticEvent<Element>) -> Unit)interface TouchList {
val length: Int
fun item(index: Int): Touch?
}
interface DataTransfer {
var dropEffect: String
var effectAllowed: String
val files: FileList
val items: DataTransferItemList
val types: Array<String>
fun clearData(format: String? = null)
fun getData(format: String): String
fun setData(format: String, data: String)
fun setDragImage(img: Element, xOffset: Int, yOffset: Int)
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-foundation--foundation-wasm-js