blog
blog copied to clipboard
react合成事件分析
先写一个事件代理的方法,这个delegate方法可以给子元素添加和移除绑定事件处理函数。
<body>
<div>
<ul id="app">
<li class="childClassname">11</li>
<li class="childClassname">11</li>
</ul>
</div>
<script>
let parentElement = document.getElementById('app')
function callback (e) {
console.log('ok', e)
}
delegate(parentElement).on('childClassname', 'click', callback);
delegate(parentElement).off('childClassname', 'click', callback);
function delegate (parentElement) {
function on (childClassname, eventType, listener) {
let list = parentElement.getElementsByClassName(childClassname)
Array.from(list, (dom) => {
let eventStore = dom.eventStore || (dom.eventStore = {})
eventStore[eventType] = listener;
})
document.addEventListener(eventType, dispatchEvent, false)
}
function dispatchEvent (event) {
let { type, target } = event
let eventType = type;
while (target) {
let { eventStore } = target;
let listener = eventStore && eventStore[eventType]
if (listener) {
listener.call(target, event)
}
target = target.parentNode;
}
}
function off (childClassname, eventType, listener) {
let list = parentElement.getElementsByClassName(childClassname)
Array.from(list, (dom) => {
let eventStore = dom.eventStore
let listener = eventStore && eventStore[eventType]
if (listener) {
eventStore[eventType] = null
}
})
}
return {on, off}
}
</script>
</body>
现在你已经明白了如何将子元素绑定的事件都代理到document对象,下面继续深入学习react的绑定事件。
let onClick = () => { console.log('hello') }
let element = React.createElement(
'button',
{ id: 'sayHello', onClick },
'say',
React.createElement('span', { color: 'red' }, 'hello'))
React在渲染DOM的时候会做事件绑定。
- 给绑定DOM的节点挂载一个对象,保存事件名称和事件处理函数
function addEvent(dom, eventType, listener) {
eventType = eventType.toLowerCase()
// 给绑定DOM的节点挂载一个对象
let eventStore = dom.eventStore || (dom.eventStore = {});
// 对象上保存事件名称和事件处理函数{onclick: listener}
eventStore[eventType] = listener
document.addEventListener(eventType.slice(2), dispatchEvent, false)
}
- 给DOM绑定监听,函数是dispatchEvent
let syntheticEvent;
function dispatchEvent(event) {
// 此时event是原生事件对象
let { target, type } = event;
// 在事件对象中获取事件源和事件名
let eventType = 'on' + type;
// 获取合成事件对象
syntheticEvent = getSyntheticEvent(event)
while (target) {
let { eventStore } = target;
let listener = eventStore && eventStore[eventType]
if (listener) {
listener.call(target, syntheticEvent)
}
target = target.parentNode;
}
// 合成事件用完会清空,如果不想清空需要使用persist函数
for (let key in syntheticEvent) {
if (key != 'persist') {
syntheticEvent[key] = null
}
}
}
- 然后创建了一个合成事件对象,合成事件对象会把原始事件的属性和方法都复制过来
function persist() {
syntheticEvent = {persist}
}
function getSyntheticEvent(nativeEvent) {
if (!syntheticEvent) {
syntheticEvent = {persist}
}
syntheticEvent.nativeEvent = nativeEvent;
syntheticEvent.currentTarget = nativeEvent.target;
// 把原生事件对象的属性和方法都复制到合成事件对象中
for (let key in nativeEvent) {
if (typeof nativeEvent[key] === 'function') {
syntheticEvent[key] = nativeEvent[key].bind(nativeEvent)
} else {
syntheticEvent[key] = nativeEvent[key]
}
}
return syntheticEvent
}
- 开始使用
let app = document.getElementById('app')
function callback (e) {
e.persist()
console.log('ok', e)
}
addEvent(app, 'onClick', callback)
完整代码
<script>
let app = document.getElementById('app')
function callback (e) {
e.persist()
console.log('ok', e)
}
addEvent(app, 'onClick', callback)
function addEvent(dom, eventType, listener) {
eventType = eventType.toLowerCase()
// 给绑定DOM的节点挂载一个对象
let eventStore = dom.eventStore || (dom.eventStore = {});
// 对象上保存事件名称和事件处理函数{onclick: listener}
eventStore[eventType] = listener
document.addEventListener(eventType.slice(2), dispatchEvent, false)
}
let syntheticEvent;
function dispatchEvent(event) {
// 此时event是原生事件对象
let { target, type } = event;
// 在事件对象中获取事件源和事件名
let eventType = 'on' + type;
// 获取合成事件对象
syntheticEvent = getSyntheticEvent(event)
while (target) {
// 从事件源开始冒泡
let { eventStore } = target;
let listener = eventStore && eventStore[eventType]
if (listener) {
listener.call(target, syntheticEvent)
}
target = target.parentNode;
}
// 完成之后清空syntheticEvent
for (let key in syntheticEvent) {
if (key != 'persist') {
syntheticEvent[key] = null
}
}
}
function persist() {
syntheticEvent = {persist}
}
function getSyntheticEvent(nativeEvent) {
if (!syntheticEvent) {
syntheticEvent = {persist}
}
syntheticEvent.nativeEvent = nativeEvent;
syntheticEvent.currentTarget = nativeEvent.target;
// 把原生事件对象的属性和方法都复制到合成事件对象中
for (let key in nativeEvent) {
if (typeof nativeEvent[key] === 'function') {
syntheticEvent[key] = nativeEvent[key].bind(nativeEvent)
} else {
syntheticEvent[key] = nativeEvent[key]
}
}
return syntheticEvent
}
</script>
</body>
持久化之后下次会创建一个新的对象,旧的对象也不会销毁,两个对象互不干扰 关于persist可以参考官方文档链接