マークダウンページの目次の自動生成

imatomix
2017年12月3日 13:47

ゴール

  • マークダウンで書いたページの目次を自動で生成したい。
  • 目次に入れるのは h1、h2、h3 タグ。

ざっくりな流れ

  • hタグをカウントしながらアンカーオブジェクトを配列に追加していく。
  • アンカーオブジェクト配列を元に目次を生成する。

スクリプト

アンカー

  • ハイフンで区切られた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] ) } } /* 結果こうなる #a // <h1 id="#index_1-0-0">a</h1> ##b // <h2 id="#index_1-1-0">b</h2> ###c // <h3 id="#index_1-1-1">b</h3> ##d // <h2 id="#index_1-2-0">b</h2> #e // <h1 id="#index_2-0-0">a</h1> */

目次生成

  • 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) { // h4以降は無視 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>