使用DV创建动态更新的笔记目录大纲(TOC)

这一行:

const file = app.workspace.getActiveFile();

把它换成你想要获取的笔记:

// 指定笔记文件的路径
const filePath = "path/to/your/note.md";

// 获取对应文件
const file = app.vault.getAbstractFileByPath(filePath );

更进一步的定制可以问 AI。

大佬,我想问一下,如果我在每一篇的笔记开头都添加这个目录显示,那在拥有上千个笔记的库在启动和运行时会不会很卡,因为我不了解dataview的运行方式,所以不是很清楚

你好,不会的,Obsidian 是动态加载,只有当前打开/聚焦的笔记内容会进行渲染。

另外如果想给每个笔记都加上目录,用这个代码块就有点麻烦了,可以考虑试试这个插件:
GitHub - RavenHogWarts/obsidian-ravenhogwarts-toolkit

3 个赞

背景

【借楼发挥】在以上各位的基础上,我通过问AI得出新的自动生成目录代码,即无序目录代码二【经实用:如果没有一级标题,从二级标题开始,那就把第一行的1改成2,就显示正常了。】

诉求

但同时,请求教高手,是否可以把带层级序号的自动目录,改成可以缩进层级的版本【不懂,已问过多个AI,但仍然无法解决,先行谢过了】

问题

以下两段可以在obsidian笔记软件里使用的dataview代码,无序目录代码二可以正常显示层级目录,请按此要求,修改序号目录代码一,使其也可以正常显示为层级目录,并保留序号相关设置:

序号目录代码一

const startHeadinglevel = 1; // 设置起始标题层级(1=从H1开始)
const file = app.workspace.getActiveFile();
const { headings } = app.metadataCache.getFileCache(file);

// 初始化层级计数器
let counters = [];

const raws = headings.map(p => {
    const adjustedLevel = p.level - startHeadinglevel + 1;
    if (adjustedLevel < 1) return ""; // 过滤低于起始层级的标题
    
    // 扩展计数器数组到当前层级
    while (counters.length < adjustedLevel) counters.push(0);
    
    // 递增当前层级计数器,并重置子层级
    counters[adjustedLevel-1]++;
    for (let i = adjustedLevel; i < counters.length; i++) counters[i] = 0;
    
    // 生成数字序号
    const number = counters.slice(0, adjustedLevel).join(".") + ".";
    
    // 计算缩进
    const indent = " ".repeat((p.level - startHeadinglevel) * 4);
    return `${indent}${number} [[#${p.heading}]]`;
});

// 输出带行距的段落
dv.container.style.lineHeight = "1.2em";
dv.paragraph(raws.join("\n"));

无序目录代码二

const startHeadinglevel = 1; // 现在可以设置为1
const file = app.workspace.getActiveFile();
const { headings } = app.metadataCache.getFileCache(file);

// 全列表的形式
const raws = headings.map(p => {
    let repeatCount = Math.max((p.level - startHeadinglevel) * 4, 0);
    let spacesPrefix = ' '.repeat(repeatCount); // 修改这里,直接使用 repeatCount
    let listSign = '- ';
    let linkText = `[[#${p.heading}]]`;
    let headingList = `${spacesPrefix}${listSign}${linkText}`; // 简化逻辑
    return headingList;
});

let result = raws.join('\n');
// 添加行距
dv.container.style.lineHeight = "1.5em";
dv.paragraph(result);
1 个赞

2025-05-23更新
dataview自动生成ob笔记目录

复制到某笔记中,把代码首行dataviewjs-text中的-text删除就可以正常使用了

序号大纲目录完美(层级缩进正常)

const startHeadinglevel = 1;
const file = dv.current().file; // 使用 dv.current().file 获取当前笔记文件
const { headings } = app.metadataCache.getFileCache(file);

// 初始化层级计数器
let counters = [];
let previousLevel = 0;

const raws = headings.map(p => {
    const relativeLevel = p.level - startHeadinglevel + 1;
    if (relativeLevel < 1) return null;
    
    // 动态计数器逻辑
    while (counters.length < relativeLevel) counters.push(0);
    counters[relativeLevel-1]++;
    counters = counters.slice(0, relativeLevel);
    
    // 生成无尾随空格的编号
    const number = counters.join(".") + "."; // 保留结尾点号
    
    // 构建列表项(关键修改点)
    const listIndent = "  ".repeat(relativeLevel - 1);
    return `${listIndent}- ${number}[[#${p.heading}]]`; // 删除点号后空格
}).filter(Boolean);

// 输出设置
dv.container.style.lineHeight = "1.3 em";
// dv.container.style.whiteSpace = "pre";【多余空行的原因】
dv.paragraph(raws.join('\n'));

如果没有一级标题,从二级标题开始,那就把第一行的1改成2,就显示正常了。

无序列表大纲目录

无序号有层级Dv目录模板

[[dv自动目录模板]],如果没有一级标题,从二级标题开始,那就把第一行的1改成2,就显示正常了。

const startHeadinglevel = 1; // 现在可以设置为1
const file = dv.current().file; // 使用 dv.current().file 获取当前笔记文件
const { headings } = app.metadataCache.getFileCache(file);

// 全列表的形式
const raws = headings.map(p => {
    let repeatCount = Math.max((p.level - startHeadinglevel) * 4, 0);
    let spacesPrefix = ' '.repeat(repeatCount); // 修改这里,直接使用 repeatCount
    let listSign = '- ';
    let linkText = `[[#${p.heading}]]`;
    let headingList = `${spacesPrefix}${listSign}${linkText}`; // 简化逻辑
    return headingList;
});

let result = raws.join('\n');
// 添加行距
dv.container.style.lineHeight = "1.2em";
dv.paragraph(result);

补充调整

仅显示当前笔记目录

问题:没替换的情况下,光标移到对侧活动窗口,就改为另一个笔记的目录。

这一行:
const file = app.workspace.getActiveFile();
替换成:
const file = dv.current().file; // 使用 dv.current().file 获取当前笔记文件
通过 Dataview 的上下文获取当前文件自身(而不是活动窗口的焦点文件)

可以指定提取其他笔记的标题吗?

这一行:
const file = app.workspace.getActiveFile();
把它换成你想要获取的笔记:
// 指定笔记文件的路径
const filePath = "path/to/your/note.md";
// 获取对应文件
const file = app.vault.getAbstractFileByPath(filePath );

正则将dv目录转ob手工目录

  1. 手工复制dv目录结果到一个新的ob笔记页面
  2. 新笔记dv目录,全选复制到可正则替换的编辑器【无此步骤,层级缩进会被忽略,个人试验】
  3. 查找:\[(.*?)\]\(.*?\),全部 替换为:[[#$1]] 【此步骤为正则搜索替换】
2 个赞