Skip to content

Offcanvas hide() then dispose() runs into "this._element is null" #41473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
3 tasks done
steffen-harbich-cognitum opened this issue May 17, 2025 · 2 comments
Open
3 tasks done

Comments

@steffen-harbich-cognitum

Prerequisites

Describe the issue

I use the Offcanvas to show navigation items. Clicking on a navigation item calls hide() and starts a navigation (Angular client-side routing). Now, when the navigation leads to a page that does not have the offcanvas in it, the dispose() is called, too. This happens before the animation of hide() is finished and results in the following error:

ERROR TypeError: this._element is null
completeCallback offcanvas.js:124
execute index.js:203
handler index.js:221
Angular 10
triggerTransitionEnd index.js:76
executeAfterTransition index.js:226
Angular 21
executeAfterTransition index.js:224
_queueCallback base-component.js:48
hide offcanvas.js:132
close compact-navigation.component.ts:88
navigate compact-navigation.component.ts:96

Code line:

const completeCallback = () => {

Expectation: Offcanvas should not continue hiding when disposed already.

Reduced test cases

Example here

  • click button to open offcanvas
  • click button in offcanvas
  • observe console error

What operating system(s) are you seeing the problem on?

Windows

What browser(s) are you seeing the problem on?

Firefox

What version of Bootstrap are you using?

5.3.5

@julien-deramond
Copy link
Member

Thanks for reporting this issue @steffen-harbich-cognitum

I tried to play with your CodePen example and probably managed to find a something to make it work with the following JS code:

const offcanvasElement = document.querySelector("#offcanvasExample");

document.querySelector("#myBtn").addEventListener("click", () => {
  const oc = bootstrap.Offcanvas.getOrCreateInstance(offcanvasElement);

  offcanvasElement.addEventListener("hidden.bs.offcanvas", () => {
    oc.dispose();

    // Recreate instance for future use (optional)
    bootstrap.Offcanvas.getOrCreateInstance(offcanvasElement);
  }, { once: true });

  oc.hide();
});

From my understanding, the CodePen example JS code fails because it calls dispose() almost immediately after hide(), using setTimeout(). However, hide() triggers a transition and internal cleanup that isn’t finished yet when dispose() runs. As a result, Bootstrap is still using internal references like this._element or this._backdrop, which dispose() has already removed, leading to errors like this._element is null or this._backdrop is null.

The JS code I've tried seems to work because it waits for the Offcanvas to fully finish hiding (using the hidden.bs.offcanvas event) before calling dispose(). After that, it immediately reinitializes the Offcanvas instance, so it’s fully set up again and safe to open the next time. This avoids the race condition and ensures the internal Bootstrap logic stays intact.

@steffen-harbich-cognitum
Copy link
Author

steffen-harbich-cognitum commented May 21, 2025

Thanks for looking into it. As I described, my use case is a real world example, the CodePen example is just to reproduce the issue. Applying your workaround to my Angular app would mean that the user has to wait the closing animation before the actual navigation to another page can start. As the animation duration is not too long, it would work, yes, but is still a workaround.

What about adding a if (disposed) return; to the callback completeCallback instead? The disposed is something like this._element === null.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants