LearnNewsExamplesServices
Frontmatter
id9510
titleTooltip regression: dynamic domListeners bypassed reactivity and bubbling fixes
stateClosed
labels
bugaicore
assigneestobiu
createdAtMar 18, 2026, 1:00 PM
updatedAtMar 18, 2026, 1:15 PM
githubUrlhttps://github.com/neomjs/neo/issues/9510
authortobiu
commentsCount2
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMar 18, 2026, 1:15 PM

Tooltip regression: dynamic domListeners bypassed reactivity and bubbling fixes

Closed v12.1.0 bugaicore
tobiu
tobiu commented on Mar 18, 2026, 1:00 PM

In Neo v12, dynamic DOM event registration (e.g., addDomListeners) failed due to three separate bugs in the event architecture:

  1. Reactivity Bypass: src/mixin/DomEvents.mjs mutated the this.domListeners array in place. The core.Config system's isEqual check evaluated the identical array reference as unchanged, bypassing the afterSetDomListeners hook entirely. Fixed by spreading into a new array.
  2. Bubbling Cancellation: In src/manager/DomEvent.mjs, the global bubble flag was cancelled if !listener.bubble was true. Since omitted configs are undefined, !undefined evaluated to true, incorrectly cancelling event bubbling for any listener that didn't explicitly set bubble: true. Fixed to explicitly check for listener.bubble === false.
  3. Boolean Parsing in Manager: In DomEvent.updateDomListeners, resolving the bubble config used an || operator (domListener.bubble || value.bubble), losing explicitly set false values and failing to default to true. Fixed to use hasOwnProperty. Additionally, fixed a potential crash where vnodeId lacked a fallback to config.id if vdom was not present in the register method.
tobiu added the bug label on Mar 18, 2026, 1:00 PM
tobiu added the ai label on Mar 18, 2026, 1:00 PM
tobiu added the core label on Mar 18, 2026, 1:00 PM
tobiu referenced in commit 5348eb1 - "fix(core): Resolve dynamic domListeners reactivity bypass and bubble cancellation (#9510) on Mar 18, 2026, 1:00 PM
tobiu
tobiu Mar 18, 2026, 1:01 PM

Input from Gemini 3.1 Pro:

✦ Completed the fix.

Commit: 5348eb155

Resolved the following issues:

  1. Bubbling Cancellation: In src/manager/DomEvent.mjs, fixed the event loop check to explicitly use listener.bubble === false instead of !listener.bubble.
  2. Boolean Parsing in Manager: Fixed DomEvent.updateDomListeners to use hasOwnProperty for bubble config parsing rather than ||.
  3. vnodeId Consistent Fallback: Added fallback logic to config.id in manager/DomEvent.mjs register method to prevent crashes when a VDOM object isn't present yet.
tobiu referenced in commit f1b55cc - "refactor(core): Implement cloneOnGet for domListeners reactivity (#9510) on Mar 18, 2026, 1:14 PM
tobiu
tobiu Mar 18, 2026, 1:14 PM

Input from Gemini 3.1 Pro:

✦ Added architectural refactoring.

Commit: f1b55cc7b

Reverted the manual [...this.domListeners] array spread hacks in favor of a cleaner configuration-level fix: we changed the domListeners_ config to use a descriptor with cloneOnGet: 'shallow'.

This ensures that any time a developer accesses this.domListeners, they receive a new array reference. If they mutate it (e.g. .push()) and assign it back, core.Config naturally detects the reference difference and triggers the afterSetDomListeners reactivity hook. This makes the system inherently more maintainable and resilient to accidental reactivity bypasses across the entire framework.

tobiu assigned to @tobiu on Mar 18, 2026, 1:15 PM
tobiu closed this issue on Mar 18, 2026, 1:15 PM