@@ -8,6 +8,7 @@ document.addEventListener('DOMContentLoaded', () => {
88 const moonIcon = document . getElementById ( 'moon-icon' ) ;
99 const sunIcon = document . getElementById ( 'sun-icon' ) ;
1010 const githubEditLink = document . getElementById ( 'github-edit-link' ) ;
11+ const backToTopBtn = document . getElementById ( 'back-to-top' ) ;
1112
1213 const GITHUB_REPO_URL = 'https://github.com/luffydod/ConfigNote/edit/main/' ;
1314 const APP_BASE_URL = new URL ( './' , document . baseURI ) ;
@@ -264,6 +265,11 @@ document.addEventListener('DOMContentLoaded', () => {
264265 buildTableOfContents ( ) ;
265266 addCopyButtons ( ) ;
266267
268+ const h1 = markdownContent . querySelector ( 'h1' ) ;
269+ const fileTitle = typeof notesConfig !== 'undefined' ? notesConfig . find ( n => n . path === normalizedNotePath ) ?. title : undefined ;
270+ const noteTitle = h1 ? h1 . textContent : ( fileTitle || '笔记' ) ;
271+ document . title = `${ noteTitle } - ConfigNote` ;
272+
267273 if ( window . location . hash ) {
268274 scrollToHeading ( window . location . hash . substring ( 1 ) ) ;
269275 }
@@ -311,6 +317,7 @@ document.addEventListener('DOMContentLoaded', () => {
311317 } ) ;
312318
313319 tocList . innerHTML = tocHTML ;
320+ setupScrollspy ( ) ;
314321 }
315322
316323 const tocListDom = document . getElementById ( 'toc-list' ) ;
@@ -392,6 +399,61 @@ document.addEventListener('DOMContentLoaded', () => {
392399 } ) ;
393400 }
394401
402+ // --- Scrollspy & Back to Top ---
403+ let currentScrollListener = null ;
404+
405+ function setupScrollspy ( ) {
406+ const headings = Array . from ( markdownContent . querySelectorAll ( 'h1, h2, h3, h4' ) ) ;
407+ const tocLinks = document . querySelectorAll ( '#toc-list a' ) ;
408+
409+ if ( headings . length === 0 ) return ;
410+
411+ if ( currentScrollListener ) {
412+ window . removeEventListener ( 'scroll' , currentScrollListener ) ;
413+ }
414+
415+ currentScrollListener = ( ) => {
416+ let activeId = '' ;
417+ const scrollPos = window . scrollY + 120 ;
418+
419+ for ( const h of headings ) {
420+ if ( h . offsetTop <= scrollPos ) {
421+ activeId = h . id ;
422+ } else {
423+ break ;
424+ }
425+ }
426+
427+ if ( ! activeId && headings . length > 0 ) {
428+ activeId = headings [ 0 ] . id ;
429+ }
430+
431+ tocLinks . forEach ( a => {
432+ a . classList . remove ( 'active' ) ;
433+ if ( activeId && a . getAttribute ( 'href' ) === `#${ activeId } ` ) {
434+ a . classList . add ( 'active' ) ;
435+ }
436+ } ) ;
437+ } ;
438+
439+ window . addEventListener ( 'scroll' , currentScrollListener ) ;
440+ currentScrollListener ( ) ;
441+ }
442+
443+ if ( backToTopBtn ) {
444+ window . addEventListener ( 'scroll' , ( ) => {
445+ if ( window . scrollY > 300 ) {
446+ backToTopBtn . classList . add ( 'show' ) ;
447+ } else {
448+ backToTopBtn . classList . remove ( 'show' ) ;
449+ }
450+ } ) ;
451+
452+ backToTopBtn . addEventListener ( 'click' , ( ) => {
453+ window . scrollTo ( { top : 0 , behavior : 'smooth' } ) ;
454+ } ) ;
455+ }
456+
395457 // --- Router ---
396458 function handleRoute ( ) {
397459 const urlParams = new URLSearchParams ( window . location . search ) ;
@@ -400,6 +462,7 @@ document.addEventListener('DOMContentLoaded', () => {
400462 if ( notePath ) {
401463 fetchAndRenderNote ( notePath ) ;
402464 } else {
465+ document . title = "ConfigNote - 📝 我的开发工具配置备忘录" ;
403466 noteView . classList . replace ( 'section-active' , 'section-hidden' ) ;
404467 homeView . classList . replace ( 'section-hidden' , 'section-active' ) ;
405468 window . scrollTo ( 0 , 0 ) ;
0 commit comments