11const sheets = new Map < string , HTMLDivElement > ( ) ;
2- let isInitialized = false ;
2+ const sheetListeners = new WeakMap <
3+ HTMLElement ,
4+ {
5+ closeButton : ( ) => void ;
6+ escapeKey : ( e : KeyboardEvent ) => void ;
7+ }
8+ > ( ) ;
9+ let clickHandler : ( ( e : MouseEvent ) => void ) | null = null ;
310
411function initExplainCodeButtons ( ) {
5- // Prevent multiple initializations
6- if ( isInitialized ) return ;
7- isInitialized = true ;
12+ // Remove existing handler if present
13+ if ( clickHandler ) {
14+ document . body . removeEventListener ( "click" , clickHandler ) ;
15+ }
816
9- // Use event delegation on document body to catch all clicks
10- document . body . addEventListener ( "click" , ( e ) => {
17+ // Create new click handler
18+ clickHandler = ( e : MouseEvent ) => {
1119 const target = e . target as HTMLElement ;
1220 const button = target . closest < HTMLButtonElement > (
1321 "button[data-sheet-trigger]" ,
@@ -34,7 +42,21 @@ function initExplainCodeButtons() {
3442 }
3543
3644 openSheet ( sheet ) ;
37- } ) ;
45+ } ;
46+
47+ // Attach the new handler
48+ document . body . addEventListener ( "click" , clickHandler ) ;
49+ }
50+
51+ function cleanup ( ) {
52+ // Remove click handler
53+ if ( clickHandler ) {
54+ document . body . removeEventListener ( "click" , clickHandler ) ;
55+ clickHandler = null ;
56+ }
57+
58+ // Restore body overflow in case a sheet was open
59+ document . body . style . overflow = "" ;
3860}
3961
4062function createSheet (
@@ -154,6 +176,9 @@ function initSheet(container: HTMLElement) {
154176
155177 if ( ! content ) return ;
156178
179+ // Check if listeners already exist for this sheet
180+ if ( sheetListeners . has ( content ) ) return ;
181+
157182 function closeSheet ( ) {
158183 const side = content . dataset . side || "right" ;
159184 const slideInClass = `slide-in-from-${ side } ` ;
@@ -172,13 +197,23 @@ function initSheet(container: HTMLElement) {
172197 document . body . style . overflow = "" ;
173198 }
174199
175- closeButton ?. addEventListener ( "click" , closeSheet ) ;
176-
177- document . addEventListener ( "keydown" , ( e ) => {
200+ // Create listener functions that can be removed later
201+ const closeButtonHandler = ( ) => closeSheet ( ) ;
202+ const escapeKeyHandler = ( e : KeyboardEvent ) => {
178203 if ( e . key === "Escape" && content . dataset . state === "open" ) {
179204 closeSheet ( ) ;
180205 }
206+ } ;
207+
208+ // Store listener references for potential cleanup
209+ sheetListeners . set ( content , {
210+ closeButton : closeButtonHandler ,
211+ escapeKey : escapeKeyHandler ,
181212 } ) ;
213+
214+ // Attach listeners
215+ closeButton ?. addEventListener ( "click" , closeButtonHandler ) ;
216+ document . addEventListener ( "keydown" , escapeKeyHandler ) ;
182217}
183218
184219function openSheet ( container : HTMLElement ) {
@@ -200,16 +235,22 @@ function openSheet(container: HTMLElement) {
200235 firstFocusable ?. focus ( ) ;
201236}
202237
203- document . addEventListener ( "astro:page-load" , ( ) => {
204- initExplainCodeButtons ( ) ;
238+ // Clean up before Astro swaps pages (view transitions)
239+ document . addEventListener ( "astro:before-swap" , ( ) => {
240+ cleanup ( ) ;
205241} ) ;
206242
207- // Also try DOMContentLoaded as a fallback
208- document . addEventListener ( "DOMContentLoaded " , ( ) => {
243+ // Initialize on every page load (works with view transitions)
244+ document . addEventListener ( "astro:page-load " , ( ) => {
209245 initExplainCodeButtons ( ) ;
210246} ) ;
211247
212- // And try running immediately if DOM is already loaded
213- if ( document . readyState !== "loading" ) {
248+ // Fallback for non-Astro environments or initial page load
249+ if ( document . readyState === "loading" ) {
250+ document . addEventListener ( "DOMContentLoaded" , ( ) => {
251+ initExplainCodeButtons ( ) ;
252+ } ) ;
253+ } else {
254+ // DOM already loaded, initialize immediately
214255 initExplainCodeButtons ( ) ;
215256}
0 commit comments