您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 Confluence 文章页面上浮动展示文章目录,并支持展开和折叠功能
当前为
- // ==UserScript==
- // @name Confluence Floating TOC
- // @namespace http://tampermonkey.net/
- // @version 2.1
- // @description 在 Confluence 文章页面上浮动展示文章目录,并支持展开和折叠功能
- // @author mkdir700
- // @match https://*.atlassian.net/wiki/*
- // @grant none
- // @license MIT
- // ==/UserScript==
- // 递归处理已有的 TOC,重新生成新的 TOC
- function genertateTOCFromExistingToc(toc) {
- if (toc.textContent === '') {
- return;
- }
- let currUl = document.createElement('ul');
- currUl.id = 'floating-toc-ul';
- for (let i = 0; i < toc.children.length; i++) {
- // li > span > a > span > span
- var headerTextElement = toc.children[i].querySelector('span > a > span > span');
- if (!headerTextElement) {
- continue;
- }
- var headerText = headerTextElement.textContent;
- // 创建目录项
- var tocItem = document.createElement('li');
- // 创建链接
- var tocLink = document.createElement('a');
- tocLink.textContent = headerText;
- // 使用标题的 id 作为 URL 片段
- // 标题中的空格需要替换为 -,并且转为小写
- tocLink.href = '#' + headerText.replace(/\s/g, '-');
- tocItem.appendChild(tocLink);
- // 如果有子目录,递归处理
- var childUl = toc.children[i].querySelector('ul');
- if (childUl) {
- var newUl = genertateTOCFromExistingToc(childUl);
- if (newUl) {
- tocItem.appendChild(newUl);
- }
- }
- currUl.appendChild(tocItem);
- }
- return currUl;
- }
- function getExistingToc() {
- return document.querySelector('[data-testid="list-style-toc-level-container"]');
- }
- function generateTOCFormPage() {
- // 创建目录列表
- var tocList = document.createElement('ul');
- tocList.id = 'floating-toc-ul';
- // 获取所有标题
- var headers = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
- headers.forEach(function (header) {
- // 过滤掉 id 为空的标题
- if (header.textContent === '') {
- return;
- }
- // 检查是否有属性 data-item-title
- if (header.hasAttribute('data-item-title')) {
- return;
- }
- // 检查属性 data-testid 是否等于 title-text
- if (header.getAttribute('data-testid') === 'title-text') {
- return;
- }
- if (header.id === 'floating-toc-title') {
- return;
- }
- // class 为 'cc-te0214' 的标题不需要显示在目录中
- if (header.className === 'cc-te0214') {
- return;
- }
- // 创建目录项
- var tocItem = document.createElement('li');
- tocItem.style.marginLeft = (parseInt(header.tagName[1]) - 1) * 10 + 'px'; // 根据标题级别缩进
- // 创建链接
- var tocLink = document.createElement('a');
- tocLink.textContent = header.textContent;
- // 使用标题作为 URL 片段
- tocLink.href = '#' + header.textContent.replace(/\s/g, '-');
- tocItem.appendChild(tocLink);
- // 将目录项添加到目录列表中
- tocList.appendChild(tocItem);
- });
- return tocList;
- }
- function buildToggleButton() {
- // 添加折叠/展开按钮
- var toggleButton = document.createElement('button');
- toggleButton.textContent = '折叠';
- toggleButton.style.position = 'fixed';
- toggleButton.style.top = '200px';
- toggleButton.style.right = '0';
- toggleButton.style.backgroundColor = '#007bff';
- toggleButton.style.color = '#fff';
- toggleButton.style.border = 'none';
- toggleButton.style.padding = '5px';
- toggleButton.style.cursor = 'pointer';
- var isCollapsed = false;
- // 折叠和展开功能
- toggleButton.addEventListener('click', function () {
- var tocContainer = document.getElementById('floating-toc-container');
- if (isCollapsed) {
- tocContainer.style.visibility = 'visible';
- toggleButton.textContent = '折叠';
- } else {
- tocContainer.style.visibility = 'hidden';
- toggleButton.textContent = '展开';
- }
- isCollapsed = !isCollapsed;
- });
- return toggleButton;
- }
- function buildToc() {
- // 创建浮动目录的容器
- var tocContainer = document.createElement('div');
- tocContainer.id = 'floating-toc-container';
- tocContainer.style.width = '200px';
- tocContainer.style.backgroundColor = '#fff';
- tocContainer.style.border = '1px solid #ccc';
- tocContainer.style.padding = '10px';
- tocContainer.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)';
- tocContainer.style.zIndex = '4';
- tocContainer.style.fontSize = '14px';
- // 添加隐藏滚动条样式
- var style = document.createElement('style');
- style.innerHTML = `
- #floating-toc-container {
- scrollbar-width: none;
- -ms-overflow-style: none;
- }
- #floating-toc-container::-webkit-scrollbar {
- display: none;
- }
- `;
- document.head.appendChild(style);
- return tocContainer;
- }
- function generateTOC() {
- // 检查是否存在已有的 TOC
- var existingTOC = getExistingToc();
- var toc;
- if (existingTOC) {
- toc = genertateTOCFromExistingToc(existingTOC);
- }
- if (toc === undefined || !toc) {
- toc = generateTOCFormPage();
- }
- toc.style.position = 'relative';
- toc.style.listStyle = 'none';
- toc.style.padding = '0';
- return toc
- }
- function updateMaxHeight(tocContainer) {
- const viewportHeight = window.innerHeight;
- const topOffset = parseFloat(tocContainer.style.top);
- tocContainer.style.maxHeight = (viewportHeight - topOffset - 20) + 'px'; // 20px 为一些额外的间距
- }
- (function () {
- 'use strict';
- var container = document.createElement('div');
- container.id = 'floating-toc-div';
- container.style.position = 'fixed';
- container.style.right = '0';
- container.style.top = '200px'; // 设置为 200px
- container.style.maxHeight = 'calc(100vh - 400px)';
- container.style.overflowY = 'auto';
- // 添加隐藏滚动条样式
- var style = document.createElement('style');
- style.innerHTML = `
- #floating-toc-div {
- scrollbar-width: none;
- -ms-overflow-style: none;
- }
- #floating-toc-div::-webkit-scrollbar {
- display: none;
- }
- `;
- document.head.appendChild(style);
- document.body.appendChild(container);
- var tocContainer = buildToc();
- container.appendChild(tocContainer);
- // 添加折叠/展开按钮
- const toggleButton = buildToggleButton();
- container.appendChild(toggleButton);
- function onChange() {
- var tocList;
- tocList = document.getElementById('floating-toc-ul');
- if (tocList) {
- tocList.remove();
- }
- tocList = generateTOC(tocContainer);
- tocContainer.appendChild(tocList);
- // 动态计算最大高度
- updateMaxHeight(tocContainer);
- }
- onChange();
- var latestMainContent;
- var latestEditorTextarea;
- window.addEventListener('load', function () {
- const checkMainContentExistence = setInterval(function() {
- const mainContent = document.getElementById('main-content');
- if (mainContent) {
- if (latestMainContent === mainContent) {
- return;
- }
- onChange();
- }
- }, 1000);
- // 轮询检查 ak-editor-textarea 是否存在
- const checkTextareaExistence = setInterval(function() {
- const editorTextarea = document.getElementById('ak-editor-textarea');
- if (editorTextarea) {
- if (latestEditorTextarea === editorTextarea) {
- return;
- }
- onChange();
- }
- }, 1000);
- });
- // 确保目录在滚动时保持在视口内
- window.addEventListener('scroll', function () {
- updateMaxHeight(tocContainer);
- });
- })();