您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
display a fixed sider on the top-right of notion page which has a table of content block inside
// ==UserScript== // @name notion TOC sider & go to top // @namespace https://www.notion.so/ // @version 0.0.4 // @description display a fixed sider on the top-right of notion page which has a table of content block inside // @author nan chen // @match *://www.notion.so/* // @icon https://www.google.com/s2/favicons?domain=notion.so // @grant none // @run-at document-idle // @license MIT // ==/UserScript== (function () { setTimeout(function () { var tocEle = document.querySelector( '.notion-table_of_contents-block' ); var parent = document.querySelector('#notion-app'); function refresh() { var tocMenu = document.querySelector('#notion-toc-menu-sider'); var closeButton = document.querySelector( '#notion-toc-menu-sider-close-button' ); if (tocMenu) { parent.removeChild(tocMenu); } if (closeButton) { parent.removeChild(closeButton); } } function buildSider() { var headings = []; var parseLevel = function (outerHtml) { if (outerHtml.includes('margin-left: 24px')) { return 2; } if (outerHtml.includes('margin-left: 48px')) { return 3; } return 1; }; var tocEleToBuild = document.querySelector( '.notion-table_of_contents-block' ); if (!tocEleToBuild) return; tocEleToBuild.childNodes[0].childNodes.forEach(function (n) { if (n.nodeType === 1) { var an = n.querySelector('a'); headings.push({ title: n.innerText, level: parseLevel(n.outerHTML), href: an.href, }); } }); var menu = document.createElement('aside'); menu.id = 'notion-toc-menu-sider'; var isExpanded = true; parent.appendChild(menu); menu.style.minWidth = '120px'; menu.style.maxHeight = '600px'; menu.style.overflowY = 'auto'; menu.style.position = 'fixed'; menu.style.top = '55px'; menu.style.right = '10px'; menu.style.zIndex = 100000; menu.style.boxShadow = 'rgb(15 15 15 / 5%) 0px 0px 0px 1px, rgb(15 15 15 / 10%) 0px 3px 6px, rgb(15 15 15 / 20%) 0px 9px 24px'; var menuList = document.createElement('div'); menuList.style.maxWidth = '160px'; menuList.style.backgroundColor = '#fff'; menuList.style.padding = '10px'; menu.appendChild(menuList); var closeButtonWrapper = document.createElement('div'); closeButtonWrapper.id = 'notion-toc-menu-sider-close-button'; var closeButton = document.createElement('div'); closeButtonWrapper.style.borderRadius = '12px'; closeButtonWrapper.style.backgroundColor = '#fff'; closeButtonWrapper.style.width = '24px'; closeButtonWrapper.style.height = '24px'; closeButtonWrapper.style.cursor = 'pointer'; closeButtonWrapper.style.zIndex = 100000; closeButtonWrapper.style.position = 'absolute'; closeButtonWrapper.style.top = '30px'; closeButtonWrapper.style.right = '10px'; closeButton.style.width = '10px'; closeButton.style.height = '10px'; closeButton.style.borderLeft = '1px solid black'; closeButton.style.borderBottom = '1px solid black'; closeButton.style.transform = 'rotate(-45deg)'; closeButton.style.position = 'absolute'; closeButton.style.top = '10px'; closeButton.style.left = '7px'; parent.appendChild(closeButtonWrapper); closeButtonWrapper.appendChild(closeButton); setTimeout(function () { closeButton.addEventListener('click', function () { if (!isExpanded) { closeButton.style.transform = 'rotate(-45deg)'; menu.style.display = 'block'; } else { closeButton.style.transform = 'rotate(135deg)'; menu.style.display = 'none'; } isExpanded = !isExpanded; }); }); var lists = headings.map(function (h) { var anchor = document.createElement('a'); anchor.innerText = h.title; anchor.style.fontSize = '10px'; anchor.href = h.href; anchor.style.cursor = 'pointer'; anchor.style.display = 'block'; anchor.style.textOverflow = 'ellipsis'; anchor.style.whiteSpace = 'nowrap'; anchor.style.overflow = 'hidden'; anchor.style.color = 'rgb(141,141,141)'; switch (h.level) { case 1: break; case 2: anchor.style.marginLeft = '16px'; break; case 3: anchor.style.marginLeft = '24px'; break; default: break; } return anchor; }); lists.forEach(function (l) { menuList.appendChild(l); }); /**** to top ******/ var topAnchor = document.createElement('a'); topAnchor.name = 'top'; parent.prepend(topAnchor); var toTop = document.createElement('div'); toTop.className = 'to-top'; toTop.innerHTML = `<a id="go-to-top" href="#top" target="_Self"></a><svg class="to-top-button" role="button" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" style="cursor: pointer; position: fixed; bottom: 5rem; right: 2rem; width: 2rem; color: rgb(235, 235, 235); background-color: #fff; border-radius: 50%; overflow: hidden; z-index: 1; fill: currentcolor;" > <path d="M512 0C229.517 0 0 229.517 0 512s227.752 512 512 512c282.483 0 512-227.752 512-512C1024 229.517 794.483 0 512 0zM351.338 271.89h305.434c14.125 0 26.483 12.358 26.483 26.482s-12.358 26.483-26.483 26.483H351.338c-14.124 0-26.483-12.358-26.483-26.483 0-15.89 12.359-26.482 26.483-26.482z m331.917 303.669c-12.358 12.358-33.545 12.358-45.903 0L531.42 471.393v270.124c0 14.124-12.359 26.483-26.483 26.483s-26.483-12.359-26.483-26.483v-271.89l-105.93 104.166c-12.36 12.359-33.546 12.359-45.904 0-12.359-12.359-12.359-31.78 0-45.903l155.365-151.835c7.062-7.062 14.124-8.827 22.952-8.827s15.89 3.53 22.952 8.827L683.255 527.89c12.359 15.89 12.359 35.31 0 47.669z" fill="currentColor" ></path> </svg>`; parent.appendChild(toTop); var toTopButton = document.querySelector('.to-top-button'); document.addEventListener('scroll', scrollFunction); function scrollFunction() { console.log('scrolling'); if ( document.body.scrollTop > 20 || document.documentElement.scrollTop > 20 ) { toTopButton.style.display = 'block'; } else { toTopButton.style.display = 'none'; } } toTopButton.addEventListener('click', function (e) { document.querySelector('#go-to-top').click(); }); } buildSider(); var lastUrlWithoutHash = location.origin + location.pathname; var lastUrl = location.href; new MutationObserver(() => { var url = location.href; const urlWithoutHash = location.origin + location.pathname; if (lastUrlWithoutHash !== urlWithoutHash) { lastUrl = url; lastUrlWithoutHash = urlWithoutHash; onUrlChange(); } }).observe(document, { subtree: true, childList: true }); function onUrlChange() { setTimeout(function () { var toc = document.querySelector( '.notion-table_of_contents-block' ); refresh(); if (toc) { buildSider(); } }, 0); // console.log('change'); } }, 2000); })();