- // ==UserScript==
- // @name TreeBar
- // @name:zh-CN 目录树导航
- // @namespace https://github.com/zhilidali/TreeBar/
- // @version 0.1.11
- // @description 目录树导航 - 显示文章目录大纲导航
- // @description:zh-cn 目录树导航 - 显示文章目录大纲导航
- // @author zhilidali
- // @mail zhilidali@qq.com
- // @license MIT Licensed
- // @match *://www.jianshu.com/p/*
- // @match *://cdn2.jianshu.io/p/*
- // @match *://juejin.im/post/*
- // @match *://juejin.im/entry/*
- // @match *://sspai.com/*
- // @match *://zhuanlan.zhihu.com/p/*
- // @match *://mp.weixin.qq.com/s?*
- // @match *://cnodejs.org/topic/*
- // @match *://div.io/topic/*
- // @match *://www.zcfy.cc/article/*
- // @grant none
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- var map = {
- jianshu: {
- tagName: '.ouvJEz',
- style: {
- top: '55px',
- color: '#ea6f5a',
- }
- },
- zhihu: {
- tagName: '.Post-RichText',
- },
- sspai: {
- tagName: '.notion-page-content',
- },
- juejin: {
- tagName: '.article-content',
- },
- div: {
- tagName: '.topic-firstfloor-detail'
- },
- zcfy: {
- tagName: '.markdown-body',
- },
- qq: {
- tagName: '.rich_media_content',
- },
- default: {
- tagName: 'body'
- }
- };
- var treeBar = window.treeBar = {
- site: {
- name: '',
- tagName: '',
- },
- className: 'treeBar',
- style: `
- /* 样式重置 */
- html { scroll-behavior: smooth; }
- .treeBar ul { padding-left: 1.1em; margin: 0; }
- .treeBar > ul > li { list-style-type: disc; }
- .treeBar > ul > li > ul > li { list-style-type: circle; }
- .treeBar > ul > li > ul > li > ul > li { list-style-type: square; }
- /* common */
- .treeBar {
- z-index: 99999; position: fixed; top: 10%; right: 0; max-width: 300px; max-height: 88%;
- border: 1px solid #ddd; overflow-y: auto; overflow-x: visible; background-color: rgba(255, 255, 255, .9);
- }
- .treeBar-resize {
- position: absolute; /*cursor: col-resize;*/ width: 5px; left: -2px; top: 0; bottom: 0;
- }
- .treeBar-btn {
- box-sizing: border-box; position: absolute; top: -1px; left: -1px; width: 72px; height: 28px;
- padding: 0; border: 1px solid #ddd; border-radius: 3px; box-shadow: 0 1px 1px 1px #ddd;
- font-size: 14px; background-color: #fff; vertical-align: middle; text-align: center;
- outline: none; cursor: pointer; color: #333;
- }
- .treeBar > ul {
- padding: 30px 10px 10px 25px;
- }
- .treeBar > ul > li a {
- line-height: 30px; /*overflow: hidden; white-space: nowrap; text-overflow: ellipsis;*/
- text-decoration: none; font-size: 14px; cursor: pointer; color: #0371e9;
- }
- .treeBar > ul > li a:hover {
- text-decoration: underline;
- }
- /* slideToggle */
- .treeBar-slide { overflow-y: visible; }
- .treeBar-slide .treeBar-btn { left: -71px; top: -1px; }
- .treeBar-slide > ul { display: none; }`,
- innerDom: `<button class="treeBar-btn">TreeBar</button><div class="treeBar-resize"></div>`,
- matchSite: function() {/* 匹配站点 */
- var domain = location.href.match(/([\d\w]+)\.(com|cn|net|org|im|io|cc)/i);
- this.site.name = (domain && domain[1]);
- var siteInfo = map[this.site.name] || map.default;
- this.site.tagName = siteInfo.tagName;
- this.site.sync = siteInfo.sync || false;
- if (siteInfo.style) {
- this.style += `
- .treeBar {
- top: ${siteInfo.style.top};
- border-color: ${siteInfo.style.color};
- }
- .treeBar-btn {
- border-color: ${siteInfo.style.color};
- background-color: ${siteInfo.style.color};
- color: #fff;
- }
- .treeBar > ul > li a {
- color: ${siteInfo.style.color};
- }
- `;
- }
- },
- createDom: function() {/* 创建DOM */
- 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);
- },
- onEvent: function() {
- var eTree = document.querySelector('.treeBar'),
- eBtn = document.querySelector('.treeBar-btn'),
- eResize = document.querySelector('.treeBar-resize');
- eBtn.onclick = function () {
- eTree.classList.toggle('treeBar-slide');
- };
- /*var resize = {};
- document.body.onmouseup = function() {
- console.log('up');
- resize.flag = false;
- };
- eResize.onmousedown = function() {
- resize.flag = true;
- };
- document.body.onmousemove = function() {
- if (!resize.flag) return;
- if (resize.current === undefined) {
- resize.current = event.x;
- return;
- }
- var x = event.x - resize.current;
- if(Math.abs(x) >= 10) {
- tree.style.width = tree.offsetWidth - x + 'px';
- console.log(x);
- resize.current = event.x;
- }
- };*/
- },
- init: function() {
- console.time('TreeBar');
-
- // 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');
- }
- };
-
- // 0. 匹配站点
- treeBar.matchSite();
- if (treeBar.site.tagName !== 'body' && document.querySelector(treeBar.site.tagName)) {
- treeBar.init();
- } else {
- document.onreadystatechange = function () {
- document.readyState === "complete" && treeBar.init();
- };
- }
-
-
- /* 解析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) {
- arr.push({
- name: obj.innerText,
- id: obj.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 +=
- `<li>
- <a href="#${item.id}" title="${item.name}">${item.name}</a>${ul}
- </li>`;
- });
- return `<ul>${list}</ul>`;
- }
-
- })();