ゴール
ざっくりな流れ
スクリプト
アンカー
ハイフンで区切られた3桁の数字をアンカーに用いる。
それぞれの桁で h1、h2、h3 タグをカウントしていく。
h1がカウントされると、h2 と h3 は 0 にリセットされる。
h2がカウントされると、h3 は 0 にリセットされる。
これでアンカーがページ内でかぶることはない。
data: function() {
return {
anchor: [0, 0, 0]
}
},
method: {
getAnchor: function(level) {
this.anchor[level - 1] += 1
for (var i = level; i < this.anchor.length; i++) {
this.anchor[i] = 0
}
return (
'index_' + this.anchor[0] + '-' + this.anchor[1] + '-' + this.anchor[2]
)
}
}
目次生成
marked の renderer をオーバーライドする。
new marked.Renderer() でRenederオブジェクトを作成し、renderer.headingでh1、h2、h3要素のレンダー処理を上書き。
上書き処理中に目次オブジェクトに以下項目を追加していく
レベル
アンカー
見出し
data: function() {
return {
renderer: null,
toc: [],
anchor: [0, 0, 0]
}
},
computed: {
compiledMarkdown: function() {
return marked(this.note.body, { sanitize: true, renderer: this.renderer })
}
},
created: function() {
this.renderer = new marked.Renderer()
let vm = this
this.renderer.heading = function(text, level) {
let escapedText = text.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, '')
if(level < 4) {
let anchor = vm.getAnchor(level)
vm.toc.push({ level, anchor, escapedText })
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
} else {
return '<h' + level + '>' + text + '</h' + level + '>'
}
}
},
<section>
<ul id="toc">
<li>index</li>
<li v-for="t in toc" :class="'level-'+t.level"><a :href="'#'+t.anchor" @click="scroll">{{t.escapedText}}</a></li>
</ul>
</section>