需求:在返回过来的文章中生成目录,并且点击目录文章可以跳转到对应的位置
解决:约定使用h1-h6标签来生成目录
/**
* 从HTML数据中提取内容目录树。
*
* @param htmlData HTML字符串,包含<h1-6>标题。
* @returns 返回一个对象,包含目录树和修改后的HTML字符串,其中标题标签添加了id属性。
*/
const getContentDirTree: any = (htmlData) => {
// 如果HTML数据为空,则直接返回空数组。
if (!htmlData) {
return [];
}
// 使用正则表达式匹配所有的<h1-6>标签。
let hDomList = htmlData.match(
/<h[1-6]{1}[^>]*>([\s\S]*?)<\/h[1-6]{1}>/g
);
if (!Array.isArray(hDomList)) {
return [];
}
// 初始化根节点
let stack: any[] = [{ id: 0, pid: 0, children: [] }];
// 在原有函数中增加一个映射来存储标题内容与ID的关系
let titleToIdMap: Record<string, string> = {};
// 遍历所有匹配到的标题标签。
hDomList.forEach((hDom, index) => {
// 提取标题的级别和内容。
const startIndex = hDom.indexOf("<");
const endIndex = hDom.indexOf(">");
if (startIndex != -1 && endIndex != -1) {
const re = /<h([1-6]{1})[^>]*>([\s\S]*?)<\/h[1-6]{1}>/
let hLevel = Number(hDom.replace(re, "$1"));
let titleContent = hDom.replace(re, "$2");
// 移除标题内容中的HTML标签。
let title = titleContent.replaceAll(/<([\s\S]*?)>|<\/([\s\S]*?)>/g, "");
// 为每个标题生成唯一的id。
const id = `generatedId_${index}`; // 如果没有ID,则生成一个
// 更新映射
titleToIdMap[title] = id;
// 当前标题的层级小于等于栈顶元素的层级时,出栈直到找到合适的父级或回到根节点
while (stack.length > 1 && hLevel <= stack[stack.length - 1].hLevel) {
stack.pop();
}
// 构建当前目录项,并将其添加到栈顶元素(即当前的父级)的children中
const currentDir: any = {
id,//用于点击的时候选中态
title, // 标题
domId: id, // 用于定位到标题
hLevel, // 层级
pid: stack[stack.length - 1].id,
children: []
}
stack[stack.length - 1].children.push(currentDir);
stack.push(currentDir); // 将当前目录项压入栈作为新的父级候选
}
})
// 最终的dirTree应为根节点的children
const dirTree = stack[0].children;
// 为每个目录项添加子项
dirTree.forEach((item) => {
if (item.pid !== 0) {
const findParent = dirTree.find(i => i.id === item.pid);
if (findParent) {
if (!Array.isArray(findParent.children)) {
findParent.children = []
}
findParent.children.push(item)
}
}
})
// 修改原始HTML的逻辑更新为使用titleToIdMap
const modifiedHtml = htmlData.replace(
/<h([1-6]){1}[^>]*>([\s\S]*?)<\/h[1-6]{1}>/g,
(match, hLevel, titleContent) => {
const cleanedTitle = titleContent.replace(/<([\s\S]*?)>|<\/([\s\S]*?)>/g, ""); // 简化清理标签的正则
const id = titleToIdMap[cleanedTitle];
if (id) {
return `<h${hLevel} id="${id}">${titleContent}</h${hLevel}>`;
} else {
console.warn(`No ID found for title: "${cleanedTitle}"`);
return match; // 如果没有找到ID,原样返回匹配的文本
}
}
);
// 返回目录树和修改后的HTML。
return { dirTree: dirTree.filter((i) => i.pid === 0), modifiedHtml };
}
// 调用
const { dirTree, modifiedHtml } = getContentDirTree(props.dataSource?.messageContent);