Skip to content

Commit 1c967fc

Browse files
committed
fix(runtime-dom): fix v-on same computed handler on multiple elements
fix #1747
1 parent b2a9142 commit 1c967fc

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

packages/runtime-dom/__tests__/patchEvents.spec.ts

+34
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,38 @@ describe(`runtime-dom: events patching`, () => {
119119
expect(fn1).toHaveBeenCalledTimes(1)
120120
expect(fn2).toHaveBeenCalledTimes(0)
121121
})
122+
123+
// #1747
124+
it('should handle same computed handler function being bound on multiple targets', async () => {
125+
const el1 = document.createElement('div')
126+
const el2 = document.createElement('div')
127+
128+
const event = new Event('click')
129+
const prevFn = jest.fn()
130+
const nextFn = jest.fn()
131+
132+
patchProp(el1, 'onClick', null, prevFn)
133+
patchProp(el2, 'onClick', null, prevFn)
134+
135+
el1.dispatchEvent(event)
136+
el2.dispatchEvent(event)
137+
await timeout()
138+
expect(prevFn).toHaveBeenCalledTimes(2)
139+
expect(nextFn).toHaveBeenCalledTimes(0)
140+
141+
patchProp(el1, 'onClick', prevFn, nextFn)
142+
patchProp(el2, 'onClick', prevFn, nextFn)
143+
144+
el1.dispatchEvent(event)
145+
el2.dispatchEvent(event)
146+
await timeout()
147+
expect(prevFn).toHaveBeenCalledTimes(2)
148+
expect(nextFn).toHaveBeenCalledTimes(2)
149+
150+
el1.dispatchEvent(event)
151+
el2.dispatchEvent(event)
152+
await timeout()
153+
expect(prevFn).toHaveBeenCalledTimes(2)
154+
expect(nextFn).toHaveBeenCalledTimes(4)
155+
})
122156
})

packages/runtime-dom/src/modules/events.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,27 @@ export function removeEventListener(
5959
}
6060

6161
export function patchEvent(
62-
el: Element,
62+
el: Element & { _vei?: Record<string, Invoker | undefined> },
6363
rawName: string,
6464
prevValue: EventValue | null,
6565
nextValue: EventValue | null,
6666
instance: ComponentInternalInstance | null = null
6767
) {
68-
const invoker = prevValue && prevValue.invoker
69-
if (nextValue && invoker) {
68+
// vei = vue event invokers
69+
const invokers = el._vei || (el._vei = {})
70+
const existingInvoker = invokers[rawName]
71+
if (nextValue && existingInvoker) {
7072
// patch
7173
;(prevValue as EventValue).invoker = null
72-
invoker.value = nextValue
73-
nextValue.invoker = invoker
74+
existingInvoker.value = nextValue
7475
} else {
7576
const [name, options] = parseName(rawName)
7677
if (nextValue) {
77-
addEventListener(el, name, createInvoker(nextValue, instance), options)
78-
} else if (invoker) {
78+
const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
79+
addEventListener(el, name, invoker, options)
80+
} else if (existingInvoker) {
7981
// remove
80-
removeEventListener(el, name, invoker, options)
82+
removeEventListener(el, name, existingInvoker, options)
8183
}
8284
}
8385
}
@@ -120,7 +122,6 @@ function createInvoker(
120122
}
121123
}
122124
invoker.value = initialValue
123-
initialValue.invoker = invoker
124125
invoker.attached = getNow()
125126
return invoker
126127
}

0 commit comments

Comments
 (0)