Overlay stops working as it does not append "hidden" class with DOM replacement
Summary
On a turbo:load event, I use autoInit() but then overlay does not close anymore
Steps to Reproduce
This is how I found the bug, but I think it can be reproduced with simpe JS.
- Using Turbo with Ruby on Rails but I think this is irrelevant. We just need to replace the DOM
- Turbo replaces the HTML body of the page with the response body. Another DOM is then in place.
- There is an event listener on
turbo:loadthat re-initiate the components usingwindow.HSStaticMethods.autoInit(). Or we can just usewindow.HSOverlay.autoInit(), it will be the same result - When clicking on the toggle button, the sidebar does not get
"hidden"class as shown in the video
https://github.com/user-attachments/assets/0f4786bb-51a2-414d-9b5c-8c9da62053a9
Please read the README.md for instructions and investigation
Thank you! ❤️
Demo Link
https://codesandbox.io/p/github/makikata/demo-preline/main
Expected Behavior
When I click on the toggle button it should hide the sidebar, the "hidden" class should be added to the sidebar
Actual Behavior
When I click on the toggle button does not hide the sidebar, the "hidden" class is not added to the sidebar
Screenshots
https://github.com/user-attachments/assets/0f4786bb-51a2-414d-9b5c-8c9da62053a9
More investigation done. Setting HSOverlay.openedItemsQty = 0; in the event listener looks like it solves the issue.
Though I am not sure this should be intended: Relying on a global variable to keep track of a state of a component.
Hi!
This issue might occur because Turbo replaces the <body> element while leaving the previous overlay state (instances, backdrops, and <html> classes) active. When you re-initialize on the new DOM, that desynchronization prevents the hidden class from being applied on close.
If your setup exposes a public destroy() method, it’s better to perform a proper teardown instead:
document.addEventListener('turbo:before-render', () => {
try {
// Destroy every overlay instance tied to the old DOM
document.querySelectorAll('[data-hs-overlay]').forEach((el) => {
const api = window.HSOverlay.getInstance.(el);
api.destroy.();
});
// Safety: remove global artifacts from the old page
document.documentElement.classList.remove('hs-overlay-open');
document.querySelectorAll('.hs-overlay-backdrop, [data-hs-overlay-backdrop-template]')
.forEach(el => el.remove());
} catch {}
});
document.addEventListener('turbo:load', () => {
// Re-initialize the fresh DOM
window.HSStaticMethods.autoInit.();
// or: window.HSOverlay.autoInit.();
});
@olegpix Thank you for the comment, I tried it and it is still occuring if I do not reset HSOverlay.openedItemsQty = 0
Replacing/reinitiating the element is not the problem, it is the counter.