Events
Ripl provides a full event system modeled after the browser DOM. Elements can listen for and emit events, events bubble up through the element hierarchy, and propagation can be stopped — all familiar patterns for web developers.
EventBus
Every element in Ripl extends EventBus, which provides the core event subscription and emission API.
on(event, handler, options?)
Subscribe to an event. Returns an unsubscribe function:
const off = circle.on('click', (event) => {
console.log('Clicked!', event.data);
});
// Later, unsubscribe
off();once(event, handler)
Subscribe to an event that fires only once:
circle.once('click', (event) => {
console.log('First click only');
});off(event?, handler?)
Remove event handlers. With no arguments, removes all handlers. With just an event name, removes all handlers for that event:
circle.off('click', myHandler); // Remove specific handler
circle.off('click'); // Remove all click handlers
circle.off(); // Remove all handlersemit(event, data?)
Emit an event. The event bubbles up to parent elements by default:
circle.emit('custom-event', { value: 42 });Event Object
Event handlers receive an Event object with:
| Property | Type | Description |
|---|---|---|
type | string | The event name |
data | any | Event payload data |
source | EventBus | The element that originally emitted the event |
currentTarget | EventBus | The element currently handling the event |
stopPropagation()
Prevent the event from bubbling further up the tree:
circle.on('click', (event) => {
event.stopPropagation();
// Parent group's click handler will NOT fire
});Pointer Events
When elements are inside a Scene, the scene automatically delegates DOM pointer events to the correct elements based on hit testing.
Tracked Events
| Event | Description |
|---|---|
click | Element was clicked |
mouseenter | Pointer entered the element's bounds |
mouseleave | Pointer left the element's bounds |
mousemove | Pointer moved within the element's bounds |
const scene = createScene('.container', {
children: [circle],
});
scene.render();
circle.on('mouseenter', () => {
circle.fillStyle = '#ff006e';
scene.render();
});
circle.on('mouseleave', () => {
circle.fillStyle = '#3a86ff';
scene.render();
});IMPORTANT
Pointer events only work when elements are inside a Scene. The scene handles DOM event listening and hit testing.
Event Bubbling
Events bubble up through the element hierarchy, just like the DOM. If a circle inside a group emits a click event, the group will also receive it:
const circle = createCircle({ cx: 100,
cy: 100,
radius: 50 });
const group = createGroup({ children: [circle] });
// This fires when the circle (or any child) is clicked
group.on('click', (event) => {
console.log('Group received click from:', event.source.type);
});Self Option
Use the self option to only handle events that originated from the element itself (not from children):
group.on('click', (event) => {
console.log('Only fires for direct group clicks');
}, { self: true });Custom Events
You can emit and listen for any custom event name:
circle.on('highlight', (event) => {
circle.fillStyle = event.data.color;
});
circle.emit('highlight', { color: '#ff006e' });The pointerEvents Property
The pointerEvents property on elements controls hit testing behavior:
| Value | Description |
|---|---|
'all' | Responds to hits on fill and stroke (default) |
'fill' | Only responds to hits on the fill area |
'stroke' | Only responds to hits on the stroke area |
'none' | Element is invisible to pointer events |
const overlay = createRect({
pointerEvents: 'none', // Click passes through to elements below
fillStyle: 'rgba(0, 0, 0, 0.3)',
x: 0,
y: 0,
width: 400,
height: 300,
});Demo
Hover over and click the elements to see events in action. Events bubble from child elements to the parent group.