Skip to content

Events

Listen to events with an option when creating the instance, or with on() later. Both can be used together.

const penciled = annotate('#article', {
onCreate: (annotation) => console.log('created', annotation),
});
const off = penciled.on('update', (annotation) => console.log('updated', annotation));

Listeners receive copies of annotations. A listener that throws is logged and doesn’t affect other listeners or the instance.

Events fire for changes made by readers and by your code alike, including import() and clear().

(annotation: Annotation) => void

An annotation was created, by a reader or with create(). import() emits it for each annotation that wasn’t there before.

(annotation: Annotation) => void

An annotation’s color, note, editable flag or meta changed. import() emits it for each annotation it replaces.

(annotation: Annotation) => void

An annotation was removed. Receives the annotation as it was.

(annotation: Annotation) => void

An annotation’s text can no longer be found with confidence, so it is no longer highlighted. It stays in getAll() and in storage. This fires once each time an annotation goes from anchored (or newly loaded) to orphaned. See Orphans.

(annotations: Annotation[]) => void

Stored annotations were loaded and anchored. Fires once, after onOrphan has fired for any that couldn’t be anchored. At this point ready resolves too.

(error: unknown) => void

Loading or saving failed. The annotations in memory, and the UI, are unaffected. Without a listener, errors are logged with console.error.

annotate('#article', {
storage: myApi,
onError: (error) => toast.error('Your highlights could not be saved'),
});
(context: SelectContext | null) => void

The reader selected text in the container, or opened an annotation by clicking it, by keyboard or through focus(). It fires with null when that is dismissed. This is what the default toolbar listens to, and what you use to build your own UI with penciled/headless.

interface SelectContext {
text: string; // the selected or annotated text
range: Range; // where it is, for positioning UI
annotation?: Annotation; // set when an existing annotation was opened
editable: boolean; // false with `readOnly` or on an annotation with `editable: false`
}

With readOnly, selecting text that isn’t an annotation counts as no selection. Selecting exactly the text of an existing annotation opens that annotation.