This document describes the custom DOM events fired by the Typograph canvas engine in response to page-element actions (selecting an element, editing its text, etc.). It is intended for developers who embed the Typograph editor and need to react to element-level activity — for example to drive a custom properties panel, sync selection with an external UI, or track unsaved changes.
For page-structure events (add / delete / duplicate / reorder pages) see
PageEvents.md. For "the document's content changed", which is what an
auto-save or a dirty-state indicator wants, see DocumentEvents.md.
Source of truth: the event names live in the canvas package at
src/typograph_events.ts. This document reflects what the engine actually dispatches today — a number of element event names are declared but not yet fired (see Reserved / not-yet-fired).
All events are dispatched on document. Element events fired through
sendCustomEvent are CustomEvent objects whose payload is on event.detail;
events fired through sendEvent are plain Event objects with no payload.
document.addEventListener('typograph_pageelement_selected', (event) => {
const { uuid } = event.detail;
console.log(`Element ${uuid} was selected`);
});
Nothing is throttled by default. Throttling is opt-in per event type, and no type opts in (TYP-592). Assume one event per user action.
This previously said that every CustomEvent passed through a shared 5-per-second throttle. That
was true and it was the bug: the throttle only ever dropped discrete events, so two elements
selected within 200ms produced one typograph_pageelement_selected and left the listener pointing
at the wrong element, and a dropped typograph_textInput — which carries the inserted text, not
the whole string — lost characters outright.
set_custom_event_throttle(max) still sets the rate, for whichever types are opted in.
document.addEventListener('typograph_editor_ready', () => {
typograph.set_custom_event_throttle(10); // max 10 per type per second
});
Plain Event-based events (typograph_edit_done) are not throttled.
typograph_pageelement_hover is also not throttled — it is rate-limited by
its own change-detection instead (see its entry below).
typograph_pageelement_selectedFired when an element becomes selected — specifically on the transition from
not selected to selected (PageElement.set_selected(true)). Re-selecting an
already-selected element does not re-fire.
| Property | Type | Description |
|---|---|---|
message |
string |
Always 'selected' |
uuid |
string |
UUID of the element that was selected |
document.addEventListener('typograph_pageelement_selected', (e) => {
const { uuid } = e.detail;
});
There is currently no matching deselect event (see below), and the throttle can drop a selection if several happen within the same 200 ms window. If you need reliable current-selection state, read it from the document/page API on receipt rather than treating the event as an exact 1:1 signal.
typograph_pageelement_hoverFired when the element under the cursor changes — the mouse enters a different element, or leaves all elements. Use it to drive a hover highlight or a context-sensitive UI.
It fires once per boundary crossing: it does not fire repeatedly while
the mouse keeps moving inside the same element. When the cursor leaves all
elements, it fires once with uuid: null.
| Property | Type | Description |
|---|---|---|
uuid |
string | null |
UUID of the element now under the cursor, or null when the cursor is over no element |
document.addEventListener('typograph_pageelement_hover', (e) => {
const { uuid } = e.detail;
if (uuid) highlightElement(uuid);
else clearHighlight();
});
Unlike the other custom events,
typograph_pageelement_hoveris not throttled — the change-detection already limits it to one event per element-boundary crossing, and each event is meaningful (throttling could drop the final transition and leave your highlight stale).
typograph_textInputFired while the user types into a rich-text element (PageElementRichText).
| Property | Type | Description |
|---|---|---|
text |
string |
The text that was input |
uuid |
string |
UUID of the rich-text element being edited |
document.addEventListener('typograph_textInput', (e) => {
const { text, uuid } = e.detail;
});
Throttled to 5/sec by default, so this is not a per-keystroke event and should not be used to reconstruct the typed string. Treat it as a "text in this element changed" hint and re-read the element's content when you need it.
typograph_edit_doneFired when a rich-text element loses focus (the editor's
setFocus(false)), i.e. the user finished editing text and clicked away.
This is a plain Event — there is no detail payload, so it does not tell
you which element was being edited. Read the current selection/active element
from the document API if you need that.
document.addEventListener('typograph_edit_done', () => {
// user stopped editing text somewhere — re-read state / mark dirty
});
The following element event names are declared in typograph_events.ts but
are not dispatched anywhere in the current canvas or editor-ui code. Do not
rely on them yet — a listener will simply never be called until the engine is
updated to fire them.
| Constant | String value | Intended meaning |
|---|---|---|
typograph_pageelement_created |
typograph_pageelement_created |
An element was created |
typograph_pageelement_deleted |
typograph_pageelement_deleted |
An element was deleted |
Both are still undispatched because the engine does not yet distinguish a user creating an element from one appearing for another reason — opening a document creates every element, undo recreates them, duplicating a spread creates a dozen. Firing on all of that is noise a listener cannot use, so the distinction has to come first.
Two corrections to earlier versions of this list, so nothing is inferred from a stale copy:
typograph_pageelement_deselected does fire, and has since TYP-592. It is
documented in the table above.typograph_text_edit_start / _change / _stop no longer exist. They were
declared but never dispatched and were deleted in TYP-592. typograph_edit_done
covers "text editing finished" and typograph_textInput covers "the text
changed".There is still no per-element "changed" event: geometry changes (move, resize,
rotate) and style changes (fill, stroke, colour, opacity, shadow, …) do not report
which element changed. If you only need to know that the document changed at all
— for an auto-save or a modified indicator — use typograph_document_changed
(DocumentEvents.md), which covers those edits. Polling
element state is no longer the recommended approach for that case.
| Event | Fired? | Trigger | detail |
|---|---|---|---|
typograph_pageelement_selected |
✅ | Element becomes selected (unselected → selected) | { message: 'selected', uuid } |
typograph_pageelement_deselected |
✅ | Element becomes deselected (selected → unselected) | { message: 'deselected', uuid } |
typograph_pageelement_hover |
✅ (unthrottled) | Hovered element changes (enter/leave) | { uuid: string | null } |
typograph_textInput |
✅ | Typing into a rich-text element | { text, uuid } |
typograph_edit_done |
✅ (plain Event) |
Rich-text element loses focus | (none) |
typograph_document_changed |
✅ | Document content changed — see DocumentEvents.md |
{ reason } |
typograph_pageelement_created |
❌ reserved | Element created | — |
typograph_pageelement_deleted |
❌ reserved | Element deleted | — |