uu
HTML
<p class="sticky-controls"></p>
<textarea id="body_content">
[Generating a ToC | An Archaeopteryx](https://anarchaeopteryx.bearblog.dev/2025-10-20-generating-a-toc/)
Stranger, I have a better solution for you.
## Customize your dashboard
1. Visit [https://bearblog.dev/dashboard/customise/](https://bearblog.dev/dashboard/customise/)
2. Copy+Paste the below javascript code into the 'Dashboard footer content'
3. Once you have written a post, click the 'Generate ToC' button (*it gets copied into your clipboard*)
4. Paste it into your post wherever you want it.
## The Code
Heavily commented for non-coders.
```html
<script type="text/javascript">
</script>
```
---
## Sample Header 1
blah blah body text body text and what about an ###inline hashtag which should be ignored
### Sub_header 1
so let's hope that an super ## inline hashtag doesn't mess things up
#### Let's ne--st again!
We're now even deeper in the ToC
## Sample Header 2
okay but this one will only have one nested list
### Sample header 2 sub 1
testing testign 2 1
### Sample header 2 sub 2
testing testing 2 2
</textarea>
<div id="sample_table"></div>
JavaScript
// create a new button element
const toc_btn = document.createElement('button');
toc_btn.classList.add('rdb_post_restorer');
toc_btn.setAttribute('onclick', "event.preventDefault();generate_toc();");
toc_btn.innerText = 'Generate ToC';
// add the button to your 'New Post' page
document.querySelector('.sticky-controls').appendChild(toc_btn);
/** scan your post text and generate a ToC, copying it to your clipboard */
function generate_toc(){
// HTML for the start of the ToC list
var toc = "\n\n"+'<div class="toc">'
+"\n"+'<h2 class="toc-head">Contents:</h2>'
+"\n"+'<ol class="toc-list">';
// get your post's content
const body = document.querySelector('#body_content').value;
// find all header occurences
const headers_flat = body.matchAll(/^#{1,6}/gm);
// the first header in the ToC should use '##'
let header_size = 2;
let started = false;
// loop over the header occurences and generate html
for (const match of headers_flat){
// how many hashtags this header has
const length = match[0].length;
// you should not use '#' for section headers. Use `##`
if (length < 2)continue;
const indent = " ";
const adjusted_indent = indent.repeat(length-2);
const li_indent = adjusted_indent + indent;
if (length == header_size && started == true){
toc += "</li>";
} else if (length > header_size) {
// add a new sub-list for sub-headers
toc += "\n"+adjusted_indent+"<ul class=\"tol-list\">";
} else if (length < header_size){
// end a sub-list, escape into the parent list
toc += "</li>\n"+adjusted_indent+"</ul></li>".repeat(header_size - length);
}
header_size = length;
started = true;
// get the full text of the header
const start_index = match.index + length;
const end_index = body.indexOf("\n", match.index);
const header_text = body.substring(start_index, end_index)
.trim();
const...