博客文章是使用Markdown写的,有了TOC(Table of Contents)阅读起来体验更好,TOC就是所有h1组成的标签目录,点击后调到相应的位置。
获取所有h1标签
我用的是github_markdown库,在renderer的Header方法中可以获取,存储在level1List 列表中
func (r *renderer) Header(out *bytes.Buffer, text func() bool, level int, _ string) {
marker := out.Len()
doubleSpace(out)
if !text() {
out.Truncate(marker)
return
}
textHTML := out.String()[marker:]
out.Truncate(marker)
// Extract text content of the heading.
var textContent string
if node, err := html.Parse(strings.NewReader(textHTML)); err == nil {
textContent = extractText(node)
} else {
// Failed to parse HTML (probably can never happen), so just use the whole thing.
textContent = html.UnescapeString(textHTML)
}
anchorName := sanitized_anchor_name.Create(textContent)
out.WriteString(fmt.Sprintf(`<h%d><a name="%s" class="anchor" href="#%s" rel="nofollow" aria-hidden="true"><span class="octicon octicon-link"></span></a>`, level, anchorName, anchorName))
out.WriteString(textHTML)
out.WriteString(fmt.Sprintf("</h%d>\n", level))
if level == 1 {
r.level1List = append(r.level1List, anchorName)
}
}
拼装成html文本
tocTemplate就是拼装后的html将其放在文章内容前面就可以了
func Markdown(text []byte) []byte {
var htmlFlags = 0
renderer := &renderer{Html: blackfriday.HtmlRenderer(htmlFlags, "", "").(*blackfriday.Html)}
unsanitized := blackfriday.Markdown(text, renderer, extensions)
sanitized := policy.SanitizeBytes(unsanitized)
if len(renderer.level1List) > 0 {
var headerNameList []string
for i, v := range renderer.level1List {
headerNameList = append(headerNameList, fmt.Sprintf(`<li><a class="ez-toc-link" href="#%s">%d. %s</a></li>`, v, i+1, v))
}
var tocTemplate = fmt.Sprintf(
`<div id="ez-toc-container" class="ez-toc-light-blue">
<div class="ez-toc-title-container">
<p class="ez-toc-title">Table of Contents</p>
<span class="ez-toc-title-toggle">
<a class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle">
<i class="fa fa-dedent"></i>
</a>
</span>
</div>
<nav><ul>%s</ul></nav>
</div>`, strings.Join(headerNameList, ""))
b := []byte(tocTemplate)
b = append(b, sanitized...)
return b
}
return sanitized
}
引入CSS
将css文件引入到html中
#ez-toc-container {
background:#f9f9f9;
border:1px solid #aaa;
border-radius:4px;
box-shadow:0 1px 1px rgba(0,0,0,.05);
display:table;
margin-bottom:1em;
padding:10px;
position:relative;
width:auto
}
div.ez-toc-widget-container {
padding:0;
position:relative
}
#ez-toc-container.ez-toc-light-blue {
background:#edf6ff
}
#ez-toc-container.ez-toc-white {
background:#fff
}
#ez-toc-container.ez-toc-black {
background:#000
}
#ez-toc-container.ez-toc-transparent {
background:none transparent
}
div.ez-toc-widget-container ul {
display:block
}
div.ez-toc-widget-container li {
border:none;
padding:0
}
div.ez-toc-widget-container ul.ez-toc-list {
padding:10px
}
#ez-toc-container ul ul,.ez-toc div.ez-toc-widget-container ul ul {
margin-left:1.5em
}
#ez-toc-container li,#ez-toc-container ul {
margin:0;
padding:0
}
#ez-toc-container li,#ez-toc-container ul,#ez-toc-container ul li,div.ez-toc-widget-container,div.ez-toc-widget-container li {
background:0 0;
list-style:none none;
line-height:1.6;
margin:0;
overflow:hidden;
z-index:1
}
#ez-toc-container p.ez-toc-title {
text-align:left;
line-height:1.45;
margin:0;
padding:0;
color:#000;
font-weight:bold;
font-size: 1.2em;
}
.ez-toc-title-container {
display:table;
width:100%
}
.ez-toc-title,.ez-toc-title-toggle {
display:table-cell;
text-align:left;
vertical-align:middle
}
#ez-toc-container.ez-toc-black p.ez-toc-title {
color:#fff
}
#ez-toc-container div.ez-toc-title-container+ul.ez-toc-list {
margin-top:1em
}
.ez-toc-wrap-left {
float:left;
margin-right:10px
}
.ez-toc-wrap-right {
float:right;
margin-left:10px
}
#ez-toc-container a {
color:#444;
text-decoration:none;
text-shadow:none
}
#ez-toc-container a:visited {
color:#9f9f9f
}
#ez-toc-container a:hover {
text-decoration:underline
}
#ez-toc-container.ez-toc-black a {
color:#fff
}
#ez-toc-container.ez-toc-black a:visited {
color:#fff
}
#ez-toc-container a.ez-toc-toggle {
color:#444
}
#ez-toc-container.counter-flat ul,#ez-toc-container.counter-hierarchy ul,.ez-toc-widget-container.counter-flat ul,.ez-toc-widget-container.counter-hierarchy ul {
counter-reset:item
}
#ez-toc-container.counter-numeric li,.ez-toc-widget-container.counter-numeric li {
list-style-type:decimal;
list-style-position:inside
}
#ez-toc-container.counter-decimal ul.ez-toc-list li a::before,.ez-toc-widget-container.counter-decimal ul.ez-toc-list li a::before {
content:counters(item,".") ". ";
counter-increment:item
}
#ez-toc-container.counter-roman li a::before,.ez-toc-widget-container.counter-roman ul.ez-toc-list li a::before {
content:counters(item,".",upper-roman) ". ";
counter-increment:item
}
.ez-toc-widget-container ul.ez-toc-list li::before {
content:' ';
position:absolute;
left:0;
right:0;
height:30px;
line-height:30px;
z-index:-1
}
.ez-toc-widget-container ul.ez-toc-list li.active::before {
background-color:#ededed
}
.ez-toc-widget-container li.active>a {
font-weight:900
}
.ez-toc-btn {
display:inline-block;
padding:6px 12px;
margin-bottom:0;
font-size:14px;
font-weight:400;
line-height:1.428571429;
text-align:center;
white-space:nowrap;
vertical-align:middle;
cursor:pointer;
background-image:none;
border:1px solid transparent;
border-radius:4px;
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
-o-user-select:none;
user-select:none
}
.ez-toc-btn:focus {
outline:thin dotted #333;
outline:5px auto -webkit-focus-ring-color;
outline-offset:-2px
}
.ez-toc-btn:focus,.ez-toc-btn:hover {
color:#333;
text-decoration:none
}
.ez-toc-btn.active,.ez-toc-btn:active {
background-image:none;
outline:0;
box-shadow:inset 0 3px 5px rgba(0,0,0,.125)
}
.ez-toc-btn-default {
color:#333;
background-color:#fff;
border-color:#ccc
}
.ez-toc-btn-default.active,.ez-toc-btn-default:active,.ez-toc-btn-default:focus,.ez-toc-btn-default:hover {
color:#333;
background-color:#ebebeb;
border-color:#adadad
}
.ez-toc-btn-default.active,.ez-toc-btn-default:active {
background-image:none
}
.ez-toc-btn-sm,.ez-toc-btn-xs {
padding:5px 10px;
font-size:12px;
line-height:1.5;
border-radius:3px
}
.ez-toc-btn-xs {
padding:1px 5px
}
.ez-toc-btn-default {
text-shadow:0 -1px 0 rgba(0,0,0,.2);
box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)
}
.ez-toc-btn-default:active {
box-shadow:inset 0 3px 5px rgba(0,0,0,.125)
}
.btn.active,.ez-toc-btn:active {
background-image:none
}
.ez-toc-btn-default {
text-shadow:0 1px 0 #fff;
background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);
background-repeat:repeat-x;
border-color:#dbdbdb;
border-color:#ccc
}
.ez-toc-btn-default:focus,.ez-toc-btn-default:hover {
background-color:#e0e0e0;
background-position:0 -15px
}
.ez-toc-btn-default.active,.ez-toc-btn-default:active {
background-color:#e0e0e0;
border-color:#dbdbdb
}
.ez-toc-pull-right {
float:right!important;
margin-left:10px
}
结果
效果就如本文章开头。