簡(jiǎn)書(shū)-文章左側(cè)目錄的生成和適配(日間/夜間)

簡(jiǎn)書(shū)目前不支持頁(yè)面內(nèi)索引,也就無(wú)法創(chuàng)建有效的目錄
辦法總比困難多,通過(guò)度娘,找到了以下兩篇文章[1],[2]
在這兩篇文章的基礎(chǔ)上,結(jié)合自己的使用習(xí)慣,做了一些適配,完善了一些備注,希望可以幫助到有需要的朋友

效果展示

日間模式

日間模式.gif

夜間模式

夜間模式.gif

下載插件

瀏覽器(建議使用google、火狐瀏覽器)安裝油猴插件(Tampermonkey)提取碼:4xge

新建腳本

插件安裝好之后==》打開(kāi)插件功能菜單==》添加新腳本


添加新腳本

跳轉(zhuǎn)到腳本編輯界面


腳本編輯界面

日間模式效果圖和對(duì)應(yīng)的代碼

// ==UserScript==
// @name         簡(jiǎn)書(shū)網(wǎng)站左側(cè)目錄生成(日間模式)
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  簡(jiǎn)書(shū)網(wǎng)站左側(cè)目錄生成,支持非h1標(biāo)題,支持滾動(dòng)
// @author       https://github.com/lxx2013
// @match        http://www.itdecent.cn/p/*
// @match        http://www.itdecent.cn/p/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';
    initSidebar('.sidebar', '.post');
})();

// 使文章區(qū)域?qū)挾冗m配屏幕
let wider = $('.note').width() - 400;
let oriWidth = $('.post').width();
console.log(wider);
    console.log(oriWidth);
    if (wider < oriWidth) {
       wider = oriWidth;
    }
// 適配寬度
$('.post').width(wider);
// 將文章內(nèi)容右移
$('.post').css('padding-left','200px');

/**
* 簡(jiǎn)書(shū)網(wǎng)站左側(cè)目錄生成插件
* 代碼參考了 https://github.com/vuejs/vuejs.org/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 標(biāo)簽內(nèi)部添加 div.sidebar 側(cè)邊欄,用于顯示文檔目錄
    if (!sidebar) {
        sidebar = document.createElement('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 = document.createElement('ul')
    ul.classList.add('menu-root')
    sidebar.appendChild(ul)

    var allHeaders = []
    // 遍歷文章中的所有 h1或 h2(取決于最大的 h 是多大) , 編輯為li.h3插入 ul
    //因?yàn)闃?biāo)題一定是 h1 所以優(yōu)先處理,然后再看文章正文部分是以 h1作為一級(jí)標(biāo)題還是 h2或 h3作為一級(jí)標(biāo)題
    //采用的方法是優(yōu)先遍歷正文, 然后再插入標(biāo)題這個(gè)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的子標(biāo)題
            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 的子標(biāo)題 h3
                    if (h3s.length) {
                        var subUl = document.createElement('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 點(diǎn)擊處理,使用 scrollIntoView,增加控制滾動(dòng)的 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 = document.getElementById(e.target.getAttribute('href').slice(1))
            target.scrollIntoView({ behavior: 'smooth', block: "center" })
        }
    })
    //監(jiān)聽(tīng)窗口的滾動(dòng)和縮放事件
    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)) {
                if (!last) { last = link }
                break
            } else {
                last = link
            }
        }
        if (last) {
            setActive(last.id, sidebar)
        }
    }
}

/**
>為正文的標(biāo)題創(chuàng)建一個(gè)對(duì)應(yīng)的錨,返回的節(jié)點(diǎn)格式為`<li><tag class="className"> some text </tag><li>`
@param {HTMLElement} h - 需要在目錄中為其創(chuàng)建鏈接的一個(gè)標(biāo)題,它的`NodeType`可能為`H1 | H2 | H3`
@param {string} tag - 返回的 li 中的節(jié)點(diǎn)類(lèi)型, 默認(rèn)為 a
@param {string} className - 返回的 tag 的 class ,默認(rèn)為空
@returns {HTMLElement} 返回的節(jié)點(diǎn)格式為`<li><a> some text </a><li>`
*/
function makeLink(h, tag, className) {
    tag = tag || 'a'
    className = className || ''
    var link = document.createElement('li')
    var text = [].slice.call(h.childNodes).map(function (node) {
        if (node.nodeType === Node.TEXT_NODE) {
            return node.nodeValue
        } else if (['CODE', 'SPAN', 'A'].indexOf(node.tagName) !== -1) {
            return node.textContent
        } else {
            return ''
        }
    }).join('').replace(/\(.*\)$/, '')
    if (!h.id) h.id = IdEscape(text)
    link.innerHTML =
        `<${tag} class="${className}" href="#${h.id}">${htmlEscape(text)}</${tag}>`
    return link
}

/**
*對(duì) id 進(jìn)行格式化.把空白字符和引號(hào)轉(zhuǎn)義為下劃線
*>注意:id值使用字符時(shí),除了 ASCII字母和數(shù)字、“—”、“-"、"."之外,可能會(huì)引起兼容性問(wèn)題,因?yàn)樵贖TML4中是不允許包含這些字符的,這個(gè)限制在HTML5中更加嚴(yán)格,為了兼容性id值必須由字母開(kāi)頭,同時(shí)不允許其中有空格。參考https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/id
*>但是本程序中使用了 document.getElementById 的要求稍放寬了一些,"#3.1_createComponent"這樣的 id能成功執(zhí)行
@param {string} text - HTML特殊字符
@returns {string} 轉(zhuǎn)義后的字符串,例如`# 1'2"3標(biāo)題`被轉(zhuǎn)義為`#_1_2_3標(biāo)題`
*/
function IdEscape(text) {
    return text.replace(/[\s"']/g, '_') //注意這里不加 g 的話就會(huì)只匹配第一個(gè)匹配,所以會(huì)出錯(cuò)
}
/**
>HTML 特殊字符[ &, ", ', <, > ]轉(zhuǎn)義
@param {string} text - HTML特殊字符
@returns {string} 轉(zhuǎn)義后的字符,例如`<`被轉(zhuǎn)義為`&lt`
*/
function htmlEscape(text) {
    return text
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
}
/**
*為一個(gè) `h(x)`標(biāo)題節(jié)點(diǎn)收集跟在它屁股后面的 `h(x+1)`標(biāo)題節(jié)點(diǎn),
>若屁股后面沒(méi)有`h(x+1)`節(jié)點(diǎn),則收集`h(x+2)`節(jié)點(diǎn)甚至`h(x+3)`,畢竟不知道文章作者喜歡用哪種大小做標(biāo)題
>收集過(guò)程中若遇到 `h(x)或h(x-1)`節(jié)點(diǎn)的話要立即返回
@param {HTMLElement}  h - HTML 標(biāo)題節(jié)點(diǎn) `H1~H6`
@returns {HTMLElement[]} 一個(gè)由 h(x+1)或 h(x+2)等后代目錄節(jié)點(diǎn)組成的數(shù)組
*/
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 = next.nextElementSibling
        }
    } while (childTag < 'H6' && childIndexes.length == 0)
    return childIndexes
}
/**
*設(shè)置目錄的激活狀態(tài),按既定規(guī)則添加 active 和 current 類(lèi)
*>無(wú)論對(duì)h2還是 h3進(jìn)行操作,首先都要移除所有的 active 和 current 類(lèi), 然后對(duì) h2添加 active 和 current, 或?qū)?h3添加 active 對(duì)其父目錄添加 current
@param {String|HTMLElement}  id - HTML標(biāo)題節(jié)點(diǎn)或 querySelector 字符串
@param {HTMLElement} sidebar - 邊欄的 HTML 節(jié)點(diǎn)
*/
function setActive(id, sidebar) {
    //1.無(wú)論對(duì)h2還是 h3進(jìn)行操作,首先都要移除所有的 active 和 current 類(lèi),
    var previousActives = sidebar.querySelectorAll(`.active`)
        ;[].forEach.call(previousActives, function (h) {
            h.classList.remove('active')
        })
    previousActives = sidebar.querySelectorAll(`.current`)
        ;[].forEach.call(previousActives, function (h) {
            h.classList.remove('current')
        })
    //獲取要操作的目錄節(jié)點(diǎn)
    var currentActive = typeof id === 'string'
        ? sidebar.querySelector('a[href="#' + id + '"]')
        : id
    if (currentActive.classList.contains('h2-link') != -1) {
        //2. 若為 h2,則添加 active 和 current
        currentActive.classList.add('active', 'current')
    }
    if ([].indexOf.call(currentActive.classList, 'h3-link') != -1) {
        //3. 若為 h3,則添加 active 且對(duì)其父目錄添加 current
        currentActive.classList.add('active')
        var parent = currentActive
        while (parent && parent.tagName != 'UL') {
            parent = parent.parentNode
        }
        parent.parentNode.querySelector('.h2-link').classList.add('current', 'active')
    }
    //左側(cè)目錄太長(zhǎng)時(shí)的效果
    currentActive.scrollIntoView({ behavior: 'smooth' })
}
/**
>增加 sidebar 需要的全部樣式
@param {string} highlightColor - 高亮顏色, 默認(rèn)為'#c7254e'
*/
function addAllStyle(highlightColor) {
    highlightColor = highlightColor || "#EA6F5A"
    var sheet = newStyleSheet()
    /**
    >創(chuàng)建一個(gè)新的`<style></style>`標(biāo)簽插入`<head>`中
    @return {Object} style.sheet,`它具有方法insertRule`
    */
    function newStyleSheet() {
        var style = document.createElement("style");
        // 對(duì)WebKit hack :(
        style.appendChild(document.createTextNode(""));
        // 將 <style> 元素加到頁(yè)面中
        document.head.appendChild(style);
        return style.sheet;
    }
    var position = 0
    /**
    >添加一條 css 規(guī)則
    @param {string} str - css樣式,也可以是@media
    */
    function addStyle(str) {
        sheet.insertRule(str,position++);
    }
    addStyle(`.sidebar{position:fixed;    z-index: 10;
        top: 61px;
        left: 0;
        bottom: 0;
        overflow-x: hidden;
        overflow-y: auto;
        padding: 40px 20px 60px 30px;
        max-width: 310px;
    }`)
    addStyle(`.menu-root { list-style:none; text-align:left }`)
    // 一級(jí)標(biāo)題樣式
    addStyle(`.menu-root .h1-link{
        display:inline-block;
        color:rgb(51, 51, 51);
        font-family:"source sans pro", "helvetica neue", Arial, sans-serif;
        font-size:17.55px;
        font-weight:600;
        height:22px;
        line-height:22.5px;
        list-style-type:none;
        margin-block-end:11px;
        margin-block-start:11px;
    }`)
    addStyle(`.menu-root .h2-link:hover {
        border-bottom: 2px solid ${highlightColor};
    }`)
    addStyle(`.menu-root .h2-link.current+.menu-sub{
        display:block;
    }`)
    // 二級(jí)標(biāo)題樣式
    addStyle(`.menu-root .h2-link{
        color:rgb(51,51,51);
        cursor:pointer;
        font-family:"source sans pro", "helvetica neue", Arial, sans-serif;
        font-size:15px;
        height:auto;
        line-height:22.5px;
        list-style-type:none;
        text-align:left;
        text-decoration-color:rgb(127, 140, 141);
        text-decoration-line:none;
        text-decoration-style:solid;
        margin-left:12.5px;
    }`)
    addStyle(`.menu-sub {
        padding-left:25px;
        list-style:none;
        display:none;
    }`)
    // 三級(jí)標(biāo)題樣式
    addStyle(`.menu-sub .h3-link{
        color:#333333;
        cursor:pointer;
        display:inline;
        font-family:"source sans pro", "helvetica neue", Arial, sans-serif;
        font-size:12.75px;
        height:auto;
        line-height:19.125px;
        list-style-type:none;
        text-align:left;
        text-decoration-color:rgb(52, 73, 94);
        text-decoration-line:none;
        text-decoration-style:solid;
    }`)
    addStyle(`@media only screen and (max-width : 1300px){
        .content-with-sidebar {
            margin-left:310px !important;
        }
    }`)
    addStyle(`.sidebar .active{
        color:${highlightColor};
        font-weight:700;
    }`)
}
/**
>函數(shù)節(jié)流
>參考https://juejin.im/entry/58c0379e44d9040068dc952f
@param {Fuction} fn - 要執(zhí)行的函數(shù)
*/
function throttle(fn, interval = 300) {
    let canRun = true;
    return function () {
        if (!canRun) return;
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);
            canRun = true;
        }, interval);
    };
}

夜間模式效果圖和對(duì)應(yīng)的代碼

// ==UserScript==
// @name         簡(jiǎn)書(shū)網(wǎng)站左側(cè)目錄生成(夜間模式)
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  簡(jiǎn)書(shū)網(wǎng)站左側(cè)目錄生成,支持非h1標(biāo)題,支持滾動(dòng)
// @author       https://github.com/lxx2013
// @match        http://www.itdecent.cn/p/*
// @match        http://www.itdecent.cn/p/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';
    initSidebar('.sidebar', '.post');
})();

// 使文章區(qū)域?qū)挾冗m配屏幕
let wider = $('.note').width() - 400;
let oriWidth = $('.post').width();
console.log(wider);
    console.log(oriWidth);
    if (wider < oriWidth) {
       wider = oriWidth;
    }
// 適配寬度
$('.post').width(wider);
// 將文章內(nèi)容右移
$('.post').css('padding-left','200px');

/**
* 簡(jiǎn)書(shū)網(wǎng)站左側(cè)目錄生成插件
* 代碼參考了 https://github.com/vuejs/vuejs.org/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 標(biāo)簽內(nèi)部添加 div.sidebar 側(cè)邊欄,用于顯示文檔目錄
    if (!sidebar) {
        sidebar = document.createElement('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 = document.createElement('ul')
    ul.classList.add('menu-root')
    sidebar.appendChild(ul)

    var allHeaders = []
    // 遍歷文章中的所有 h1或 h2(取決于最大的 h 是多大) , 編輯為li.h3插入 ul
    //因?yàn)闃?biāo)題一定是 h1 所以優(yōu)先處理,然后再看文章正文部分是以 h1作為一級(jí)標(biāo)題還是 h2或 h3作為一級(jí)標(biāo)題
    //采用的方法是優(yōu)先遍歷正文, 然后再插入標(biāo)題這個(gè)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的子標(biāo)題
            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 的子標(biāo)題 h3
                    if (h3s.length) {
                        var subUl = document.createElement('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 點(diǎn)擊處理,使用 scrollIntoView,增加控制滾動(dòng)的 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 = document.getElementById(e.target.getAttribute('href').slice(1))
            target.scrollIntoView({ behavior: 'smooth', block: "center" })
        }
    })
    //監(jiān)聽(tīng)窗口的滾動(dòng)和縮放事件
    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)) {
                if (!last) { last = link }
                break
            } else {
                last = link
            }
        }
        if (last) {
            setActive(last.id, sidebar)
        }
    }
}

/**
>為正文的標(biāo)題創(chuàng)建一個(gè)對(duì)應(yīng)的錨,返回的節(jié)點(diǎn)格式為`<li><tag class="className"> some text </tag><li>`
@param {HTMLElement} h - 需要在目錄中為其創(chuàng)建鏈接的一個(gè)標(biāo)題,它的`NodeType`可能為`H1 | H2 | H3`
@param {string} tag - 返回的 li 中的節(jié)點(diǎn)類(lèi)型, 默認(rèn)為 a
@param {string} className - 返回的 tag 的 class ,默認(rèn)為空
@returns {HTMLElement} 返回的節(jié)點(diǎn)格式為`<li><a> some text </a><li>`
*/
function makeLink(h, tag, className) {
    tag = tag || 'a'
    className = className || ''
    var link = document.createElement('li')
    var text = [].slice.call(h.childNodes).map(function (node) {
        if (node.nodeType === Node.TEXT_NODE) {
            return node.nodeValue
        } else if (['CODE', 'SPAN', 'A'].indexOf(node.tagName) !== -1) {
            return node.textContent
        } else {
            return ''
        }
    }).join('').replace(/\(.*\)$/, '')
    if (!h.id) h.id = IdEscape(text)
    link.innerHTML =
        `<${tag} class="${className}" href="#${h.id}">${htmlEscape(text)}</${tag}>`
    return link
}

/**
*對(duì) id 進(jìn)行格式化.把空白字符和引號(hào)轉(zhuǎn)義為下劃線
*>注意:id值使用字符時(shí),除了 ASCII字母和數(shù)字、“—”、“-"、"."之外,可能會(huì)引起兼容性問(wèn)題,因?yàn)樵贖TML4中是不允許包含這些字符的,這個(gè)限制在HTML5中更加嚴(yán)格,為了兼容性id值必須由字母開(kāi)頭,同時(shí)不允許其中有空格。參考https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/id
*>但是本程序中使用了 document.getElementById 的要求稍放寬了一些,"#3.1_createComponent"這樣的 id能成功執(zhí)行
@param {string} text - HTML特殊字符
@returns {string} 轉(zhuǎn)義后的字符串,例如`# 1'2"3標(biāo)題`被轉(zhuǎn)義為`#_1_2_3標(biāo)題`
*/
function IdEscape(text) {
    return text.replace(/[\s"']/g, '_') //注意這里不加 g 的話就會(huì)只匹配第一個(gè)匹配,所以會(huì)出錯(cuò)
}
/**
>HTML 特殊字符[ &, ", ', <, > ]轉(zhuǎn)義
@param {string} text - HTML特殊字符
@returns {string} 轉(zhuǎn)義后的字符,例如`<`被轉(zhuǎn)義為`&lt`
*/
function htmlEscape(text) {
    return text
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
}
/**
*為一個(gè) `h(x)`標(biāo)題節(jié)點(diǎn)收集跟在它屁股后面的 `h(x+1)`標(biāo)題節(jié)點(diǎn),
>若屁股后面沒(méi)有`h(x+1)`節(jié)點(diǎn),則收集`h(x+2)`節(jié)點(diǎn)甚至`h(x+3)`,畢竟不知道文章作者喜歡用哪種大小做標(biāo)題
>收集過(guò)程中若遇到 `h(x)或h(x-1)`節(jié)點(diǎn)的話要立即返回
@param {HTMLElement}  h - HTML 標(biāo)題節(jié)點(diǎn) `H1~H6`
@returns {HTMLElement[]} 一個(gè)由 h(x+1)或 h(x+2)等后代目錄節(jié)點(diǎn)組成的數(shù)組
*/
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 = next.nextElementSibling
        }
    } while (childTag < 'H6' && childIndexes.length == 0)
    return childIndexes
}
/**
*設(shè)置目錄的激活狀態(tài),按既定規(guī)則添加 active 和 current 類(lèi)
*>無(wú)論對(duì)h2還是 h3進(jìn)行操作,首先都要移除所有的 active 和 current 類(lèi), 然后對(duì) h2添加 active 和 current, 或?qū)?h3添加 active 對(duì)其父目錄添加 current
@param {String|HTMLElement}  id - HTML標(biāo)題節(jié)點(diǎn)或 querySelector 字符串
@param {HTMLElement} sidebar - 邊欄的 HTML 節(jié)點(diǎn)
*/
function setActive(id, sidebar) {
    //1.無(wú)論對(duì)h2還是 h3進(jìn)行操作,首先都要移除所有的 active 和 current 類(lèi),
    var previousActives = sidebar.querySelectorAll(`.active`)
        ;[].forEach.call(previousActives, function (h) {
            h.classList.remove('active')
        })
    previousActives = sidebar.querySelectorAll(`.current`)
        ;[].forEach.call(previousActives, function (h) {
            h.classList.remove('current')
        })
    //獲取要操作的目錄節(jié)點(diǎn)
    var currentActive = typeof id === 'string'
        ? sidebar.querySelector('a[href="#' + id + '"]')
        : id
    if (currentActive.classList.contains('h2-link') != -1) {
        //2. 若為 h2,則添加 active 和 current
        currentActive.classList.add('active', 'current')
    }
    if ([].indexOf.call(currentActive.classList, 'h3-link') != -1) {
        //3. 若為 h3,則添加 active 且對(duì)其父目錄添加 current
        currentActive.classList.add('active')
        var parent = currentActive
        while (parent && parent.tagName != 'UL') {
            parent = parent.parentNode
        }
        parent.parentNode.querySelector('.h2-link').classList.add('current', 'active')
    }
    //左側(cè)目錄太長(zhǎng)時(shí)的效果
    currentActive.scrollIntoView({ behavior: 'smooth' })
}
/**
>增加 sidebar 需要的全部樣式
@param {string} highlightColor - 高亮顏色, 默認(rèn)為'#c7254e'
*/
function addAllStyle(highlightColor) {
    highlightColor = highlightColor || "#EA6F5A"
    var sheet = newStyleSheet()
    /**
    >創(chuàng)建一個(gè)新的`<style></style>`標(biāo)簽插入`<head>`中
    @return {Object} style.sheet,`它具有方法insertRule`
    */
    function newStyleSheet() {
        var style = document.createElement("style");
        // 對(duì)WebKit hack :(
        style.appendChild(document.createTextNode(""));
        // 將 <style> 元素加到頁(yè)面中
        document.head.appendChild(style);
        return style.sheet;
    }
    var position = 0
    /**
    >添加一條 css 規(guī)則
    @param {string} str - css樣式,也可以是@media
    */
    function addStyle(str) {
        sheet.insertRule(str,position++);
    }
    addStyle(`.sidebar{position:fixed;    z-index: 10;
        top: 61px;
        left: 0;
        bottom: 0;
        overflow-x: hidden;
        overflow-y: auto;
        padding: 40px 20px 60px 30px;
        max-width: 310px;
    }`)
    addStyle(`.menu-root { list-style:none; text-align:left }`)
    // 一級(jí)標(biāo)題樣式
    addStyle(`.menu-root .h1-link{
        display:inline-block;
        color:rgb(200, 200, 200);
        font-family:"source sans pro", "helvetica neue", Arial, sans-serif;
        font-size:17.55px;
        font-weight:600;
        height:22px;
        line-height:22.5px;
        list-style-type:none;
        margin-block-end:11px;
        margin-block-start:11px;
    }`)
    addStyle(`.menu-root .h2-link:hover {
        border-bottom: 2px solid ${highlightColor};
    }`)
    addStyle(`.menu-root .h2-link.current+.menu-sub{
        display:block;
    }`)
    // 二級(jí)標(biāo)題樣式
    addStyle(`.menu-root .h2-link{
        color:rgb(200,200,200);
        cursor:pointer;
        font-family:"source sans pro", "helvetica neue", Arial, sans-serif;
        font-size:15px;
        height:auto;
        line-height:22.5px;
        list-style-type:none;
        text-align:left;
        text-decoration-color:rgb(127, 140, 141);
        text-decoration-line:none;
        text-decoration-style:solid;
        margin-left:12.5px;
    }`)
    addStyle(`.menu-sub {
        padding-left:25px;
        list-style:none;
        display:none;
    }`)
    // 三級(jí)標(biāo)題樣式
    addStyle(`.menu-sub .h3-link{
        color:rgb(200,200,200);
        cursor:pointer;
        display:inline;
        font-family:"source sans pro", "helvetica neue", Arial, sans-serif;
        font-size:12.75px;
        height:auto;
        line-height:19.125px;
        list-style-type:none;
        text-align:left;
        text-decoration-color:rgb(52, 73, 94);
        text-decoration-line:none;
        text-decoration-style:solid;
    }`)
    addStyle(`@media only screen and (max-width : 1300px){
        .content-with-sidebar {
            margin-left:310px !important;
        }
    }`)
    addStyle(`.sidebar .active{
        color:${highlightColor};
        font-weight:700;
    }`)
}
/**
>函數(shù)節(jié)流
>參考https://juejin.im/entry/58c0379e44d9040068dc952f
@param {Fuction} fn - 要執(zhí)行的函數(shù)
*/
function throttle(fn, interval = 300) {
    let canRun = true;
    return function () {
        if (!canRun) return;
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);
            canRun = true;
        }, interval);
    };
}

管理腳本

打開(kāi)油猴插件菜單,找到管理面板


管理面板

結(jié)合簡(jiǎn)書(shū)主題選擇需要開(kāi)關(guān)的腳本


選擇腳本

備注

如果有更好的解決方案,歡迎大家留言討論

參考資料

[1] 簡(jiǎn)書(shū)文章左側(cè)目錄(帶滾動(dòng))
[2] 一步步教你簡(jiǎn)書(shū)左側(cè)自動(dòng)生成目錄(Markdown)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容