// ==UserScript== // @name TreeBar // @namespace https://github.com/zhilidali/ // @version 0.1.1 // @description 目录树导航条 - 显示文章目录大纲导航 // @author zhilidali // @match http://www.jianshu.com/p/* // @match http*://juejin.im/post/* // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; var map = { jianshu: { tagName: '.show-content', style: ` .treeBar { top: 60px; right: 0; border-color: #ea6f5a; } .treeBar-btn { padding: .2em 1em; border-color: #ea6f5a; background-color: #ea6f5a; color: #fff; } .treeBar > ul > li a { color: #ea6f5a; } .treeBar > ul > li a:hover { color: rgb(236, 97, 73); } ` }, juejin: { tagName: '.post-content-container', style: ` .treeBar { top: 60px; right: 0; } .treeBar-btn { padding: .5em 1.5em; border-color: rgba(3, 113, 233, 1); background-color: rgba(3, 113, 233, 1); color: #fff; } .treeBar > ul > li a { color: rgba(20, 20, 20, .8); } .treeBar > ul > li a:hover { color: rgba(20, 20, 20, 1); } ` }, default: { tagName: 'body', style: ` .treeBar { top: 10%; right: 1%; border-color: #555; } .treeBar-btn { padding: .3em 1.2em; background-color: #fff; color: #000; } .treeBar > ul > li a { color: #0366d6; } ` } }; var treeBar = window.treeBar = { site: { name: '', tagName: '', }, className: `treeBar`, style: ` .treeBar { position: fixed; max-width: 300px; max-height: 90%; overflow-y: auto; padding: 10px; border: 1px solid #ddd; background-color: rgba(255, 255, 255, .9); } .treeBar-btn { display: inline-block; border: 1px solid #333; border-radius: 3px; text-align: center; vertical-align: middle; outline: none; cursor: pointer; } .treeBar ul { padding-left: 1em; margin: 0; } .treeBar > ul > li a { line-height: 30px; /*overflow: hidden; white-space: nowrap; text-overflow: ellipsis;*/ text-decoration: none; font-size: 14px; cursor: pointer; } .treeBar > ul > li a:hover { text-decoration: underline; }`, innerDom: `
`, }; /* 匹配站点 */ treeBar.matchSite = function() { var domain = location.href.match(/([\d\w]+)\.(com|cn|im)/i); this.site.name = (domain && domain[1]) || 'default'; var siteInfo = map[this.site.name]; this.site.tagName = siteInfo.tagName; this.style += siteInfo.style; }; /* 创建DOM */ treeBar.createDom = function() { var style = document.createElement('style'), dom = document.createElement('div'); style.innerHTML = this.style; document.head.appendChild(style); dom.className = this.className; dom.innerHTML = this.innerDom + this.ul; document.body.appendChild(dom); }; treeBar.onEvent = function() { document.querySelector('.treeBar-btn').onclick = function () { var ul = document.querySelector('.treeBar > ul'); ul.style.display = ul.style.display === 'none' ? 'block' : 'none'; }; }; document.onreadystatechange = function () { if (document.readyState === "complete") { console.time('TreeBar'); // 0. 匹配站点 treeBar.matchSite(); // 1. 获取DOM var hList = document.querySelector(treeBar.site.tagName) .querySelectorAll('h1, h2, h3, h4, h5, h6'); // 2. 构建数据 var tree = transformTree(Array.from(hList)); // 3. 构建DOM treeBar.ul = compileList(tree); treeBar.createDom(); // 4. 注册事件 treeBar.onEvent(); console.timeEnd('TreeBar'); } }; /* 解析DOM,构建树形数据 */ function transformTree(list) { var result = []; list.reduce(function (res, cur, index, arr) { var prev = res[res.length - 1]; if (compare(prev, cur)) { if (!prev.sub) prev.sub = []; prev.sub.push(cur); if (index === arr.length - 1) prev.sub = transformTree(prev.sub); } else { construct(res, cur); if (prev && prev.sub) prev.sub = transformTree(prev.sub); } return res; }, result); // 转为树形结构的条件依据 function compare(prev, cur) { return prev && cur.tagName.replace(/h/i, '') > prev.tagName.replace(/h/i, ''); } // 转为树形结构后的数据改造 function construct(arr, obj) { obj.id += obj.innerText; arr.push({ name: obj.innerText, id: obj.innerText, tagName: obj.tagName }); } return result; } /* 根据数据构建目录 */ function compileList(tree) { var list = ''; tree.forEach(function(item) { var ul = item.sub ? compileList(item.sub) : ''; list += `
  • ${item.name}${ul}
  • `; }); return ``; } })();