preline icon indicating copy to clipboard operation
preline copied to clipboard

Overlay stops working as it does not append "hidden" class with DOM replacement

Open makikata opened this issue 3 months ago • 3 comments

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.

  1. Using Turbo with Ruby on Rails but I think this is irrelevant. We just need to replace the DOM
  2. Turbo replaces the HTML body of the page with the response body. Another DOM is then in place.
  3. There is an event listener on turbo:load that re-initiate the components using window.HSStaticMethods.autoInit(). Or we can just usewindow.HSOverlay.autoInit(), it will be the same result
  4. 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

makikata avatar Sep 24 '25 00:09 makikata

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.

makikata avatar Sep 24 '25 00:09 makikata

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 avatar Oct 13 '25 14:10 olegpix

@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.

makikata avatar Nov 11 '25 20:11 makikata