简书markdown如何实现侧边⽬录效果图
效果⼤概如下图所⽰
效果图.png
本来markdown语法是⽀持标题⽣成⽬录的,但是简书的并没有⽀持。所以就想到⽤脚本解决了。
⽅法
1、⾕歌浏览器安装ta m perm o nk ey
permo
打开扩展程序页⾯
(2) 打开扩展程序页⾯的“开发者模式”
打开开发者模式(3) 将crx⽂件拖拽到扩展程序页⾯,完成安装
完成安装2、c lo ne脚本
打开添加脚本页⾯下⾯脚本来⾃GitHub lxx2013/utils
// ==UserScript==
// @name 简书⽹站左侧⽬录⽣成
// @namespace tampermonkey/
// @version 1.0.0
// @description 简书⽹站左侧⽬录⽣成,⽀持⾮h1标题,⽀持滚动
// @author github/lxx2013
// @match www.jianshu/p/*
// @match www.jianshu/p/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
initSidebar('.sidebar', '.post');
})();
/**
* 简书⽹站左侧⽬录⽣成插件
* 代码参考了 github//blob/master/themes/vue/source/js/common.js
* @param {string} sidebarQuery - ⽬录 Element 的 query 字符串
* @param {string} contentQuery - 正⽂ Element 的 query 字符串
*/
function initSidebar(sidebarQuery, contentQuery) {
addAllStyle()
var body = document.body
var sidebar = document.querySelector(sidebarQuery)
// 在 body 标签内部添加 div.sidebar 侧边栏,⽤于显⽰⽂档⽬录
if (!sidebar) {
sidebar = ateElement('div')
body.insertBefore(sidebar, body.firstChild)
}
sidebar.classList.add('sidebar')
var content = document.querySelector(contentQuery)
if (!content) {
throw ('Error: content not find!')
return
}
content.classList.add('content-with-sidebar');
var ul = ateElement('ul')
ul.classList.add('menu-root')
sidebar.appendChild(ul)
var allHeaders = []
// 遍历⽂章中的所有 h1或 h2(取决于最⼤的 h 是多⼤) , 编辑为li.h3插⼊ ul
//因为标题⼀定是 h1 所以优先处理,然后再看⽂章正⽂部分是以 h1作为⼀级标题还是 h2或 h3作为⼀级标题 //采⽤的⽅法是优先遍历正⽂, 然后再插⼊标题这个h1
var i = 1
var headers = [].slice.call(content.querySelectorAll('h' + i++), 1)
while (!headers.length && i <= 6) {
headers = Array.from(content.querySelectorAll('h' + i++))
}
[].unshift.call(headers, content.querySelector('h1'))
if (headers.length) {
[].forEach.call(headers, function (h) {
var h1 = makeLink(h, 'a', 'h1-link')
ul.appendChild(h1)
allHeaders.push(h)
//寻h1的⼦标题
var h2s = collectHs(h)
if (h2s.length) {
[].forEach.call(h2s, function (h2) {
allHeaders.push(h2)
var h3s = collectHs(h2)
h2 = makeLink(h2, 'a', 'h2-link')
ul.appendChild(h2)
//再寻 h2 的⼦标题 h3
if (h3s.length) {
var subUl = ateElement('ul')
subUl.classList.add('menu-sub')
h2.appendChild(subUl)
;[].forEach.call(h3s, function (h3) {
allHeaders.push(h3)
h3 = makeLink(h3, 'a', 'h3-link')
subUl.appendChild(h3)
})
}
})
}
})
}
/
/增加 click 点击处理,使⽤ scrollIntoView,增加控制滚动的 flag
var scrollFlag = 0
var scrollFlagTimer
sidebar.addEventListener('click', function (e) {
e.preventDefault()
if (e.target.href) {
scrollFlag = 1
clearTimeout(scrollFlagTimer)
scrollFlagTimer = setTimeout(() => scrollFlag = 0, 1500)
setActive(e.target, sidebar)
var target = ElementById(Attribute('href').slice(1))
target.scrollIntoView({ behavior: 'smooth', block: "center" })
}
})
//监听窗⼝的滚动和缩放事件
window.addEventListener('scroll', updateSidebar)
window.addEventListener('resize', throttle(updateSidebar))
function updateSidebar() {
if (scrollFlag)
return
var doc = document.documentElement
var top = doc && doc.scrollTop || document.body.scrollTop
if (!allHeaders.length) return
var last
for (var i = 0; i < allHeaders.length; i++) {
var link = allHeaders[i]
if (link.offsetTop > (top + document.body.clientHeight / 2 - 73)) {
tampermonkeyif (!last) { last = link }
break
} else {
last = link
}
}
if (last) {
setActive(last.id, sidebar)
}
}
}
/**
>为正⽂的标题创建⼀个对应的锚,返回的节点格式为`<li><tag class="className"> some text </tag><li>` @param {HTMLElement} h - 需要在⽬录中为其创建链接的⼀个标题,它的`NodeType`可能为`H1 | H2 | H3` @param {string} tag - 返回的 li 中的节点类型, 默认为 a
@param {string} className - 返回的 tag 的 class ,默认为空
@returns {HTMLElement} 返回的节点格式为`<li><a> some text </a><li>`
*/
function makeLink(h, tag, className) {
tag = tag || 'a'
className = className || ''
var link = ateElement('li')
var text = [].slice.call(h.childNodes).map(function (node) {
if (deType === Node.TEXT_NODE) {
deValue
} else if (['CODE', 'SPAN', 'A'].indexOf(node.tagName) !== -1) {
Content
} else {
return ''
}
}).join('').replace(/\(.*\)$/, '')
if (!h.id) h.id = IdEscape(text)
link.innerHTML =
`<${tag} class="${className}" href="#${h.id}">${htmlEscape(text)}</${tag}>`
return link
}
/**
*对 id 进⾏格式化.把空⽩字符和引号转义为下划线
*>注意:id值使⽤字符时,除了 ASCII字母和数字、“—”、“-"、"."之外,可能会引起兼容性问题,因为在HTML4中是不允许包含这些字符的,这个限制在HTML5中更加严格,为了*>但是本程序中使⽤了 ElementById 的要求稍放宽了⼀些,"#3.1_createComponent"这样的 id能成功执⾏
@param {string} text - HTML特殊字符
@returns {string} 转义后的字符串,例如`# 1'2"3标题`被转义为`#_1_2_3标题`
*/
function IdEscape(text) {
place(/[\s"']/g, '_') //注意这⾥不加 g 的话就会只匹配第⼀个匹配,所以会出错
}
/**
>HTML 特殊字符[ &, ", ', <, > ]转义
@param {string} text - HTML特殊字符
@returns {string} 转义后的字符,例如`<`被转义为`<`
*/
function htmlEscape(text) {
return text
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>')
}
/**
*为⼀个 `h(x)`标题节点收集跟在它屁股后⾯的 `h(x+1)`标题节点,
>若屁股后⾯没有`h(x+1)`节点,则收集`h(x+2)`节点甚⾄`h(x+3)`,毕竟不知道⽂章作者喜欢⽤哪种⼤⼩做标题
>收集过程中若遇到 `h(x)或h(x-1)`节点的话要⽴即返回
@param {HTMLElement} h - HTML 标题节点 `H1~H6`
@returns {HTMLElement[]} ⼀个由 h(x+1)或 h(x+2)等后代⽬录节点组成的数组
*/
function collectHs(h) {
var childIndexes = []
var thisTag = h.tagName
var count = 1
do {
var childTag = h.tagName[0] + (parseInt(h.tagName[1]) + count++)
var next = h.nextElementSibling
while (next) {
if (next.tagName[0] == 'H' && next.tagName[1] <= thisTag[1]) {
break
}
else if (next.tagName === childTag) {
childIndexes.push(next)
}
next = ElementSibling
}
} while (childTag < 'H6' && childIndexes.length == 0)
return childIndexes
}
/**
*设置⽬录的激活状态,按既定规则添加 active 和 current 类
*>⽆论对h2还是 h3进⾏操作,⾸先都要移除所有的 active 和 current 类, 然后对 h2添加 active 和 current, 或对 h3添加 active 对其⽗⽬录添加 current
@param {String|HTMLElement} id - HTML标题节点或 querySelector 字符串
@param {HTMLElement} sidebar - 边栏的 HTML 节点
*/
function setActive(id, sidebar) {
//1.⽆论对h2还是 h3进⾏操作,⾸先都要移除所有的 active 和 current 类,
var previousActives = sidebar.querySelectorAll(`.active`)
;[].forEach.call(previousActives, function (h) {
ve('active')
})
previousActives = sidebar.querySelectorAll(`.current`)
;[].forEach.call(previousActives, function (h) {
ve('current')
})
//获取要操作的⽬录节点
var currentActive = typeof id === 'string'
sidebar.querySelector('a[href="#' + id + '"]')
: id
if (ains('h2-link') != -1) {
/
/2. 若为 h2,则添加 active 和 current
currentActive.classList.add('active', 'current')
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论