Skip to content

Commit

Permalink
refactor: simplify event system
Browse files Browse the repository at this point in the history
  • Loading branch information
yuche committed Dec 6, 2017
1 parent 72b1d90 commit 3997aca
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 131 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = {
},
rootDir: __dirname,
testMatch: [
// '<rootDir>/packages/nerv/__tests__/refs.spec.js',
// '<rootDir>/packages/nerv/__tests__/event.spec.js',
// '<rootDir>/packages/nerv/__tests__/component.spec.js',
// '<rootDir>/packages/nerv/__tests__/render.spec.js',
// '<rootDir>/packages/nerv/__tests__/lifecycle.spec.js',
Expand Down
6 changes: 3 additions & 3 deletions packages/nerv/__tests__/event.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ describe('Events', () => {
const input = scratch.childNodes[0]
const proto = input.constructor.prototype
const addEventListenerSpy = sinon.spy(proto, 'addEventListener')
// const removeEventListenerSpy = sinon.spy(proto, 'removeEventListener')
const removeEventListenerSpy = sinon.spy(proto, 'removeEventListener')
// https://stackoverflow.com/questions/1096436/document-getelementbyidid-focus-is-not-working-for-firefox-or-chrome
input.focus()
await nextTick()
Expand All @@ -202,11 +202,11 @@ describe('Events', () => {
scratch.childNodes[0].focus()
await nextTick()
// @TODO: IMPORTANT
// expect(removeEventListenerSpy.called).toBeTruthy()
expect(removeEventListenerSpy.called).toBeTruthy()
})

// @TODO
it.skip('should change/fix event name', () => {
it('should change/fix event name', () => {
const container = document.createElement('div')
document.body.appendChild(container)
const onchange = function () {}
Expand Down
10 changes: 1 addition & 9 deletions packages/nerv/src/create-element.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import h from './vdom/h'
import {
isFunction,
isString,
isAttrAnEvent
isString
} from 'nerv-utils'
import FullComponent from './full-component'
import StatelessComponent from './stateless-component'
import CurrentOwner from './current-owner'
import EventHook from './hooks/event-hook'
import {
Props,
Component,
Expand All @@ -20,12 +18,6 @@ function transformPropsForRealTag (type: string, props: Props) {
const newProps: Props = {}
for (const propName in props) {
const propValue = props[propName]
if (isAttrAnEvent(propName)) {
newProps[propName] = !(propValue instanceof EventHook)
? new EventHook(propName, propValue)
: propValue
continue
}
if (propName === 'defaultValue') {
newProps.value = props.value || props.defaultValue
continue
Expand Down
176 changes: 77 additions & 99 deletions packages/nerv/src/hooks/event-hook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { isFunction, MapClass } from 'nerv-utils'
import { VHook } from './vhook'

const ONINPUT = 'oninput'
const ONPROPERTYCHANGE = 'onpropertychange'
Expand Down Expand Up @@ -67,105 +66,77 @@ if (navigator.userAgent.indexOf('MSIE 9') >= 0) {
})
}

class EventHook {
vhook = VHook.Event
eventName: string
export function attachEvent (
domNode: Element,
eventName: string,
handler: Function
constructor (eventName: string, handler) {
this.eventName = getEventName(eventName)
this.handler = handler
) {
eventName = fixEvent(domNode, eventName)
/* istanbul ignore next */
if (eventName === ONPROPERTYCHANGE) {
processOnPropertyChangeEvent(domNode, handler)
return
}

hook (node, prop, prev) {
if (
prev &&
prev.vhook === VHook.Event &&
prev.handler === this.handler &&
prev.eventName === this.eventName
) {
return
let delegatedRoots = delegatedEvents.get(eventName)
if (unbubbleEvents[eventName] === 1) {
if (!delegatedRoots) {
delegatedRoots = new MapClass()
}
const eventName = fixEvent(node, this.eventName)
this.eventName = eventName
/* istanbul ignore next */
if (eventName === ONPROPERTYCHANGE) {
processOnPropertyChangeEvent(node, this.handler)
return
const event = attachEventToNode(domNode, eventName, delegatedRoots)
delegatedEvents.set(eventName, delegatedRoots)
if (isFunction(handler)) {
delegatedRoots.set(domNode, {
eventHandler: handler,
event
})
}
let delegatedRoots = delegatedEvents.get(eventName)
if (unbubbleEvents[eventName] === 1) {
if (!delegatedRoots) {
delegatedRoots = new MapClass()
} else {
if (!delegatedRoots) {
delegatedRoots = {
items: new MapClass()
}
const event = attachEventToNode(node, eventName, delegatedRoots)
delegatedRoots.event = attachEventToDocument(
doc,
eventName,
delegatedRoots
)
delegatedEvents.set(eventName, delegatedRoots)
if (isFunction(this.handler)) {
delegatedRoots.set(node, {
eventHandler: this.handler,
event
})
}
} else {
if (!delegatedRoots) {
delegatedRoots = {
items: new MapClass()
}
delegatedRoots.event = attachEventToDocument(
doc,
eventName,
delegatedRoots
)
delegatedEvents.set(eventName, delegatedRoots)
}
if (isFunction(this.handler)) {
delegatedRoots.items.set(node, this.handler)
}
}
}
/* istanbul ignore next */
unhook (node, prop, next) {
if (
next &&
next.vhook === VHook.Event &&
next.handler === this.handler &&
next.eventName === next.eventName
) {
return
}
const eventName = fixEvent(node, this.eventName)
if (eventName === ONPROPERTYCHANGE) {
return
}
const delegatedRoots = delegatedEvents.get(eventName)
if (unbubbleEvents[eventName] === 1 && delegatedRoots) {
const event = delegatedRoots.get(node)
node.removeEventListener(parseEventName(eventName), event.event, false)
/* istanbul ignore next */
const delegatedRootsSize = delegatedRoots.size
if (delegatedRoots.delete(node) && delegatedRootsSize === 0) {
delegatedEvents.delete(eventName)
}
} else if (delegatedRoots && delegatedRoots.items) {
const items = delegatedRoots.items
if (items.delete(node) && items.size === 0) {
doc.removeEventListener(
parseEventName(eventName),
delegatedRoots.event,
false
)
delegatedEvents.delete(eventName)
}
if (isFunction(handler)) {
delegatedRoots.items.set(domNode, handler)
}
}
}

function getEventName (eventName) {
if (eventName === 'onDoubleClick') {
eventName = 'ondblclick'
} else if (eventName === 'onTouchTap') {
eventName = 'onclick'
export function detachEvent (
domNode: Element,
eventName: string,
handler: Function
) {
eventName = fixEvent(domNode, eventName)
if (eventName === ONPROPERTYCHANGE) {
return
}
const delegatedRoots = delegatedEvents.get(eventName)
if (unbubbleEvents[eventName] === 1 && delegatedRoots) {
const event = delegatedRoots.get(domNode)
domNode.removeEventListener(parseEventName(eventName), event.event, false)
/* istanbul ignore next */
const delegatedRootsSize = delegatedRoots.size
if (delegatedRoots.delete(domNode) && delegatedRootsSize === 0) {
delegatedEvents.delete(eventName)
}
} else if (delegatedRoots && delegatedRoots.items) {
const items = delegatedRoots.items
if (items.delete(domNode) && items.size === 0) {
doc.removeEventListener(
parseEventName(eventName),
delegatedRoots.event,
false
)
delegatedEvents.delete(eventName)
}
}
return eventName.toLowerCase()
}

let propertyChangeActiveElement
Expand Down Expand Up @@ -194,10 +165,14 @@ function processOnPropertyChangeEvent (node, handler) {
propertyChangeActiveHandler = handler
if (!bindFocus) {
bindFocus = true
doc.addEventListener('focusin', () => {
unbindOnPropertyChange()
bindOnPropertyChange(node)
}, false)
doc.addEventListener(
'focusin',
() => {
unbindOnPropertyChange()
bindOnPropertyChange(node)
},
false
)
doc.addEventListener('focusout', unbindOnPropertyChange, false)
}
}
Expand Down Expand Up @@ -252,11 +227,16 @@ function detectCanUseOnInputNode (node) {
)
}

function fixEvent (node, eventName) {
if (detectCanUseOnInputNode(node)) {
if (eventName === 'onchange') {
eventName = ONINPUT in window ? ONINPUT : ONPROPERTYCHANGE
}
function fixEvent (node: Element, eventName: string) {
if (eventName === 'onDoubleClick') {
eventName = 'ondblclick'
} else if (eventName === 'onTouchTap') {
eventName = 'onclick'
// tslint:disable-next-line:prefer-conditional-expression
} else if (eventName === 'onChange' && detectCanUseOnInputNode(node)) {
eventName = ONINPUT in window ? ONINPUT : ONPROPERTYCHANGE
} else {
eventName = eventName.toLowerCase()
}
return eventName
}
Expand Down Expand Up @@ -341,5 +321,3 @@ function attachEventToNode (node, eventName, delegatedRoots) {
node.addEventListener(parseEventName(eventName), eventHandler, false)
return eventHandler
}

export default EventHook
6 changes: 0 additions & 6 deletions packages/nerv/src/hooks/vhook.ts

This file was deleted.

22 changes: 17 additions & 5 deletions packages/nerv/src/vdom/patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from 'nerv-shared'
import { unmount, unmountChildren } from './unmount'
import Ref from './ref'
import { attachEvent, detachEvent } from '../hooks/event-hook'

export function patch (
lastVnode,
Expand Down Expand Up @@ -475,6 +476,20 @@ function setStyle (domStyle, style, value) {
}
}

function patchEvent (
eventName: string,
lastEvent: Function,
nextEvent: Function,
domNode: Element
) {
if (lastEvent !== nextEvent) {
if (isFunction(lastEvent)) {
detachEvent(domNode, eventName, lastEvent)
}
attachEvent(domNode, eventName, nextEvent)
}
}

function patchStyle (lastAttrValue, nextAttrValue, dom) {
const domStyle = dom.style
let style
Expand Down Expand Up @@ -530,10 +545,7 @@ export function patchProp (
}
}
} else if (isAttrAnEvent(prop)) {
if (isFunction(lastValue)) {
lastValue.unhook(domNode, prop, nextValue)
}
nextValue.hook(domNode, prop, lastValue)
patchEvent(prop, lastValue, nextValue, domNode)
} else if (prop === 'style') {
patchStyle(lastValue, nextValue, domNode)
} else if (
Expand Down Expand Up @@ -587,7 +599,7 @@ function patchProps (
const value = previousProps[propName]
if (isNullOrUndef(nextProps[propName]) && !isNullOrUndef(value)) {
if (isAttrAnEvent(propName)) {
value.unhook(domNode, propName, nextProps[propName])
detachEvent(domNode, propName, value)
} else {
domNode.removeAttribute(propName)
}
Expand Down
11 changes: 3 additions & 8 deletions packages/nerv/src/vdom/unmount.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {
isNullOrUndef,
isInvalid,
VType,
VirtualChildren
} from 'nerv-shared'
import { isNullOrUndef, isInvalid, VType, VirtualChildren } from 'nerv-shared'
import { isAttrAnEvent, isArray } from 'nerv-utils'
import Ref from './ref'
import { detachEvent } from '../hooks/event-hook'

export function unmountChildren (
children: VirtualChildren,
Expand Down Expand Up @@ -35,9 +31,8 @@ export function unmount (vnode, parentDom?) {
const { props, children, ref } = vnode
unmountChildren(children)
for (const propName in props) {
const property = props[propName]
if (isAttrAnEvent(propName)) {
property.unhook(dom, propName, null)
detachEvent(dom, propName, props[propName])
}
}
if (ref !== null) {
Expand Down

0 comments on commit 3997aca

Please sign in to comment.