Moy
1
本帖的想法来自 DataviewJS 的提取与汇总 - 经验分享 - Obsidian 中文论坛 以及 使用 Dataview 读取文件中所有标题 - 经验分享 - Obsidian 中文论坛
感谢前人的经验,也特别感谢 @PlayerMiller 的代码示例
效果
其中,目录 里的链接是使用 Dataview JS 脚本自动生成的,会随着笔记内容自动更新。
和其他方案的对比:
- 和 Automatic Table of Content 等插件相比,使用了大部分人都在用的 Dataview 插件,减少额外的插件安装
- 和 Table of Contents 插件相比,优势是可以自动实时更新,劣势是并非实际的 Markdown 文本,无法脱离插件使用
使用方法
将 dataviewjs 代码粘贴进笔记,开启 Dataview 插件的脚本渲染功能的情况下,直接会生效。
生成的目录本质是 [[#标题]]
形式的链接,可以直接点击跳转。
代码
第一版:纯列表形式
```dataviewjs
const startHeadinglevel = 2;
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 + 4 );
let listSign = repeatCount > 0 ? '- ' : '';
let linkText = `[[#${p.heading}]]`;
let headingList = (p.level < startHeadinglevel) ? `- ${linkText}` : `${spacesPrefix}- ${linkText}`;
return headingList;
}
)
let result = raws.join('\n');
// 添加行距
dv.container.style.lineHeight = "1.5em";
dv.paragraph(result)
```
第二种:一级标题单独呈现
```dataviewjs
// 尝试另一种形式,个人更喜欢让一级列表特殊一点
const startHeadinglevel = 2;
const file = app.workspace.getActiveFile();
const { headings } = app.metadataCache.getFileCache(file);
let output = '';
const additionalAttr = {attr: {style: 'margin-block-start: 0 !important; margin-block-end: 0 !important;'}};
// 整个 dv 容器的样式调整
dv.container.style.marginBlockStart = "0em";
headings.forEach ( h => {
if (h.level < startHeadinglevel) {
// 先输出之前的子列表内容
if (output) dv.paragraph(output, additionalAttr);
// 将一级标题输出成单独的段落
output = `[[#${h.heading}]]\n`;
dv.paragraph(output);
output = '';
} else {
output += `${' '.repeat((h.level - startHeadinglevel) * 4)}- [[#${h.heading}]]\n`;
}
})
// 把最后的残余内容输出出来
if (output) dv.paragraph(output, additionalAttr);
```
脚本要点
- 最初的难点就是 「怎么用 Dataview 输出有层级的列表」,因为直接加空格会导致文本内容被识别成「代码」而非「嵌套列表」
- 查看 @PlayerMiller 的代码发现可以通过 将所有文本一起输出成 paragraph 的方式渲染成正确的列表
- 但是对第二版来说,没法让作为「非列表」的一级标题很好地共同渲染,所以分段进行单独渲染
- 为了调整行间距等样式,使用
dv.container.style
直接给 dv 容器指定样式
- 针对列表的段落,用
dv.paragraph(output, {attr: {style: 'margin-block-start: 0 !important; margin-block-end: 0 !important;'}} )
单独指定样式
4 个赞
Moy
3
- 位置不一样(独立窗口 VS 笔记上方)
- 可定制性(可以自定义显示的内容,比如还可以加上序号,etc)
别的大差不差,主要就是提供展示目录+跳转到标题的功能。
Alpha
(黄海博)
4
你好,使用了你的代码,我的三级和四级标题以及以下的标题会显示错误,一级和二级就没问题
Alpha
(黄海博)
6
我调整一下 lever=4 就可以显示了。只不过调整后,二级和三级标题同级了,但是也挺满意的,谢谢啦
Alpha
(黄海博)
7
后面尝试之后,默认level=2 的话,标题一定要有一级标题出现才行,不然缩进有问题,同理 level=3 的话,文章中要有二级标题才行,这是 level=2,但是没有一级标题的显示效果
还有个问题请教下,那个间距我调了好像没效果,不知道是不是和我的 css 或插件起冲突了
Moy
8
const startHeadingLevel = 2;
const file = app.workspace.getActiveFile();
const { headings } = app.metadataCache.getFileCache(file);
if (headings) {
const fragment = document.createDocumentFragment();
let currentList = null;
let currentLevel = 0;
dv.container.style.marginBlockStart = "0em";
headings.forEach(h => {
if (h.level < startHeadingLevel) {
// 对于一级标题,创建一个单独的段落
const p = dv.el('p', '');
p.appendChild(dv.el('a', h.heading, {href: '#' + h.heading, cls: 'internal-link'}));
fragment.appendChild(p);
currentList = null;
currentLevel = 0;
} else {
const listItem = dv.el('li', '');
listItem.appendChild(dv.el('a', h.heading, {href: '#' + h.heading, cls: 'internal-link'}));
if (!currentList || h.level > currentLevel) {
const newList = dv.el('ul', '', {
attr: {
style: 'margin-block-start: 0; margin-block-end: 0; line-height: 1.5em;'
}
});
if (currentList) {
currentList.lastChild.appendChild(newList);
} else {
fragment.appendChild(newList);
}
currentList = newList;
} else if (h.level < currentLevel) {
for (let i = 0; i < currentLevel - h.level; i++) {
currentList = currentList.parentNode.parentNode;
}
}
currentList.appendChild(listItem);
currentLevel = h.level;
}
});
dv.container.appendChild(fragment);
}
换了一个代码方案,试试这个呢?
Moy
9
以及间距没效果的话,按 Ctrl+Shift+I
然后点左上角那个按钮:
选择列表,看一下是不是有什么属性把它覆盖了
Alpha
(黄海博)
10
我的文章只有三级和四级标题,这个代码也出错,将 level=3 就正常了哈哈哈,我的需求已经满足了,感谢大佬,换了代码间距也正常了
Moy
14
发图我没办法测hhh
你可以直接把你出问题的 md 文件传一份,或者把笔记完整文本发出来
1212
(z)
15
通过百度网盘分享的文件:test-.7z
链接:https://pan.baidu.com/s/1O6v7Of4Cx19hdeJX51KGlg?pwd=2fmj
提取码:2fmj
–来自百度网盘超级会员V5的分享,
这个发送不了MD文件,我用网盘传一下,
Moy
18
找出来了……是你的最后一行:
}
和 ``` 不能放在一起的,要换行。
你的:
正确的:
改完就行了:
1212
(z)
19
大佬,前几天考试,没时间看,今天尝试了下,还是不行
Moy
20
这个是用来提取「当前文件」的标题的,并不是「当前文件夹」
所以,对于这样的笔记:
## 标题
### 小标题
它才能生效。
你多加几个标题试试?