diff --git a/packages/runtime-dom/__tests__/modules/events.spec.ts b/packages/runtime-dom/__tests__/modules/events.spec.ts index 2021a1547c5..cb053482b56 100644 --- a/packages/runtime-dom/__tests__/modules/events.spec.ts +++ b/packages/runtime-dom/__tests__/modules/events.spec.ts @@ -7,7 +7,7 @@ describe(`events`, () => { const el = document.createElement('div') const event = new Event('click') const fn = jest.fn() - patchEvent(el, 'click', null, fn, null) + patchEvent(el, 'onClick', null, fn, null) el.dispatchEvent(event) await timeout() el.dispatchEvent(event) @@ -22,9 +22,9 @@ describe(`events`, () => { const event = new Event('click') const prevFn = jest.fn() const nextFn = jest.fn() - patchEvent(el, 'click', null, prevFn, null) + patchEvent(el, 'onClick', null, prevFn, null) el.dispatchEvent(event) - patchEvent(el, 'click', prevFn, nextFn, null) + patchEvent(el, 'onClick', prevFn, nextFn, null) await timeout() el.dispatchEvent(event) await timeout() @@ -39,7 +39,7 @@ describe(`events`, () => { const event = new Event('click') const fn1 = jest.fn() const fn2 = jest.fn() - patchEvent(el, 'click', null, [fn1, fn2], null) + patchEvent(el, 'onClick', null, [fn1, fn2], null) el.dispatchEvent(event) await timeout() expect(fn1).toHaveBeenCalledTimes(1) @@ -50,8 +50,8 @@ describe(`events`, () => { const el = document.createElement('div') const event = new Event('click') const fn = jest.fn() - patchEvent(el, 'click', null, fn, null) - patchEvent(el, 'click', fn, null, null) + patchEvent(el, 'onClick', null, fn, null) + patchEvent(el, 'onClick', fn, null, null) el.dispatchEvent(event) await timeout() expect(fn).not.toHaveBeenCalled() @@ -67,7 +67,7 @@ describe(`events`, () => { once: true } } - patchEvent(el, 'click', null, nextValue, null) + patchEvent(el, 'onClick', null, nextValue, null) el.dispatchEvent(event) await timeout() el.dispatchEvent(event) @@ -86,8 +86,8 @@ describe(`events`, () => { once: true } } - patchEvent(el, 'click', null, prevFn, null) - patchEvent(el, 'click', prevFn, nextValue, null) + patchEvent(el, 'onClick', null, prevFn, null) + patchEvent(el, 'onClick', prevFn, nextValue, null) el.dispatchEvent(event) await timeout() el.dispatchEvent(event) @@ -106,12 +106,30 @@ describe(`events`, () => { once: true } } - patchEvent(el, 'click', null, nextValue, null) - patchEvent(el, 'click', nextValue, null, null) + patchEvent(el, 'onClick', null, nextValue, null) + patchEvent(el, 'onClick', nextValue, null, null) el.dispatchEvent(event) await timeout() el.dispatchEvent(event) await timeout() expect(fn).not.toHaveBeenCalled() }) + + it('should assign native onclick attribute', async () => { + const el = document.createElement('div') + const event = new Event('click') + const fn = ((window as any)._nativeClickSpy = jest.fn()) + + patchEvent(el, 'onclick', null, '_nativeClickSpy()' as any) + el.dispatchEvent(event) + await timeout() + expect(fn).toHaveBeenCalledTimes(1) + + const fn2 = jest.fn() + patchEvent(el, 'onclick', null, fn2) + el.dispatchEvent(event) + await timeout() + expect(fn).toHaveBeenCalledTimes(1) + expect(fn2).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/runtime-dom/src/modules/events.ts b/packages/runtime-dom/src/modules/events.ts index 11b060a879d..0ac69479e52 100644 --- a/packages/runtime-dom/src/modules/events.ts +++ b/packages/runtime-dom/src/modules/events.ts @@ -1,4 +1,4 @@ -import { EMPTY_OBJ } from '@vue/shared' +import { EMPTY_OBJ, isString } from '@vue/shared' import { ComponentInternalInstance, callWithAsyncErrorHandling @@ -66,11 +66,22 @@ export function removeEventListener( export function patchEvent( el: Element, - name: string, + rawName: string, prevValue: EventValueWithOptions | EventValue | null, nextValue: EventValueWithOptions | EventValue | null, instance: ComponentInternalInstance | null = null ) { + // support native onxxx handlers + if (rawName in el) { + if (isString(nextValue)) { + el.setAttribute(rawName, nextValue) + } else { + ;(el as any)[rawName] = nextValue + } + return + } + + const name = rawName.slice(2).toLowerCase() const prevOptions = prevValue && 'options' in prevValue && prevValue.options const nextOptions = nextValue && 'options' in nextValue && nextValue.options const invoker = prevValue && prevValue.invoker diff --git a/packages/runtime-dom/src/patchProp.ts b/packages/runtime-dom/src/patchProp.ts index a0e9e04227d..013dfbf00f4 100644 --- a/packages/runtime-dom/src/patchProp.ts +++ b/packages/runtime-dom/src/patchProp.ts @@ -29,13 +29,7 @@ export const patchProp: RendererOptions['patchProp'] = ( if (isOn(key)) { // ignore v-model listeners if (key.indexOf('onUpdate:') < 0) { - patchEvent( - el, - key.slice(2).toLowerCase(), - prevValue, - nextValue, - parentComponent - ) + patchEvent(el, key, prevValue, nextValue, parentComponent) } } else if (!isSVG && key in el) { patchDOMProp(