Advanced
AJAX Persistence
Configure the api option to load, save, and delete annotations via HTTP
endpoints. Each field accepts a URL string (default fetch behavior) or a custom function
for full control over the request.
URL-based configuration
AnnotateImage.annotate(img, {
editable: true,
api: {
load: '/api/annotations/load',
save: '/api/annotations/save',
delete: '/api/annotations/delete'
},
onError: function(ctx) {
console.error('Failed to ' + ctx.type + ':', ctx.error.message);
}
});
When URLs are provided, the library uses the following HTTP behavior:
load— sends aGETrequest and expects anAnnotationNote[]JSON responsesave— sends aPOSTwith a JSON body and expects a{ annotation_id?: string }responsedelete— sends aPOSTwith a JSON body
Function-based configuration
For full control over the request lifecycle, provide functions instead of URLs. Each
function must return a Promise.
AnnotateImage.annotate(img, {
editable: true,
api: {
load: function() {
return fetch('/my-api/notes')
.then(function(r) { return r.json(); });
},
save: function(note) {
return fetch('/my-api/notes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(note)
}).then(function(r) { return r.json(); });
},
delete: function(note) {
return fetch('/my-api/notes/' + note.id, {
method: 'DELETE'
}).then(function() {});
}
}
});
Custom Labels
Override the default UI button text for internationalization or to use icon-only controls. Any field you omit keeps its default value.
Default labels
| Field | Default |
|---|---|
addNote |
"Add Note" |
save |
"OK" |
delete |
"Delete" |
cancel |
"Cancel" |
placeholder |
"" |
German example
AnnotateImage.annotate(img, {
editable: true,
notes: [],
labels: {
addNote: 'Notiz hinzufügen',
save: 'Speichern',
delete: 'Löschen',
cancel: 'Abbrechen',
placeholder: 'Notiz eingeben...'
}
});
Icon-only example
AnnotateImage.annotate(img, {
editable: true,
notes: [],
labels: {
addNote: '',
save: '',
delete: '',
cancel: ''
}
});
Live demo
Default (English)
German
Icon-only
Error Handling
The onError callback receives an AnnotateErrorContext object
whenever an API operation fails. If no callback is provided, errors are logged to the
console.
interface AnnotateErrorContext {
type: 'load' | 'save' | 'delete';
error: Error;
note?: AnnotationNote;
}
AnnotateImage.annotate(img, {
editable: true,
notes: [],
api: { load: '/api/notes' },
onError: function(ctx) {
if (ctx.type === 'load') {
alert('Failed to load annotations: ' + ctx.error.message);
} else {
console.error(ctx.type + ' failed:', ctx.error);
if (ctx.note) {
console.error('Affected note:', ctx.note.id);
}
}
}
});
Accessibility
The library includes built-in keyboard navigation support for all interactive elements.
| Key | Action |
|---|---|
| Tab | Navigate between annotation areas and controls |
| Enter / Space | Activate buttons and open editable annotations |
| Escape | Cancel editing |
Additional accessibility features:
- Annotation areas and buttons have
role="button"andtabindexattributes - Focus is managed automatically when entering and exiting edit mode
- Visible focus styles are applied to all interactive elements
- Focus trapping is enforced during annotation editing to prevent tabbing out of the editor
Editability
Editability is controlled at two levels, giving fine-grained control over what users can modify.
-
Global
editableoption — controls whether the “Add Note” button appears and whether new annotations can be created. -
Per-note
editablefield — controls whether individual existing notes can be clicked to edit.
AnnotateImage.annotate(img, {
// Global: allow adding new notes
editable: true,
notes: [
// This note CAN be edited by clicking
{ id: "1", top: 50, left: 50, width: 100, height: 100,
text: "Editable note", editable: true },
// This note CANNOT be edited (read-only)
{ id: "2", top: 200, left: 200, width: 100, height: 100,
text: "Read-only note", editable: false }
]
});
Use the setEditable() instance method to toggle editing at runtime:
// Disable editing at runtime
instance.setEditable(false);
// Re-enable
instance.setEditable(true);