实现功能

  • 增加文章详情页Toc文章目录

image.png

实现思路

对文章内容提取 h1, h2, h3, h4, h5 标签与锚,这里我们将采用到第三方包

  • github.com/PuerkitoBio/goquery
// handler/post.go

html = `
<h1 id="H55oy1">语雀文章内容</h1>
<h2 id="H55oy2">语雀文章内容</h2>
<h3 id="H55oy3">语雀文章内容</h3>
<h4 id="H55oy4">语雀文章内容</h4>
<h5 id="H55oy5">语雀文章内容</h5>
`
doc, _ := goquery.NewDocumentFromReader(strings.NewReader(html))

var navs []*Nav
doc.Find("h1, h2, h3, h4, h5").Each(func(i int, s *goquery.Selection) {
    // ...
    navs = append(navs, &Nav{
        ID: "",
        Title: "",
        Level: "",
    })
})

type Nav struct {
    ID string
    Title string
    Level int
}

详细实现

  • handler/post.go#PostDetail, 增加 toc 变量
  • theme/default/detail.html, 渲染 toc

    
    // PostDetail 文章详情页
    func PostDetail(s service.IYuQue) http.HandlerFunc {
      return func(w http.ResponseWriter, r *http.Request) {
    
          // 文章内容
          content := detail.Data.BodyHtml
          // 替换html中的cdn链接进行反向代理
          content = strings.Replace(content, "https://cdn.nlark.com/", "/", -1)
    
          var navs []*Nav
          doc, _ := goquery.NewDocumentFromReader(strings.NewReader(content))
          doc.Find("h1, h2, h3, h4, h5").Each(func(i int, s *goquery.Selection) {
    
              // 提取 ID
              id, _ := s.Attr("id")
              id = strings.ReplaceAll(id, "\\", "")
              id = strings.ReplaceAll(id, "\"", "")
              // 提取标题
              text := s.Text()
    
              // 提取标题等级
              level := 1
    
              if s.Is("h1") {
                  level = 1
              }
    
              if s.Is("h2") {
                  level = 2
              }
    
              if s.Is("h3") {
                  level = 3
              }
    
              if s.Is("h4") {
                  level = 4
              }
    
              if s.Is("h5") {
                  level = 5
              }
    
              nav := &Nav{
                  Text:  text,
                  Level: level,
                  ID:    id,
              }
              navs = append(navs, nav)
          })
    
          // 模块变量
          post := Post{
              Title:     detail.Data.Title,
              Content:   template.HTML(content),
              CreatedAt: detail.Data.CreatedAt,
              Toc:       navs,
          }
    
          t.Execute(w, map[string]interface{}{
              "post": post,
          })
      }
    }
{{ if .post.Toc }}
<div class="widget widget-content">
    <h3 class="widget-title">文章目录</h3>
    <ul style="list-style-type: none;padding-left: 0;">
        {{ range $i, $v := .post.Toc }}
        {{ if eq $v.Level 1}}
        <li>
            <a href="#{{ $v.ID }}" >{{ $v.Text }}</a>
        </li>
        {{ end }}
        {{ if eq $v.Level 2}}
        <li>
            <a href="#{{ $v.ID }}" style="padding-left: 15px;" >{{ $v.Text }}</a>
        </li>
        {{ end }}
        {{ if eq $v.Level 3}}
        <li>
            <a href="#{{ $v.ID }}" style="padding-left: 30px;" >{{ $v.Text }}</a>
        </li>
        {{ end }}
        {{ if eq $v.Level 4}}
        <li>
            <a href="#{{ $v.ID }}" style="padding-left: 45px;" >{{ $v.Text }}</a>
        </li>
        {{ end }}
        {{ if eq $v.Level 5}}
        <li>
            <a href="#{{ $v.ID }}" style="padding-left: 60px;" >{{ $v.Text }}</a>
        </li>
        {{ end }}
        {{ end }}
    </ul>
</div>
{{ end }}

最终效果

image.png

本节完整代码

https://github.com/golangtips/yuque/releases/tag/v0.0.5


Golang小贴士
1 声望0 粉丝

某厂资深工程师