update
This commit is contained in:
@@ -19,8 +19,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
#toc-btn, #toc-sidebar { display: none !important; }
|
|
||||||
html, main { padding: 0 !important; }
|
html, main { padding: 0 !important; }
|
||||||
|
#toc-panel, #toc-btn { display: none !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
html { font-size: 15px; }
|
html { font-size: 15px; }
|
||||||
@@ -28,126 +28,154 @@
|
|||||||
|
|
||||||
/* 目录按钮 */
|
/* 目录按钮 */
|
||||||
#toc-btn {
|
#toc-btn {
|
||||||
position: fixed; left: 10px; top: 50%; transform: translateY(-50%);
|
position: fixed;
|
||||||
width: 30px; height: 50px;
|
left: 10px;
|
||||||
background: #f8f8f8; border: 1px solid #ddd; border-radius: 0 4px 4px 0;
|
top: 10px;
|
||||||
cursor: pointer; z-index: 1000;
|
width: 32px;
|
||||||
display: flex; align-items: center; justify-content: center;
|
height: 32px;
|
||||||
font-size: 14px; color: #666;
|
background: #fff;
|
||||||
opacity: 0; transition: left 0.3s ease, opacity 0.2s;
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 10000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
#toc-btn:hover { background: #eee; }
|
#toc-btn:hover { background: #f5f5f5; }
|
||||||
#toc-btn.visible { opacity: 1; }
|
|
||||||
#toc-btn.open { left: 270px; }
|
|
||||||
|
|
||||||
/* 侧边栏 */
|
/* 目录面板 */
|
||||||
#toc-sidebar {
|
#toc-panel {
|
||||||
position: fixed; left: -260px; top: 0; width: 260px; height: 100vh;
|
position: fixed;
|
||||||
background: #fafafa; border-right: 1px solid #ddd;
|
left: 0;
|
||||||
z-index: 999; transition: left 0.3s ease;
|
top: 0;
|
||||||
padding: 15px; box-sizing: border-box;
|
width: 240px;
|
||||||
|
height: 100vh;
|
||||||
|
background: #fff;
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
z-index: 9999;
|
||||||
|
padding: 50px 15px 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: auto;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
}
|
}
|
||||||
#toc-sidebar.open { left: 0; }
|
#toc-panel.show { transform: translateX(0); }
|
||||||
|
|
||||||
#toc-sidebar h3 {
|
.toc-item { margin: 2px 0; }
|
||||||
margin: 0 0 15px;
|
.toc-link {
|
||||||
font-size: 16px;
|
display: flex;
|
||||||
color: #333;
|
align-items: center;
|
||||||
padding-bottom: 10px;
|
justify-content: space-between;
|
||||||
border-bottom: 1px solid #e0e0e0;
|
padding: 6px 10px;
|
||||||
}
|
|
||||||
|
|
||||||
#toc-list { list-style: none; padding: 0; margin: 0; }
|
|
||||||
#toc-list li { margin: 2px 0; }
|
|
||||||
#toc-list li a {
|
|
||||||
color: #555;
|
color: #555;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: block;
|
font-size: 13px;
|
||||||
padding: 6px 10px;
|
border-radius: 3px;
|
||||||
border-radius: 4px;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
|
||||||
transition: background 0.15s;
|
|
||||||
}
|
}
|
||||||
#toc-list li a:hover {
|
.toc-link:hover { background: #f5f5f5; }
|
||||||
background: #e8e8e8;
|
.toc-link.active { background: #007bff; color: #fff; }
|
||||||
color: #0066cc;
|
|
||||||
|
.toc-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.toc-expander {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
#toc-list .toc-h1 {
|
.toc-expander:hover { color: #333; }
|
||||||
font-weight: 600;
|
|
||||||
font-size: 15px;
|
.toc-children { display: none; padding-left: 12px; }
|
||||||
margin-top: 8px;
|
.toc-children.expanded { display: block; }
|
||||||
}
|
|
||||||
#toc-list .toc-h1 > a { color: #333; }
|
.toc-item[data-level="1"] > .toc-link { font-weight: 500; }
|
||||||
#toc-list .toc-h2 { margin-left: 0; }
|
.toc-item[data-level="2"] > .toc-link { padding-left: 20px; }
|
||||||
#toc-list .toc-h2 > a { padding-left: 24px; font-size: 14px; }
|
.toc-item[data-level="3"] > .toc-link { padding-left: 30px; }
|
||||||
#toc-list .toc-h3 { margin-left: 0; }
|
.toc-item[data-level="4"] > .toc-link { padding-left: 40px; }
|
||||||
#toc-list .toc-h3 > a { padding-left: 36px; font-size: 13px; color: #666; }
|
.toc-item[data-level="5"] > .toc-link { padding-left: 50px; }
|
||||||
#toc-list .toc-h4 { margin-left: 0; }
|
|
||||||
#toc-list .toc-h4 > a { padding-left: 48px; font-size: 12px; color: #888; }
|
|
||||||
#toc-list .toc-h5 > a { padding-left: 60px; font-size: 12px; color: #888; }
|
|
||||||
#toc-list .toc-h6 > a { padding-left: 72px; font-size: 12px; color: #888; }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="toc-panel"><ul class="toc-list" id="toc-list"></ul></div>
|
||||||
<!-- 目录按钮 -->
|
<button id="toc-btn" onclick="toggleToc()">☰</button>
|
||||||
<div id="toc-btn" title="目录">☰</div>
|
|
||||||
<!-- 侧边栏 -->
|
|
||||||
<div id="toc-sidebar">
|
|
||||||
<h3>目录</h3>
|
|
||||||
<ul id="toc-list"></ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<main>{{markdown}}</main>
|
<main>{{markdown}}</main>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
{{renderMathInElement}}
|
{{renderMathInElement}}
|
||||||
{{mermaid}}
|
{{mermaid}}
|
||||||
<script>
|
<script>
|
||||||
(function(){
|
(function() {
|
||||||
const btn = document.getElementById('toc-btn');
|
const panel = document.getElementById('toc-panel');
|
||||||
const sidebar = document.getElementById('toc-sidebar');
|
|
||||||
const list = document.getElementById('toc-list');
|
const list = document.getElementById('toc-list');
|
||||||
const main = document.querySelector('main');
|
let headings = [];
|
||||||
|
|
||||||
// 生成目录
|
function init() {
|
||||||
const headings = main.querySelectorAll('h1, h2, h3, h4');
|
headings = Array.from(document.querySelectorAll('main h1, main h2, main h3, main h4, main h5, main h6'));
|
||||||
if (headings.length === 0) { btn.style.display = 'none'; return; }
|
if (headings.length === 0) return;
|
||||||
|
|
||||||
headings.forEach((h, i) => {
|
headings.forEach((h, i) => { if (!h.id) h.id = 'h-' + i; });
|
||||||
if (!h.id) h.id = 'h-' + i;
|
|
||||||
|
const root = { level: 0, children: [] };
|
||||||
|
const stack = [root];
|
||||||
|
headings.forEach(h => {
|
||||||
|
const level = parseInt(h.tagName[1]);
|
||||||
|
const item = { id: h.id, text: h.textContent.trim(), level, children: [] };
|
||||||
|
while (stack.length > 1 && stack[stack.length - 1].level >= level) stack.pop();
|
||||||
|
stack[stack.length - 1].children.push(item);
|
||||||
|
stack.push(item);
|
||||||
|
});
|
||||||
|
render(root.children, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(items, parent) {
|
||||||
|
items.forEach(item => {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.className = 'toc-' + h.tagName.toLowerCase();
|
li.className = 'toc-item';
|
||||||
li.innerHTML = '<a href="#' + h.id + '">' + h.textContent + '</a>';
|
li.dataset.level = item.level;
|
||||||
list.appendChild(li);
|
|
||||||
|
let html = `<a class="toc-link" href="#${item.id}">
|
||||||
|
<span class="toc-text">${item.text}</span>`;
|
||||||
|
if (item.children.length) {
|
||||||
|
html += `<span class="toc-expander" onclick="event.stopPropagation(); toggle(this)">+</span>`;
|
||||||
|
}
|
||||||
|
html += `</a>`;
|
||||||
|
li.innerHTML = html;
|
||||||
|
|
||||||
|
if (item.children.length) {
|
||||||
|
const ul = document.createElement('ul');
|
||||||
|
ul.className = 'toc-children';
|
||||||
|
render(item.children, ul);
|
||||||
|
li.appendChild(ul);
|
||||||
|
if (item.level === 1) { ul.classList.add('expanded'); li.querySelector('.toc-expander').textContent = '-'; }
|
||||||
|
}
|
||||||
|
parent.appendChild(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(btn) {
|
||||||
|
const ul = btn.closest('li').querySelector('.toc-children');
|
||||||
|
ul.classList.toggle('expanded');
|
||||||
|
btn.textContent = ul.classList.contains('expanded') ? '-' : '+';
|
||||||
|
}
|
||||||
|
|
||||||
|
window.toggleToc = function() { panel.classList.toggle('show'); };
|
||||||
|
|
||||||
|
document.querySelectorAll('.toc-link').forEach(link => {
|
||||||
|
link.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const id = this.getAttribute('href').slice(1);
|
||||||
|
document.getElementById(id).scrollIntoView({ behavior: 'smooth' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 切换目录
|
init();
|
||||||
function toggleTOC() {
|
|
||||||
const open = sidebar.classList.toggle('open');
|
|
||||||
btn.classList.toggle('open', open);
|
|
||||||
btn.textContent = open ? '✕' : '☰';
|
|
||||||
}
|
|
||||||
btn.onclick = toggleTOC;
|
|
||||||
|
|
||||||
// 左侧滑动/悬停检测
|
|
||||||
let touchStartX = 0;
|
|
||||||
|
|
||||||
document.addEventListener('touchstart', e => { touchStartX = e.touches[0].clientX; });
|
|
||||||
document.addEventListener('touchmove', e => {
|
|
||||||
const x = e.touches[0].clientX;
|
|
||||||
if (touchStartX > 30 && x < 50 && !sidebar.classList.contains('open')) {
|
|
||||||
btn.classList.add('visible');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', e => {
|
|
||||||
if (e.clientX < 50 && !sidebar.classList.contains('open')) {
|
|
||||||
btn.classList.add('visible');
|
|
||||||
} else {
|
|
||||||
btn.classList.remove('visible');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user