Skip to content

Users/status: don't accumulate a click handler on every refresh - #17

Open
zattak1 wants to merge 1 commit into
Qbix:mainfrom
zattak1:fix/status-refresh-handler-accumulation
Open

Users/status: don't accumulate a click handler on every refresh#17
zattak1 wants to merge 1 commit into
Qbix:mainfrom
zattak1:fix/status-refresh-handler-accumulation

Conversation

@zattak1

@zattak1 zattak1 commented Aug 26, 2026

Copy link
Copy Markdown

refresh() starts with $(tool.element).empty(), which removes the children but not handlers bound on tool.element itself — and CASE 1 (logged in) and CASE 2 (optimistic placeholder) both bind Q.Pointer.click there.

Passing tool to .on() doesn't help here: that registers the bind in Q.Event.jQueryForTool, which is flushed when the tool is removed, and a refresh is not a removal. So every refresh leaves another live handler on the same element, and one click fires state.onInvoke once per refresh so far.

CASE 3 (logged out) escapes it only because it binds on a freshly-built child rather than on tool.element.

The tool refreshes on Users.onLogin, Users.onLogout and each of the three Q.Optimistic avatar events, so this accumulates in normal use.

Measured via CASE 2, counting jQuery handlers on tool.element:

before: refresh #1..#5 -> click:2,3,4,5,6; one click fired onInvoke 5x
after:  refresh #1..#5 -> click:2,2,2,2,2; one click fired onInvoke 1x

Unbind before rebinding, at the .empty() that makes it look already handled.

Found while fixing the same class of bug in Communities (Qbix/Communities#7), where the accumulated handler stacked one login dialog per page visited.

refresh() starts with $(tool.element).empty(), which removes the children but
not handlers bound on tool.element itself -- and CASE 1 (logged in) and
CASE 2 (optimistic placeholder) both bind Q.Pointer.click there.

Passing `tool` to .on() does not help: that registers the bind in
Q.Event.jQueryForTool, which is flushed when the tool is REMOVED, and a
refresh is not a removal. So every refresh leaves another live handler on the
same element, and one click fires state.onInvoke once per refresh so far.

CASE 3 (logged out) escapes it only because it binds on a freshly-built
child, not on tool.element.

Measured via CASE 2, counting jQuery handlers on tool.element:

    before: refresh Qbix#1..Qbix#5 -> click:2,3,4,5,6; one click fired onInvoke 5x
    after:  refresh Qbix#1..Qbix#5 -> click:2,2,2,2,2; one click fired onInvoke 1x

Unbind before rebinding, at the .empty() that makes it look already handled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant