书签是 Word 文档中一种不可见的定位标记,它充当文档中的"坐标",能够精确标记某个位置或一段文本范围。无论是合同模板中的待填写区域、长文档中需要跳转的关键章节,还是批量生成报表时用于定位数据插入点,书签都是背后关键的锚点。开发者可以基于书签实现文档内容的动态填充、定位导航、内容提取等高级功能,因此书签操作是 Word 自动化中最常用的基础能力之一。
Spire.Doc for JavaScript 基于 WebAssembly 在浏览器端直接完成书签的创建、定位和删除操作,通过虚拟文件系统(VFS)管理文件,无需后端服务支持。
本文介绍三个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。以下示例默认已安装 Spire.Doc 并完成 WebAssembly 模块初始化。
为段落添加书签
在已有文档中追加书签,可以通过 AppendBookmarkStart 和 AppendBookmarkEnd 在指定段落上标记书签区域。不仅支持为文档中已有的段落添加书签标记,也支持在新段落中追加书签。Spire.Doc 同时支持嵌套书签,方便构建层级结构。
function App() {
const createBookmarkInWord = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查模块是否就绪
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将 Word 文件载入 VFS
const inputFileName = '中国旅行手册.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 获取第一节,添加书签
let section = doc.Sections.get_Item(0);
AddBookmark(section);
// 保存为 .docx 文件
const outputFileName = "添加书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从 VFS 读取文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
doc.Dispose();
};
function AddBookmark(section) {
// 书签 1:给文档已有的段落添加书签
let paraStart = section.Paragraphs.get_Item(1);
let paraEnd = section.Paragraphs.get_Item(3);
paraStart.AppendBookmarkStart("书签1");
paraEnd.AppendBookmarkEnd("书签1");
// 书签 2:新增段落添加书签
let paragraph = section.AddParagraph();
paragraph.AppendBookmarkStart("书签2");
paragraph.AppendText("这是新增段落。");
paragraph.AppendBookmarkEnd("书签2");
}
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Bookmark in Word</h1>
<button onClick={createBookmarkInWord}>
Generate
</button>
</div>
);
}
export default App;
执行上述代码,添加的书签如下图所示:

为所选文本添加书签
为已有段落中的特定文本添加书签,需要先通过 FindAllString 查找文本位置,使用 BookmarkStart 和 BookmarkEnd 构造函数创建书签对象,再通过 ChildObjects.Insert 将起始标记插入到匹配文本的 TextRange 之前,结束标记插入到其后。
function App() {
const addBookmarkForMatchedText = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将Word 文档载入虚拟文件系统(VFS)
const inputFileName = '中国旅行手册.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载 Word 文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 查找文档中所有匹配"美食"的文本
let textSelections = doc.FindAllString('美食', false, true);
// 遍历每一处匹配结果,在其前后插入书签起始和结束标记
for (let i = 0; i < textSelections.length; i++) {
// 创建书签起始和结束对象(书签名称为 "书签_0"、"书签_1"...)
let start = new docModule.BookmarkStart(doc, "书签_" + i);
let end = new docModule.BookmarkEnd(doc, "书签_" + i);
let selection = textSelections[i];
// 获取匹配文本的 TextRange
let textRange = selection.GetAsOneRange();
// 获取匹配文本所在的段落
let para = textRange.OwnerParagraph;
// 获取该 TextRange 在段落子对象中的索引位置
let index = para.ChildObjects.IndexOf(textRange);
// 在 TextRange 之前插入书签起始,之后插入书签结束
para.ChildObjects.Insert(index, start);
para.ChildObjects.Insert(index + 2, end);
}
// 保存为新的 .docx 文件
const outputFileName = "添加书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从VFS中读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Bookmarks for Specific Text in Word Documents</h1>
<button onClick={addBookmarkForMatchedText}>
Generate
</button>
</div>
);
}
export default App;
此方式适用于需要在现有文档基础上添加定位标记的场景,例如在已写好的合同文档中标明待填充区域。执行上述代码,添加的书签如下图所示:

删除书签
删除书签仅移除书签标记本身,书签范围内的文本内容会被保留。通过 document.Bookmarks 集合获取书签对象后,调用 Remove 方法即可完成删除。
function App() {
const deleteBookmark = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将Word 文档载入虚拟文件系统(VFS)
const inputFileName = '添加书签.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载 Word 文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 按名称获取书签
let bookmark = doc.Bookmarks.get_Item("书签_1");
// // 按索引获取书签
// let bookmark = doc.Bookmarks.get_Item(0);
// 删除书签(不删除其内容)
doc.Bookmarks.Remove(bookmark);
// 保存为新的 .docx 文件
const outputFileName = "删除书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放文档资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Delete Bookmark by Name in Word Document</h1>
<button onClick={deleteBookmark}>
Generate
</button>
</div>
);
}
export default App;
删除书签后,文档中原书签标记消失,但标记范围内的文本仍保留在文档中。

常见问题
添加书签时提示名称重复
原因:Word 文档中书签名称必须唯一,重复添加同名书签会导致错误。
解决:在添加书签前检查名称是否已存在:
if (document.Bookmarks.FindByName("MyBookmark") === null) {
paragraph.AppendBookmarkStart("MyBookmark");
paragraph.AppendText("内容");
paragraph.AppendBookmarkEnd("MyBookmark");
}
删除书签和删除书签内容有什么区别
原因:Spire.Doc 的 Bookmarks.Remove 仅移除书签标记(开始和结束标记),中间的文本内容不受影响。
解决:根据实际需求选择操作:
// 仅删除书签标记,保留文本
document.Bookmarks.Remove(bookmark);
// 删除书签及其内容(通过 BookmarksNavigator 定位后删除)
let navigator = new docModule.BookmarksNavigator(doc);
navigator.MoveToBookmark("MyBookmark");
navigator.navigator.DeleteBookmarkContent(true); // 删除书签范围内的内容
AppendBookmarkStart 和 AppendBookmarkEnd 必须在同一段落吗
原因:起始标记和结束标记可以在不同段落,代码中"书签1"的示例展示了跨段落的用法。关键限制在于书签范围内的文档对象结构必须完整,不能跨表格单元格标记,因为单元格是独立容器,跨单元格可能导致书签识别异常。
解决:如果书签范围跨越了表格单元格,应调整书签的起始或结束位置,确保在同一单元格内闭合。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







