Obsidianでdataviewjsを使って目次を作成する

Obsidianでdataviewjsを使って目次を作成する

Obsidianでちょっと長めの文章書くと目次を表示したくなります。

色々な方法で目次を表示することはできますが、文章を更新したら目次も自動的に変わってほしいのでdataviewjsを使って目次を動的に表示してみました。
目次の表示方法を紹介していきますが、今回はdataviewjsの基本的な知識があることを前提に進めます。

目次
  1. dataviewjsのdv.elとは
  2. 目次を表示するためのスクリプト作成
  3. 目次の表示
  4. まとめ

dataviewjsのdv.elとは

今回目次を表示するにあたって、dataviewjsの`dv.el`を使用します。

dataviewjsを使用する際のdv.viewは、ObsidianのDataviewプラグインの中の一つの関数です。
この機能を用いることで、Obsidian内で定義された既存のビューやカスタムビューをプログラム的に呼び出して利用することができます。

たとえば、ある特定のカスタムビューを表示したい場合、以下のようにdv.viewを用いて実現できます。

1
dv.view('views/example')

目次を表示するためのスクリプト作成

dataviewjsのdv.viewを使用して、目次を表示するためにファイルを2つ作成します。
スクリプトファイルを起きたいフォルダに`view.js`と`view.css`を作成してください。

例えばこんな感じ。

  • Obsidian root/scripts/toc/view.js
  • Obsidian root/scripts/toc/view.css

HTMLを作成するためのJavaScript。

toc/view.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class Toc {
  constructor(markdown) {
    this.markdown = markdown
    this.container = dv.el('div', '')
  }

  getOutline() {
    return this.markdown
      .split('\n')
      .filter((line) => line.match(/^#+\s/))
      .map((heading) => {
        const depth = heading.split(' ')[0].length
        const text = heading.substr(depth + 1)

        return {
          depth,
          text,
          subHeadings: [],
        }
      })
  }

  getHeadings() {
    const toc = []
    const outline = this.getOutline()
    const parentHeadings = new Map()

    outline.forEach((item) => {
      parentHeadings.set(item.depth, item)

      if (item.depth === 2) {
        toc.push(item)
      } else {
        parentHeadings.get(item.depth - 1).subHeadings.push(item)
      }
    })

    return toc
  }

  createItem(heading) {
    return `<li>
      <a data-href="#${heading.text}" href="#${heading.text}" class="internal-link" target="_blank" rel="noopener">${heading.text}</a>
      ${
        heading.subHeadings.length > 0
          ? `

        <ol class="x-toc__list x-toc__list--level${heading.depth + 1}">
          ${heading.subHeadings
            .map((subHeading) => {
              return this.createItem(subHeading)
            })
            .join('')}
        </ol>
          </li>
          `
          : ''
      }`
  }

  createHTML() {
    const headings = this.getHeadings()
    const html = headings
      .map((heading) => {
        return `${this.createItem(heading)}`
      })
      .join('')

    return html
  }

  render() {
    const container = dv.el('div', '')
    const html = this.createHTML()

    container.innerHTML = `<div class="x-toc">

      <div class="x-toc__title">Table of Contents</div>
      <ol class="x-toc__list">${html}</ol>
      </div>`
  }
}

const page = dv.current()
const markdown = await dv.io.load(page.file.path)
const toc = new Toc(markdown)

toc.render()

作成したHTMLを装飾するためのCSS。

toc/view.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.x-toc {
  border: 1px solid #eee;
  border-radius: 5px;
  padding: 1rem;
}

.x-toc ol {
  list-style: none;
}

.x-toc .x-toc__title {
  margin-bottom: 10px;
  font-weight: bold;
}

.x-toc .x-toc__list {
  margin: 0;
  padding: 0;
  counter-reset: item;
}

.x-toc .x-toc__list li::before {
  counter-increment: item;
  content: counters(item, '.') ' ';
}

.x-toc .x-toc__list--level3 {
  padding-left: 1rem;
}

今回目次表示のアウトラインを作るにあたり、下記の記事を参考にさせていただきました。

Create Table of contents in Astro and sectionize the Markdown content | by Reza Zahedi | Medium

目次の表示

準備ができたらあとはノート内でスクリプトを実行するだけです。
dataviewjsの呼び出しブロックで

1
await dv.view('views/toc')

を実行することで、目次が表示されます。

例えば下記ようなのマークダウンがあるとします。

1
2
3
4
5
6
7
## Sample Heading2

### Sample Hading3

### Sample Hading3-1

## Sample Heading2-1

そうするとこんな感じで目次が表示されます。

もしCSSがわかる人は、view.cssをカスタマイズして目次の見た目を変更することもできます。

まとめ

dv.viewで作成したら目次を表示することで文章を更新すれば動的に目次が作られるので、少し長めの文章を書くときなんかはとても快適になりました。

JavaScriptとCSSがわからないと少し敷居の高いdv.viewですが、自由度がぐんと上がりいい感じ!

この記事を書いた人
KJ
KJ

初めてブログに触れたのはまだSNSが流行る前。そしてブログの楽しさをきっかけにシステムエンジニアになりました。会社員を経て独立して、人生の時間を有意義に過ごせるように日々暮らしています。 好きなことはディズニー、ライブ参加、旅行、飛行機・空港、整理収納、本、お酒、ゲーム、ObsidanやNotionのようなツールを触る、YouTubeを見るなどなど。 ■詳しいプロフィールが気になる人 こちらプロフィールを読んでください。

関連記事

  1. HOME
  2. Obsidianでdataviewjsを使って目次を作成する

注目記事