Skip to content
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

function _isWithActiveTrigger in tooltip.js is causing an uncaught error #37474

Open
3 tasks done
njmkramer opened this issue Nov 11, 2022 · 36 comments · May be fixed by #40856
Open
3 tasks done

function _isWithActiveTrigger in tooltip.js is causing an uncaught error #37474

njmkramer opened this issue Nov 11, 2022 · 36 comments · May be fixed by #40856
Labels

Comments

@njmkramer
Copy link

njmkramer commented Nov 11, 2022

Prerequisites

Describe the issue

Uncaught TypeError: Cannot convert undefined or null to object
at Function.values ()
at Popover._isWithActiveTrigger (tooltip.js:549:19)
at complete (tooltip.js:279:16)
at execute (index.js:254:5)
at HTMLDivElement.handler (index.js:276:5)
at triggerTransitionEnd (index.js:98:11)
at index.js:282:7

This happens when multiple elements have a popover or a popover is attached to a new element and fast switching hover on elements.

Reduced test cases

suggestion:

_isWithActiveTrigger() {
if(typeof this._activeTrigger == 'undefined' || this._activeTrigger === null){
return false;
}
return Object.values(this._activeTrigger).includes(true)
}

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

Windows

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

Chrome

What version of Bootstrap are you using?

v5.2.2

@github-actions
Copy link
Contributor

Hello @njmkramer. Bug reports must include a live demo of the issue. Per our contributing guidelines, please create a reduced test case on CodePen or StackBlitz and report back with your link, Bootstrap version, and specific browser and Operating System details.

@njmkramer
Copy link
Author

Reduced test:

https://js-cq37dw.stackblitz.io/

Focus as fast as you can different inputs and hover at the same time, enter a value, at some point the error will occur.
Tested on Chrome version: 107.0.5304.107
Bootstrap 5.2.2
@popperjs/core 2.11.6

@njmkramer
Copy link
Author

Hi @julien-deramond, see my previous comment for reduced test/live demo.

@njmkramer
Copy link
Author

Hi @julien-deramond any progress on this issue?

best regards

@hyphen81
Copy link

Running into this as well..

@GeoSot
Copy link
Member

GeoSot commented Jan 16, 2023

As much as I 've tested till now, I cannot replicate the issue 😕

@njmkramer
Copy link
Author

_isWithActiveTrigger

fix:
if (this!== null) {
if (
typeof this._activeTrigger != 'undefined' &&
typeof this._activeTrigger.hover == 'undefined'
) {
this._activeTrigger.hover = false;
}
}

@GeoSot
Copy link
Member

GeoSot commented Jan 16, 2023

fix:
if (this!== null) {
if (
typeof this._activeTrigger != 'undefined' &&
typeof this._activeTrigger.hover == 'undefined'
) {
this._activeTrigger.hover = false;
}
}

sorry to disappoint you, but this is not a fix, it is just a super defensive approach to save the situation of something really wrong (Tooltip is not initialized at all)

I am here to help, but I will need your cooperation, in order to replicate the issue. I have tried to many times this example, without results.

Please, create a codepen example to be able to follow your code, and try to explain me in simple bullets (steps) how to replicate.

@njmkramer
Copy link
Author

Thanxs for your reply, I wil updated the live test in the coming days

@njmkramer
Copy link
Author

Explaination:

1: from top to bottom:
focus each input -> it will turn red and have a hover 'enter a value'

2: from top to bottom:
focus and type a value -> hover will disappear or the error will occur

3: from top to bottom:
clear the input

4: repeat 1, 2 and 3 until the error comes up and it will

Sometimes the error came up very quickly, other times I had to repeat the 4 steps serveral times, as fast as I could, to trigger the error to come up.

I hope this helps you to see the problem

@GeoSot
Copy link
Member

GeoSot commented Jan 18, 2023

can you please do it in a code pen in order to be able to see your code ? ( or provide an open project url for stackblitz)

@theomega
Copy link

I'm seeing the same problem in Sentry from my Users, but I can also not reproduce it using the StackBlitz from @njmkramer, neither in Firefox (where I'm getting the User reports from) nor in Safari.

@njmkramer
Copy link
Author

@njmkramer
Copy link
Author

isWithActiveError.mp4

screenshot of error occuring

@njmkramer
Copy link
Author

@GeoSot see screenshot where the error occurs. Best regards Norbert

@hakenr
Copy link

hakenr commented Feb 1, 2023

I'm experiencing the very same issue when using Bootstrap with Blazor and trying to navigate to another route (removes the elements from DOM) when the tooltip is still displayed:
2023-02-01_17-48-23

The Blazor code is as simple as follows:

@inject NavigationManager NavigationManager

<HxButton Text="Navigate home"
	Tooltip="Navigate"
	Color="ThemeColor.Primary"
	OnClick="@(() => NavigationManager.NavigateTo(""))" />

HxButton is a component from a Blazor library wrapping Bootstrap: https://havit.blazor.eu/

For now, I'm not trying to give you a regular repro, just adding some additional info to the issue.

BTW: My original intention was to reproduce this issue (havit/Havit.Blazor#457):
image
...but when I recreated the original scenario, the _activeTrigger error appeared instead.

@Andream98
Copy link

I am also experiencing this issue in [email protected] even though my setup is a bit different than the one described by @njmkramer

I am using the Tooltips class in a [email protected] custom directive that automatically instantiates tooltips on elements with the directive.

It is also really difficult to replicate the error in my case but from the experimenting I've done, at the time of error this is completely empty:

{
    "_element": null,
    "_config": null,
    "_isEnabled": null,
    "_timeout": null,
    "_isHovered": false,
    "_activeTrigger": null,
    "_popper": null,
    "_templateFactory": null,
    "_newContent": null,
    "tip": null,
    "_hideModalHandler": null
}

So this makes me think that in some way (that I still haven't managed to figure out):

  1. the element which is causing the error has its Tooltip instance disposed of via the dispose() function (hence all the null properties)

Screenshot 2023-02-28 at 13 37 54

from bootstrap.bundle.js:809

  1. but the element still has the mouseover Events attached to it even after the instance is disposed of (at least the mouseleave, which triggers the hide() function that causes _isHovered to be false unlike all the other properties that are null)

So I'm guessing the problem resides either in the Events management after the Tooltip instance is disposed of, or when it's re-instantiated on the same element after the previous instance was disposed of.

@alchimik
Copy link

alchimik commented Mar 1, 2023

simple live demo: https://codepen.io/alchimik-irk/pen/abapgLZ

@GeoSot
Copy link
Member

GeoSot commented Mar 1, 2023

simple live demo: https://codepen.io/alchimik-irk/pen/abapgLZ

My friend, this is a wrong approach, as you are trying to dispose the tooltip instantly, not giving the proper time for hide process to get finished

the right approach on this is

setTimeout( () =>{  
  tooltipEl.addEventListener('hidden.bs.tooltip', ()=> { tooltipBs.dispose() } )
  tooltipBs.hide(); 
}, 2000);

ref: #38018

@Andream98
Copy link

That might be a wrong usage of the class but it is useful to reproduce the error consistently.

In my case I'm using the dispose() method (as the documentation states here) to hide the tooltip and remove the instance from an item, but this is sometimes generating the error.

Here's my code:
Screenshot 2023-03-01 at 12 44 58

I'm guessing that when the dispose() method is called, another call to hide() gets executed at the same time maybe by the mouseleave event.

I think a failsafe in the dispose() method is required to prevent these edge cases.

@njmkramer
Copy link
Author

Update:
In my case the error occurs frequently when an input already has a popover with an error message, by script a new error message comes in.
The existing popover must be disposed before the new message can be shown.
If the mouseover/mouseleave is triggered during that disposing and adding the new popover the error occurs.
Even if I set a timeout of 2 sec. between disposing and adding

1: start disposing

2: doing mouseover and mouseleave over the input with a popover

error occurs:
tooltip.js:536 Uncaught TypeError: Cannot convert undefined or null to object
at Function.values ()
at Popover._isWithActiveTrigger (tooltip.js:536:19)
at complete (tooltip.js:268:16)
at execute (index.js:254:5)
at HTMLDivElement.handler (index.js:276:5)
at triggerTransitionEnd (index.js:98:11)
at index.js:282:7
_isWithActiveTrigger @ tooltip.js:536
complete @ tooltip.js:268
execute @ index.js:254
handler @ index.js:276
triggerTransitionEnd @ index.js:98
(anonymous) @ index.js:282
setTimeout (async)
executeAfterTransition @ index.js:280
_queueCallback @ base-component.js:49
show @ tooltip.js:238
(anonymous) @ tooltip.js:511
setTimeout (async)
_setTimeout @ tooltip.js:532
_enter @ tooltip.js:509
(anonymous) @ tooltip.js:465
handler @ event-handler.js:98

(2 sec. passed)
3: add new popover

I hope this helps in finding a solution for this nasty error.

@njmkramer
Copy link
Author

sreencast of error occuring during disposing and mouseover/mouseleave.

popover-error.mp4

@alchimik
Copy link

#36080 - same problem, with a solution, not merged

@MarkGorissen
Copy link

same here

@Rezyan
Copy link

Rezyan commented Apr 30, 2023

Same issue here. My users also encounter this error (I have a logging system that logs user errors on the frontend), but I have never achieved to reproduce it on my side. Below are the user agents of my users devices who encountered this same issue:

  • Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
    = Chrome 112 on Windows 10
  • Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/112.0.5615.167 Mobile/15E148 Safari/604.1
    = Chrome 112 on iOS 16.3 (Apple iPhone)

Does anyone have any leads? Thanks

@mahui-cn
Copy link

I ran into the same issue, it occur when I call tooltip.dispose() in unmount of component in React, while I change it to call tooltip.hide(), it is ok and also meet my demand.

@njmkramer
Copy link
Author

@GeoSot Hi, when can i or we, expect a solution for this issue.
The uncaught error is a real pain in the a**.
Users have to reload the page because of this.
Thanxs in advance

@njmkramer
Copy link
Author

I managed to find a workaround;

var popover = Popover.getInstance(obj);
if (popover !== null && typeof popover.dispose === 'function') {
  popover.disable();
  popover.hide();
  setTimeout(function () {
    popover.dispose();
  }, 300);
}

@TomONeill
Copy link

TomONeill commented Jul 26, 2023

I ran into this error and because of the comments in this issue I was able to resolve it.

In my scenario I wanted to change the title/position after the tooltip was initialised.
I did this by disposing the tooltip and immediately re-initialising the tooltip with the latest values.
This works, however when the tooltip was shown and the title/position updated, it resulted in this error.

I found that you can use lambdas instead to always get the latest values, like so:

 new Tooltip(this.element, {
  "title": () => this.title,
  "placement": () => this.placement
});

Now I only dispose the tooltip when the element gets destroyed.

Update 7 September 2023:
Still getting the issue. The reproduction is actually straightforward: call dispose when the tooltip is visible, then the error will be thrown; unless you dispose after the hidden event triggered.

@kblicharski
Copy link

kblicharski commented Jul 31, 2023

Here's a workaround:

const disposePopover = popover => {
  setTimeout(() => popover?.dispose(), 100)
}

dae pushed a commit to ankitects/anki that referenced this issue Aug 2, 2023
* Implement import log screen in Svelte

* Show filename in import log screen title

* Remove unused NoteRow property

* Show number of imported notes

* Use a single nid expression

* Use 'count' as variable name for consistency

* Import from @tslib/backend instead

* Fix summary_template typing

* Fix clippy warning

* Apply suggestions from code review

* Fix imports

* Contents -> Fields

* Increase max length of browser search bar

https://github.com/ankitects/anki/pull/2568/files#r1255227035

* Fix race condition in Bootstrap tooltip destruction

twbs/bootstrap#37474

* summary_template -> summaryTemplate

* Make show link a button

* Run import ops on Svelte side

* Fix geometry not being restored in CSV Import page

* Make VirtualTable fill available height

* Keep CSV dialog modal

* Reword importing-existing-notes-skipped

* Avoid mentioning matching based on first field

* Change tick and cross icons

* List skipped notes last

* Pure CSS spinner

* Move set_wants_abort() call to relevant dialogs

* Show number of imported cards

* Remove bold from first sentence and indent summaries

* Update UI after import operations

* Add close button to import log page

Also make virtual table react to resize event.

* Fix typing

* Make CSV dialog non-modal again

Otherwise user can't interact with browser window.

* Update window modality after import

* Commit DB and update undo actions after import op

* Split frontend proto into separate file, so backend can ignore it

Currently the automatically-generated frontend RPC methods get placed in
'backend.js' with all the backend methods; we could optionally split them
into a separate 'frontend.js' file in the future.

* Migrate import_done from a bridgecmd to a HTTP request

* Update plural form of importing-notes-added

* Move import response handling to mediasrv

* Move task callback to script section

* Avoid unnecessary :global()

* .log cannot be missing if result exists

* Move import log search handling to mediasrv

* Type common params of ImportLogDialog

* Use else if

* Remove console.log()

* Add way to test apkg imports in new log screen

* Remove unused import

* Get actual card count for CSV imports

* Use import type

* Fix typing error

* Ignore import log when checking for changes in Python layer

* Apply suggestions from code review

* Remove imported card count for now

* Avoid non-null assertion in assignment

* Change showInBrowser to take an array of notes

* Use dataclasses for import log args

* Simplify ResultWithChanges in TS

* Only abort import when window is modal

* Fix ResultWithChanges typing

* Fix Rust warnings

* Only log one duplicate per incoming note

* Update wording about note updates

* Remove caveat about found_notes

* Reduce font size

* Remove redundant map

* Give credit to loading.io

* Remove unused line

---------

Co-authored-by: RumovZ <[email protected]>
@tagliala
Copy link
Contributor

tagliala commented Aug 22, 2023

I'm also having this issue, but it is quite random and I cannot provide a reproducible test case.

It is indeed related to dispose(), and the use case is a tooltip initialized with a Stimulus controller

  connect () {
    this.tooltip = new Tooltip(this.element)
  }

  disconnect () {
    this.tooltip.dispose()
  }
tooltip.js:534 Uncaught TypeError: Cannot convert undefined or null to object
    at Function.values (<anonymous>)
    at Tooltip._isWithActiveTrigger (tooltip.js:534:1)
    at complete (tooltip.js:268:1)
    at execute (index.js:226:1)
    at HTMLDivElement.handler (index.js:247:1)
    at triggerTransitionEnd (index.js:71:1)
    at index.js:253:1

Hovewer:

@ArgoZhang
Copy link

same issue

@qbuache
Copy link

qbuache commented Aug 20, 2024

I have the same issue using vueJs and tooltips with the "focus" trigger.

I use a directive to manage the tooltips and I "fixed" the issue using :

beforeUnmount: async (el) => {
        const tooltip = bootstrap.Tooltip.getInstance(el);
        if (tooltip) {
            setTimeout(
                () => tooltip.dispose(),
                tooltip._config.trigger.includes("focus") ? 300 : 0,
            );
        }
    },

@yads
Copy link

yads commented Aug 28, 2024

I'm running into this issue via with a wrapped popover in an Ember component. This is the basic component code.

class MyComponent extends Component {
  configurePopover(element) {
    this.popover = new bootstrap.Popover(element);
  }

  willDestroy() {
    this.popover.dispose();
  }

  someHandler() {
    this.popover.hide();
    doStuff();
  }
}

What's happening is in someHandler we hide the popover, however, sometimes doStuff causes the component to be removed from the DOM and destroyed, which causes dispose to be called. Then the error is thrown, because now in the hide complete handler the popover has been disposed.

What I've had to do is to run hide in a setTimeout.

  someHandler() {
    setTimeout(() => this.popover.hide(), 1);
    doStuff();
  }

This way if the popover is disposed, the hide call won't do anything. It does feel like the hide complete handler should check if the component has been disposed and stop execution early.

@francogiacobbi
Copy link

francogiacobbi commented Sep 10, 2024

I'm having same troubles with this environment:

ASP.NET 4.6.2 - Bootstrap 5.3.3 - Jquery 3.7.1 - Bootstrap-Table 1.23.2

Problem is when i try to edit cell and update value (inside table and on database).

For cell EDITING using bootstrap-editable.js with skycyclone-x-editable

I use popup editing text-box.

All is ok until application stores value in database.
Values are correctly stored in database.

Event onEditableHidden is perfect and popup text disapperead well.
New Value appears in table.

A thing that i can't understand is that tooltip.js (inside browser inspector) is located in
localhost:44300/lib/bootstrap/js/src/tooltip.js
but in my folder bootstrap there's not a js folder with src subfolder but in browser i can see code of tooltip.js

Bootstrap and Jquery Libraries are managed by Libman.

In browser inspector i receive this error

If anyone has a workaround.....

Uncaught TypeError: can't convert null to object
    _isWithActiveTrigger tooltip.js:536
    hide tooltip.js:270
    g index.js:226
    a index.js:247
    s index.js:71
    _ index.js:253
    setTimeout handler*_ index.js:251
    _queueCallback base-component.js:49
    hide tooltip.js:282
    jQueryInterface popover.js:86
    jQuery 2
    jQueryInterface popover.js:75

```    call bootstrap-editable.js:4834
    innerHide bootstrap-editable.js:4813
    hide bootstrap-editable.js:1149
    destroy bootstrap-editable.js:1256
    init bootstrap-editable.js:936
    i jQuery
    remove bootstrap-editable.js:1408
    jQuery 6
    value bootstrap-table.min.js:10
    value bootstrap-table-editable.min.js:10
    value bootstrap-table-filter-control.min.js:10
    value bootstrap-table-editable.min.js:10
    jQuery 4
    save bootstrap-editable.js:1221
    jQuery 5
    submit bootstrap-editable.js:286
    jQuery 3
    submit bootstrap-editable.js:243
    jQuery 10
    render bootstrap-editable.js:114
    jQuery 9
    render bootstrap-editable.js:92
    editableform bootstrap-editable.js:410
    jQuery 2
    editableform bootstrap-editable.js:401
    renderForm bootstrap-editable.js:1081
    show bootstrap-editable.js:1127
    show bootstrap-editable.js:1810
    toggle bootstrap-editable.js:1832
    init bootstrap-editable.js:1555
    jQuery 9
    init bootstrap-editable.js:1540
    Editable bootstrap-editable.js:1485
    editable bootstrap-editable.js:2147
    jQuery 2
    editable bootstrap-editable.js:2133
    value bootstrap-table-editable.min.js:10
    jQuery 2
    value bootstrap-table-editable.min.js:10
    each jQuery
    value bootstrap-table-editable.min.js:10
    value bootstrap-table-filter-control.min.js:10
    value bootstrap-table.min.js:10
    value bootstrap-table.min.js:10
    value bootstrap-table-filter-control.min.js:10
    timeoutId bootstrap-table-filter-control.min.js:10
    setTimeout handler*ag/< bootstrap-table-filter-control.min.js:10
    jQuery 6
        dispatch
        handle
        trigger
        trigger
        each
        each
[tooltip.js:536:18](https://localhost:44300/lib/bootstrap/js/src/tooltip.js)




@anitschke
Copy link

I am running into this issue when trying to use Tooltip inside of a Lit custom element that wraps Tooltip. Something like this:

import { LitElement } from "lit";
import { Tooltip as BootstrapTooltip } from "bootstrap";

class Tooltip extends LitElement {
  static properties = {
    content: { type: String },
  };

  render() {
    return html`<slot></slot>`;
  }

  connectedCallback() {
    super.connectedCallback();
    this._initializeTooltip();
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    if (this._tooltip) {
      this._tooltip.dispose();
    }
  }

  async _initializeTooltip() {
    await this.updateComplete;
    const slot = this.shadowRoot.querySelector("slot");
    const nodes = slot.assignedNodes({ flatten: true });
    const triggerElement = nodes.find(
      (node) => node.nodeType === Node.ELEMENT_NODE
    );

    this._tooltip = new BootstrapTooltip(triggerElement, {
      title: this.content,
      container: document.body,
    });
  }
}

customElements.define('my-tooltip', Tooltip);

It is used like this to give a tooltip to a specific element <my-tooltip content="This is some tooltip text"><div>this element has a tooltip</div></my-tooltip>

connectedCallback is called when the custom element is added to the DOM in order to initialize the tooltip. When the my-tooltip custom element is removed from the DOM disconnectedCallback is called to dispose of the tooltip since we shouldn't need it anymore.

Normally this all works fine. However I have recently had some automated tests sporadically fail with this same Uncaught TypeError: Cannot convert undefined or null to object error. In these tests I am clicking on a button that happens to be wrapped in a <my-tooltip> element. If the test runs fast enough then the tooltip starts to show up as part of hovering over the button to perform the click. Before the show transition can complete the test finishes, the custom element is removed from the DOM, resulting in dispose() being called. At some point when the next test is running the transition callback runs, but it errors out because we have already disposed of the tooltip and nulled out the _activeTrigger property.

I have been able to work around this for now by doing some hacky modification of the <my-tooltip> custom element class prototype within this test. However it feels like the real fix is that the Bootstrap Tooltip should be safe to calling dispose at any point in time, even during transitions.

As @Andream98 and others have mentioned the documentation states that dispose() method "Hides and destroys an element’s tooltip ...". So it seems to me that it should be safe to call in my case where I wish to hide and destroy the tooltip because my custom element is being removed from the DOM.

There is also a big warning in the documentation:

All API methods are asynchronous and start a transition. They return to the caller as soon as the transition is started, but before it ends. In addition, a method call on a transitioning component will be ignored.

If I read this extremely literally I interpret this to mean that the dispose() should be ignored in cases where the component is transitioning. Like I outlined above I think what is happening in my case is the call to dispose() is happening during the transition where we show the tooltip. Thus according to the doc it seems that call to dispose() should be ignored entirely. But if that is the case it seems like you can never safely call dispose() since there is always the chance that a user could be hovering over that element right before the call to dispose()? Or does this not about starting transitions only apply to the API and not actions taken by users?

yads added a commit to yads/bootstrap that referenced this issue Sep 19, 2024
* ensure `complete` handlers for `show` and `hide` check if the commponent was disposed
* what can happen is the element can get removed from the DOM and the tooltip would get disposed
* when the complete handler would run at a later point the component would be disposed and would throw various null related exceptions
@yads yads linked a pull request Sep 19, 2024 that will close this issue
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Needs review
Status: To do
Development

Successfully merging a pull request may close this issue.