It would be useful to be able to put a table of contents with clickable links to the headers at the top of a document. This is esp. useful if the file is exported to a PDF.
The easy way to do that would be to be able to type [toc] and get a table of contents rendered in Preview.
There is a Table of Content community plugin I just found that works well. Gives you a new shortcut to run that you can bind to a hotkey if you wish.
Only problem with it is that clicking the link opens a new pane centered on the section rather then just jumping the current pane down. I’m guessing this is a Obsidian API limitation, but not sure.
@rob3r: thanks for pointing me to this plug-in. It works well, and the problem you mention in your 2nd paragraph does not occur for me: click on a jumps to the right header in the current pane.
The only issue I have is that the text “Table of Contents” is not inserted above the ToC. I have filed an issue report of the dev’s Github page.
@3dward that is what I stated in my OP. In fact, what would really, really useful in the PDF is a clickable ToC AND have those little return arrows to return from the header to the ToC.
Hi everyone, I really miss this feature ([toc])! The mentioned plugin is not a solution as it doesn’t update automatically. Indoc Table of content is really useful.
searching solutions around this. I was watching around query. but how to tell query search this file? I tried ., ./ and how to add a search like: /# (space) to detect headers and path: … in the same line?
the other topic around this was closed
I would also like this feature. It exists in Cryptpad’s Markdown editor. The Cryptpad code is open source, so perhaps it could be borrowed. Having an inline TOC is extremely useful.
Create a toc.js file in the root of your Obsidian vault, with this contents:
// Set this to 1 if you want to include level 1 headers,
// or set it to 2 if you want to ignore level 1 headers
const startAtLevel = 2
const content = await dv.io.load(dv.current().file.path)
const toc = content.match(new RegExp(`^#{${startAtLevel},} \\S.*`, 'mg'))
.map(heading => {
const [_, level, text] = heading.match(/^(#+) (.+)$/)
const link = dv.current().file.path + '#' + text
return '\t'.repeat(level.length - startAtLevel) + `1. [[${link}|${text}]]`
})
dv.header(2, 'Table of contents')
dv.paragraph(toc.join('\n'))
And then in any note which you wish to add a dynamic TOC, you just add this code:
Thanks for const content = await dv.io.load(dv.current().file.path)!
I was successfully querying the full vault but not the current file.
I use these with regex queries as reminders to do stuff on content before publishing them. Good thing it is dynamic (meaning the dragonslayer in me sees the monster raising its ugly head(s)).
Like in this use case of mine:
Let me mention an alternative version of @AlanG’s script with a tiny modification.
Obsidian indexes the information about headings, so you don’t have to do regex. Obsidian does that part on your behalf, so you only have to read it.
// Set this to 1 if you want to include level 1 headers,
// or set it to 2 if you want to ignore level 1 headers
const startAtLevel = 2;
const path = dv.current().file.path;
const { headings } = app.metadataCache.getCache(path);
const toc = headings
?.filter(({ level }) => level >= startAtLevel)
.map(({ heading, level }) => {
const link = dv.sectionLink(path, heading, false, heading);
return '\t'.repeat(level - startAtLevel) + `1. ${link}`
}).join('\n');
dv.header(2, 'Table of contents');
dv.paragraph(toc);