@@ -8,8 +8,8 @@ import I18nKey from "../i18n/i18nKey";
88import { i18n } from "../i18n/translation" ;
99
1010export interface TOCConfig {
11- contentId : string ;
12- indicatorId : string ;
11+ contentId ? : string ;
12+ contentElement ?: HTMLElement ;
1313 maxLevel ?: number ;
1414 scrollOffset ?: number ;
1515}
@@ -20,17 +20,43 @@ export class TOCManager {
2020 private minDepth = 10 ;
2121 private maxLevel : number ;
2222 private scrollTimeout : number | null = null ;
23- private contentId : string ;
24- private indicatorId : string ;
23+ private contentId : string | null ;
24+ private contentElement : HTMLElement | null ;
2525 private scrollOffset : number ;
2626
2727 constructor ( config : TOCConfig ) {
28- this . contentId = config . contentId ;
29- this . indicatorId = config . indicatorId ;
28+ this . contentId = config . contentId ?? null ;
29+ this . contentElement = config . contentElement ?? null ;
3030 this . maxLevel = config . maxLevel || 3 ;
3131 this . scrollOffset = config . scrollOffset || 80 ;
3232 }
3333
34+ /**
35+ * 获取当前实例绑定的 TOC 容器。
36+ * 优先使用实例级元素引用,避免多实例时因为重复 id 命中错误节点。
37+ */
38+ private getTOCContentElement ( ) : HTMLElement | null {
39+ if ( this . contentElement ) {
40+ return this . contentElement ;
41+ }
42+
43+ if ( ! this . contentId ) {
44+ return null ;
45+ }
46+
47+ return document . getElementById ( this . contentId ) ;
48+ }
49+
50+ /**
51+ * 获取当前实例内部的活动指示器。
52+ * 指示器限定在当前内容容器内查询,避免多个目录组件互相污染。
53+ */
54+ private getIndicatorElement ( ) : HTMLElement | null {
55+ return this . getTOCContentElement ( ) ?. querySelector (
56+ "[data-card-toc-indicator]" ,
57+ ) as HTMLElement | null ;
58+ }
59+
3460 private getContentContainer ( ) : Element | null {
3561 return (
3662 document . querySelector ( ".custom-md" ) ||
@@ -177,19 +203,20 @@ export class TOCManager {
177203 ` ;
178204 } ) ;
179205
180- tocHTML += `<div id="${ this . indicatorId } " style="opacity: 0;" class="toc-active-indicator"></div>` ;
206+ tocHTML +=
207+ '<div data-card-toc-indicator style="opacity: 0;" class="toc-active-indicator"></div>' ;
181208
182209 return tocHTML ;
183210 }
184211
185212 public updateTOCContent ( ) : void {
186- const tocContent = document . getElementById ( this . contentId ) ;
187- if ( ! tocContent ) { return ; }
213+ const tocContent = this . getTOCContentElement ( ) ;
214+ if ( ! tocContent ) {
215+ return ;
216+ }
188217
189218 tocContent . innerHTML = this . generateTOCHTML ( ) ;
190- this . tocItems = Array . from (
191- document . querySelectorAll ( `#${ this . contentId } a` ) ,
192- ) ;
219+ this . tocItems = Array . from ( tocContent . querySelectorAll ( "a" ) ) ;
193220 }
194221
195222 private getVisibleHeadingIds ( ) : string [ ] {
@@ -233,7 +260,9 @@ export class TOCManager {
233260 }
234261
235262 public updateActiveState ( ) : void {
236- if ( ! this . tocItems || this . tocItems . length === 0 ) { return ; }
263+ if ( ! this . tocItems || this . tocItems . length === 0 ) {
264+ return ;
265+ }
237266
238267 this . tocItems . forEach ( ( item ) => {
239268 item . classList . remove ( "visible" ) ;
@@ -254,16 +283,20 @@ export class TOCManager {
254283 }
255284
256285 private updateActiveIndicator ( activeItems : HTMLElement [ ] ) : void {
257- const indicator = document . getElementById ( this . indicatorId ) ;
258- if ( ! indicator || ! this . tocItems . length ) { return ; }
286+ const indicator = this . getIndicatorElement ( ) ;
287+ if ( ! indicator || ! this . tocItems . length ) {
288+ return ;
289+ }
259290
260291 if ( activeItems . length === 0 ) {
261292 indicator . style . opacity = "0" ;
262293 return ;
263294 }
264295
265- const tocContent = document . getElementById ( this . contentId ) ;
266- if ( ! tocContent ) { return ; }
296+ const tocContent = this . getTOCContentElement ( ) ;
297+ if ( ! tocContent ) {
298+ return ;
299+ }
267300
268301 const contentRect = tocContent . getBoundingClientRect ( ) ;
269302 const firstActive = activeItems [ 0 ] ;
@@ -285,12 +318,14 @@ export class TOCManager {
285318 }
286319
287320 private scrollToActiveItem ( activeItem : HTMLElement ) : void {
288- if ( ! activeItem ) { return ; }
321+ if ( ! activeItem ) {
322+ return ;
323+ }
289324
290- const tocContainer = document
291- . querySelector ( `# ${ this . contentId } ` )
292- ?. closest ( ".toc-scroll-container" ) ;
293- if ( ! tocContainer ) { return ; }
325+ const tocContainer = activeItem . closest ( ".toc-scroll-container" ) ;
326+ if ( ! tocContainer ) {
327+ return ;
328+ }
294329
295330 if ( this . scrollTimeout ) {
296331 clearTimeout ( this . scrollTimeout ) ;
0 commit comments