Skip to content

Commit f17a2ef

Browse files
[Word] (annotations) Update article (#5149)
* [Word] (annotations) Update article * Apply suggestions from code review Co-authored-by: Linda Cannon <[email protected]> * Add code comments based on feedback --------- Co-authored-by: Linda Cannon <[email protected]>
1 parent ae38db8 commit f17a2ef

File tree

3 files changed

+371
-115
lines changed

3 files changed

+371
-115
lines changed

docs/toc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,9 @@ items:
983983
- name: Read and write data to the active selection
984984
href: develop/read-and-write-data-to-the-active-selection-in-a-document-or-spreadsheet.md
985985
displayName: Word
986+
- name: Use annotations
987+
href: word/annotations.md
988+
displayName: Word
986989
- name: Use fields
987990
href: word/fields-guidance.md
988991
displayName: Word

docs/word/annotations.md

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
---
2+
title: Use annotations in your Word add-in
3+
description: Learn to use annotations in your Word add-in.
4+
ms.date: 04/29/2025
5+
ms.localizationpriority: medium
6+
ms.topic: how-to
7+
---
8+
9+
# Use annotations in your Word add-in
10+
11+
**Includes community contributions from:** [Abdulhadi Jarad](https://github.com/abdulhadi-jarad)
12+
13+
You can use annotations to provide feedback about grammar or other aspects of content in a Word document. The user may see colorful underlining that indicates there's an issue or other information. If the user hovers over the affected content, a popup dialog is displayed that shows them what the issue is and possible actions they can take.
14+
15+
APIs for working with annotations were introduced in the [PowerPointApi 1.7 requirement set](/javascript/api/requirement-sets/word/word-api-1-7-requirement-set) and expanded in the [PowerPointApi 1.8 requirement set](/javascript/api/requirement-sets/word/word-api-1-8-requirement-set) as part of supporting writing assistance scenarios like checking spelling and grammar or providing suggestions to improve writing.
16+
17+
In this article, we show how your add-in can insert feedback and critiques using annotations in a document and allow the user to react to them.
18+
19+
> [!IMPORTANT]
20+
> These annotations aren't persisted in the document. This means that when the document is reopened, the annotations need to be regenerated. However, if the user accepts suggested changes, the changes will persist as long as the user saves them before closing the document.
21+
22+
## Prerequisites
23+
24+
The annotation APIs rely on a service that requires a Microsoft 365 subscription. As such, using this feature in Word with a one-time purchase license won't work. The user must be running Word connected to a Microsoft 365 subscription so that your add-in can successfully run the annotation APIs.
25+
26+
## Key annotation APIs
27+
28+
The following are the key annotation APIs.
29+
30+
- [Paragraph.insertAnnotations](/javascript/api/word/word.paragraph#word-word-paragraph-insertannotations-member(1))
31+
- [Paragraph.getAnnotations](/javascript/api/word/word.paragraph#word-word-paragraph-getannotations-member(1))
32+
- Objects:
33+
- [Annotation](/javascript/api/word/word.annotation): Represents an annotation.
34+
- [AnnotationCollection](/javascript/api/word/word.annotationcollection): Represents the collection of annotations.
35+
- [AnnotationSet](/javascript/api/word/word.annotationset): Represents the set of annotations produced by your add-in in this session.
36+
- [CritiqueAnnotation](/javascript/api/word/word.critiqueannotation): Represents the critique type of annotation.
37+
- [Critique](/javascript/api/word/word.critique): Represents feedback about an affected area of a paragraph, indicated by a colored underline.
38+
- Annotation events on [Document](/javascript/api/word/word.document):
39+
- [onAnnotationClicked](/javascript/api/word/word.document#word-word-document-onannotationclicked-member)
40+
- [onAnnotationHovered](/javascript/api/word/word.document#word-word-document-onannotationhovered-member)
41+
- [onAnnotationInserted](/javascript/api/word/word.document#word-word-document-onannotationinserted-member)
42+
- [onAnnotationPopupAction](/javascript/api/word/word.document#word-word-document-onannotationpopupaction-member)
43+
- [onAnnotationRemoved](/javascript/api/word/word.document#word-word-document-onannotationremoved-member)
44+
45+
## Use annotation APIs
46+
47+
The following sections show how to work with annotation APIs. These examples are based on the [Manage annotations](https://github.com/OfficeDev/office-js-snippets/blob/prod/samples/word/50-document/manage-annotations.yaml) code sample.
48+
49+
Your add-in code should use the feedback or critique results from your service run against the user's content in the document to manage annotations more dynamically.
50+
51+
## Register annotation events
52+
53+
The following code shows how to register event handlers. To learn more about working with events in Word, see [Work with events using the Word JavaScript API](word-add-ins-events.md). For examples of annotation event handlers, see the following sections.
54+
55+
```typescript
56+
let eventContexts = [];
57+
58+
async function registerEventHandlers() {
59+
// Registers event handlers.
60+
await Word.run(async (context) => {
61+
eventContexts[0] = context.document.onParagraphAdded.add(paragraphChanged);
62+
eventContexts[1] = context.document.onParagraphChanged.add(paragraphChanged);
63+
64+
eventContexts[2] = context.document.onAnnotationClicked.add(onClickedHandler);
65+
eventContexts[3] = context.document.onAnnotationHovered.add(onHoveredHandler);
66+
eventContexts[4] = context.document.onAnnotationInserted.add(onInsertedHandler);
67+
eventContexts[5] = context.document.onAnnotationRemoved.add(onRemovedHandler);
68+
eventContexts[6] = context.document.onAnnotationPopupAction.add(onPopupActionHandler);
69+
70+
await context.sync();
71+
72+
console.log("Event handlers registered.");
73+
});
74+
}
75+
```
76+
77+
### onClickedHandler event handler
78+
79+
The following code runs when the registered `onAnnotationClicked` event occurs.
80+
81+
```typescript
82+
async function onClickedHandler(args: Word.AnnotationClickedEventArgs) {
83+
// Runs when the registered Document.onAnnotationClicked event occurs.
84+
await Word.run(async (context) => {
85+
const annotation: Word.Annotation = context.document.getAnnotationById(args.id);
86+
annotation.load("critiqueAnnotation");
87+
88+
await context.sync();
89+
90+
console.log(`AnnotationClicked: ID ${args.id}:`, annotation.critiqueAnnotation.critique);
91+
});
92+
}
93+
```
94+
95+
### onHoveredHandler event handler
96+
97+
The following code runs when the registered `onAnnotationHovered` event occurs.
98+
99+
```typescript
100+
async function onHoveredHandler(args: Word.AnnotationHoveredEventArgs) {
101+
// Runs when the registered Document.onAnnotationHovered event occurs.
102+
await Word.run(async (context) => {
103+
const annotation: Word.Annotation = context.document.getAnnotationById(args.id);
104+
annotation.load("critiqueAnnotation");
105+
106+
await context.sync();
107+
108+
console.log(`AnnotationHovered: ID ${args.id}:`, annotation.critiqueAnnotation.critique);
109+
});
110+
}
111+
```
112+
113+
### onInsertedHandler event handler
114+
115+
The following code runs when the registered `onAnnotationInserted` event occurs.
116+
117+
```typescript
118+
async function onInsertedHandler(args: Word.AnnotationInsertedEventArgs) {
119+
// Runs when the registered Document.onAnnotationInserted event occurs.
120+
await Word.run(async (context) => {
121+
const annotations = [];
122+
for (let i = 0; i < args.ids.length; i++) {
123+
let annotation: Word.Annotation = context.document.getAnnotationById(args.ids[i]);
124+
annotation.load("id,critiqueAnnotation");
125+
annotations.push(annotation);
126+
}
127+
128+
await context.sync();
129+
130+
for (let annotation of annotations) {
131+
console.log(`AnnotationInserted: ID ${annotation.id}:`, annotation.critiqueAnnotation.critique);
132+
}
133+
});
134+
}
135+
```
136+
137+
### onRemovedHandler event handler
138+
139+
The following code runs when the registered `onAnnotationRemoved` event occurs.
140+
141+
```typescript
142+
async function onRemovedHandler(args: Word.AnnotationRemovedEventArgs) {
143+
// Runs when the registered Document.onAnnotationRemoved event occurs.
144+
await Word.run(async (context) => {
145+
for (let id of args.ids) {
146+
console.log(`AnnotationRemoved: ID ${id}`);
147+
}
148+
});
149+
}
150+
```
151+
152+
### onPopupActionHandler event handler
153+
154+
The following code runs when the registered `onAnnotationPopupAction` event occurs.
155+
156+
```typescript
157+
async function onPopupActionHandler(args: Word.AnnotationPopupActionEventArgs) {
158+
// Runs when the registered Document.onAnnotationPopupAction event occurs.
159+
await Word.run(async (context) => {
160+
let message = `AnnotationPopupAction: ID ${args.id} = `;
161+
if (args.action === "Accept") {
162+
message += `Accepted: ${args.critiqueSuggestion}`;
163+
} else {
164+
message += "Rejected";
165+
}
166+
167+
console.log(message);
168+
});
169+
}
170+
```
171+
172+
## Insert annotations
173+
174+
The following code shows how to insert annotations into the selected paragraph.
175+
176+
```typescript
177+
async function insertAnnotations() {
178+
// Adds annotations to the selected paragraph.
179+
await Word.run(async (context) => {
180+
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
181+
const options: Word.CritiquePopupOptions = {
182+
brandingTextResourceId: "PG.TabLabel",
183+
subtitleResourceId: "PG.HelpCommand.TipTitle",
184+
titleResourceId: "PG.HelpCommand.Label",
185+
suggestions: ["suggestion 1", "suggestion 2", "suggestion 3"]
186+
};
187+
const critique1: Word.Critique = {
188+
colorScheme: Word.CritiqueColorScheme.red,
189+
start: 1,
190+
length: 3,
191+
popupOptions: options
192+
};
193+
const critique2: Word.Critique = {
194+
colorScheme: Word.CritiqueColorScheme.green,
195+
start: 6,
196+
length: 1,
197+
popupOptions: options
198+
};
199+
const critique3: Word.Critique = {
200+
colorScheme: Word.CritiqueColorScheme.blue,
201+
start: 10,
202+
length: 3,
203+
popupOptions: options
204+
};
205+
const critique4: Word.Critique = {
206+
colorScheme: Word.CritiqueColorScheme.lavender,
207+
start: 14,
208+
length: 3,
209+
popupOptions: options
210+
};
211+
const critique5: Word.Critique = {
212+
colorScheme: Word.CritiqueColorScheme.berry,
213+
start: 18,
214+
length: 10,
215+
popupOptions: options
216+
};
217+
const annotationSet: Word.AnnotationSet = {
218+
critiques: [critique1, critique2, critique3, critique4, critique5]
219+
};
220+
221+
const annotationIds = paragraph.insertAnnotations(annotationSet);
222+
223+
await context.sync();
224+
225+
console.log("Annotations inserted:", annotationIds.value);
226+
});
227+
}
228+
```
229+
230+
## Get annotations
231+
232+
The following code shows how to get annotations from the selected paragraph.
233+
234+
```typescript
235+
async function getAnnotations() {
236+
// Gets annotations found in the selected paragraph.
237+
await Word.run(async (context) => {
238+
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
239+
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
240+
annotations.load("id,state,critiqueAnnotation");
241+
242+
await context.sync();
243+
244+
console.log("Annotations found:");
245+
246+
for (let i = 0; i < annotations.items.length; i++) {
247+
const annotation: Word.Annotation = annotations.items[i];
248+
249+
console.log(`ID ${annotation.id} - state '${annotation.state}':`, annotation.critiqueAnnotation.critique);
250+
}
251+
});
252+
}
253+
```
254+
255+
## Accept an annotation
256+
257+
The following code shows how to accept the first annotation found in the selected paragraph.
258+
259+
```typescript
260+
async function acceptFirst() {
261+
// Accepts the first annotation found in the selected paragraph.
262+
await Word.run(async (context) => {
263+
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
264+
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
265+
annotations.load("id,state,critiqueAnnotation");
266+
267+
await context.sync();
268+
269+
for (let i = 0; i < annotations.items.length; i++) {
270+
const annotation: Word.Annotation = annotations.items[i];
271+
272+
if (annotation.state === Word.AnnotationState.created) {
273+
console.log(`Accepting ID ${annotation.id}...`);
274+
annotation.critiqueAnnotation.accept();
275+
276+
await context.sync();
277+
break;
278+
}
279+
}
280+
});
281+
}
282+
```
283+
284+
## Reject an annotation
285+
286+
The following code shows how to reject the last annotation found in the selected paragraph.
287+
288+
```typescript
289+
async function rejectLast() {
290+
// Rejects the last annotation found in the selected paragraph.
291+
await Word.run(async (context) => {
292+
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
293+
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
294+
annotations.load("id,state,critiqueAnnotation");
295+
296+
await context.sync();
297+
298+
for (let i = annotations.items.length - 1; i >= 0; i--) {
299+
const annotation: Word.Annotation = annotations.items[i];
300+
301+
if (annotation.state === Word.AnnotationState.created) {
302+
console.log(`Rejecting ID ${annotation.id}...`);
303+
annotation.critiqueAnnotation.reject();
304+
305+
await context.sync();
306+
break;
307+
}
308+
}
309+
});
310+
}
311+
```
312+
313+
## Delete annotations
314+
315+
The following code shows how to delete all the annotations found in the selected paragraph.
316+
317+
```typescript
318+
async function deleteAnnotations() {
319+
// Deletes all annotations found in the selected paragraph.
320+
await Word.run(async (context) => {
321+
const paragraph: Word.Paragraph = context.document.getSelection().paragraphs.getFirst();
322+
const annotations: Word.AnnotationCollection = paragraph.getAnnotations();
323+
annotations.load("id");
324+
325+
await context.sync();
326+
327+
const ids = [];
328+
for (let i = 0; i < annotations.items.length; i++) {
329+
const annotation: Word.Annotation = annotations.items[i];
330+
331+
ids.push(annotation.id);
332+
annotation.delete();
333+
}
334+
335+
await context.sync();
336+
337+
console.log("Annotations deleted:", ids);
338+
});
339+
}
340+
```
341+
342+
## Deregister annotation events
343+
344+
The following code shows how to deregister the event handlers using their event contexts you tracked in the `eventContext` variable.
345+
346+
```typescript
347+
async function deregisterEventHandlers() {
348+
// Deregisters event handlers.
349+
await Word.run(async (context) => {
350+
for (let i = 0; i < eventContexts.length; i++) {
351+
await Word.run(eventContexts[i].context, async (context) => {
352+
eventContexts[i].remove();
353+
});
354+
}
355+
356+
await context.sync();
357+
358+
eventContexts = [];
359+
console.log("Removed event handlers.");
360+
});
361+
}
362+
```
363+
364+
## See also
365+
366+
- [Annotation APIs need a Microsoft 365 subscription](word-add-ins-troubleshooting.md#annotations-dont-work)
367+
- [Manage annotations code sample](https://github.com/OfficeDev/office-js-snippets/blob/prod/samples/word/50-document/manage-annotations.yaml)
368+
- [Work with events using the Word JavaScript API](word-add-ins-events.md)

0 commit comments

Comments
 (0)