Modding the editor.md Component to Optimize ToC Rendering
Introduction My StarBlog currently uses the editor.md component to render markdown articles on the frontend, but the ToC (table of contents) automatically generated by this component isn't very pretty. I previously modded a tree component called BootStrap-TreeView, so I wanted to use that tree component to display the ToC. The original look was like this image-20221020112752560 The result after my modding image-20221020112914518 Let's analyze first First look
目录7 个章节
Introduction
My StarBlog currently uses the editor.md component to render markdown articles on the frontend, but the ToC (table of contents) automatically generated by this component isn't very pretty. I previously modded a tree component called BootStrap-TreeView, so I wanted to use that tree component to display the ToC.
The original look was like this

The result after my modding

Let's Analyze First
First, let's see how the editor.md component renders markdown, based on the official example.
First write a textarea and put the markdown in it
<div id="post-markdown-content" class="post-content">
<textarea style="display:none;">@Model.Content</textarea>
</div>
Also need a div to hold the table of contents
<div id="post-toc-container"></div>
Then use js to call its render method
let editorMdView = editormd.markdownToHTML("post-markdown-content", {
htmlDecode: true,
tocm: true, // Using [TOCM]
tocContainer: "#toc-container", // 自定义 ToC 容器层
emoji: true,
taskList: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true, // 默认不解析
});
This way both the article content and the table of contents appear.
My first thought was that since it can render the table of contents, there must be a ToC tree structure I could use.
But after console.logging in the console for quite a while, I still didn't see this tree structure.
The data returned by the editormd.markdownToHTML method is simply a div element object...
Time to Tinker
Speechless. No other choice, I had to look at the source code.
The entire component's source code is in a single js file, luckily the readability is decent~
I went straight to the definition of the markdownToHTML method, starting at line 3885, then found a variable markdownToC inside, which should be what we need. Printed it out to take a look, the approximate structure is as follows
[
{"text": "Node1", "slug": "node1", "level": 2},
{"text": "Django-Dev", "slug": "django-dev", "level": 3},
{"text": "Java-Dev", "slug": "java-dev", "level": 3},
{"text": "Spring-Dev", "slug": "spring-dev", "level": 3}
]
Not a tree structure, just an array
It simply traverses the entire markdown document, and the level field is determined by the heading type, that is, the number of #s.
And the Bootstrap-Treeview component I previously modded needs a tree-structured object passed in for rendering, so I need to process it further.
Processing the Tree Structure
At first I was stubborn and directly threw that blob generated by editor.md into recursion.
Didn't work out.
Later I changed my approach: first modify this array, adding id and pid to each item, then recursively generating the tree becomes much easier to handle~
First define the node object; this is the data structure that Bootstrap-Treeview needs
class TocNode {
constructor(text, href, tags, nodes) {
this.text = text
this.href = href
this.tags = tags
this.nodes = nodes
}
}
Then iterate through that markdownToC blob, set the pid of top-level nodes to -1, assign ids to each node sequentially starting from 0, then for each node look backwards for a node whose level is 1 less; that's the parent, set the pid, done~
let toc = markdownToC
for (let i = 0; i < toc.length; i++) {
let item = toc[i]
item.id = i
item.pid = -1
for (let j = i; j >= 0; j--) {
let preItem = toc[j]
if (item.level === preItem.level + 1) {
item.pid = j
break
}
}
}
Generating the tree structure is pretty simple, so I won't say much
function getNodes(pid = -1) {
let nodes = toc.filter(item => item.pid === pid)
if (nodes.length === 0) return null
return nodes.map(item => new TocNode(item.text, `#${item.text}`, null, getNodes(item.id)))
}
Done~
Modding
Then, based on this approach, I forked a copy of the code and modded it.
The original version returns a div element directly after the markdownToHTML method finishes executing. I added two properties to the returned div element, then return the div:
div.markdownToc = markdownToC
div.markdownTocTree = editormd.tocListToTree(markdownToC)
return div
This way it's convenient to integrate the Bootstrap-Treeview component when using it.
Usage
First install the NPM packages I published
// 魔改的 editor.md 组件
npm i editor.md-ext
// 魔改的树形列表组件
npm i bootstrap5-treeview
Include CSS on the page
<link rel="stylesheet" href="~/lib/editormd/css/editormd.preview.css">
Include JS
<!-- jQuery -->
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<!-- 树形列表组件 -->
<script src="~/js/bootstrap-treeview.js"></script>
<!-- editor.md 需要的依赖 -->
<script src="~/lib/editormd/lib/marked.min.js"></script>
<script src="~/lib/editormd/lib/prettify.min.js"></script>
<script src="~/lib/editormd/lib/raphael.min.js"></script>
<script src="~/lib/editormd/lib/underscore.min.js"></script>
<script src="~/lib/editormd/lib/sequence-diagram.min.js"></script>
<script src="~/lib/editormd/lib/flowchart.min.js"></script>
<script src="~/lib/editormd/lib/jquery.flowchart.min.js"></script>
<script src="~/lib/editormd/editormd.js"></script>
Prepare a div as the ToC container
<div id="post-toc-container"></div>
Prepare a div to hold the article content, and put the Markdown content into this textarea.
<div id="post-markdown-content" class="post-content">
<textarea style="display:none;">@Model.Content</textarea>
</div>
Write a piece of JS code to enable rendering
$(function () {
// 渲染文章
let editorMdView = editormd.markdownToHTML("post-markdown-content", {
htmlDecode: true,
tocm: true, // Using [TOCM]
emoji: true,
taskList: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true, // 默认不解析
});
// 渲染目录
$('#post-toc-container').treeview({
data: editorMdView.markdownTocTree,
levels: 2,
enableLinks: true,
highlightSelected: false,
showTags: true,
})
})
For usage of the tree list component, you can check my GitHub: https://github.com/Deali-Axy/bootstrap5-treeview
Related Links
_EOF_
Modding the editor.md Component to Optimize ToC Rendering
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
NextJs 与 Tailwind 入门开发笔记
Next.js?React的脚手架罢了,开箱即用,拒绝精神内耗。Tailwind?Bootstrap的替代品,罢了。实际开发中,pnym坑死人,localStorage问题?小菜一碟。
2024-01-27 · Web前端 · 约 1 分钟
视频中台解决方案:组织树组件+多路视频直播界面开发
这种国产粗犷风格的项目居然能用?16路视频播放界面倒是简单直接,但现场配置和设备管理流程真是够折腾的。WVP和mpegts.js?B站开源的尿性,细节全靠蒙。
2025-07-04 · Web前端 · 约 1 分钟
使用 swagger-typescript-api 在前端项目里生成请求代码
swagger-typescript-api?文档写得像草稿,还得自己摸索。那些花哨的模块化选项除了报错还能干嘛?老老实实生成单文件,配个客户端封装,省得每次折腾。
2025-11-03 · Web前端 · 约 1 分钟
纯前端实现词云展示+附微博热搜词云Demo代码
前言 最近工作中做了几个数据可视化大屏项目,其中也有用到了词云展示,以前做词云都是用python库来生成图片显示的,这次用了纯前端的实现~~(Ctrl+V真好用)~~,同时顺手做个微博热搜的词云然后记录一下~ 依赖 echarts 4.x echarts-wordcloud 1.1.3 tips:echarts-wordcloud现在有2.0和1.x两个版本,2.0对应echarts 5.x版本
2021-12-18 · Web前端 · 约 1 分钟
讨论
共 0 条加载评论中…