React 中的 Event ( 复合事件和原生事件 )

2,062 阅读3分钟

案例

      React 项目中的如下代码:

事件方法:

挂载位置:事件是挂载在 div 上的

代码效果:

现象:

但在实际操作中发现,e.target 会指向 div 下的 a ( 虽然在页面中这两个元素确实区域是重叠的)。整个人都惊了好么.....

解决思路

首先排除原生事件冒泡的原因,因为 onmouseenter 和 onmouseout 事件跟其他所有事件( 包括 onmouseouver )最大的不同,就是这两个事件不会冒泡。所以但凡触发了事件方法,e.target 一定指向触发该事件的元素本身。

那么思路就指向了 React 中的事件机制。React 中的事件是通过事件委托实现的。因此具有以下特性:

  • 不能像原生事件那样,依靠 return false 来阻止元素的默认事件( 如常用的 a 上的 onclick 事件 return false 会阻止 a 自身的跳转 )。阻止元素的默认事件必须明确地使用 e.preventDefault() 方法。
  • React 中事件函数的参数 e 是一个“合成事件”。但是这个“合成事件”是完全符合 W3C 标准的,可以像原生事件函数中的参数 e 一样使用。甚至 React 还帮助我们解决了浏览器兼容问题。
  • 一般情况下,不建议使用 addEventListener 方法增加额外的事件监听器,因为这样增加的事件就不是 React 中带的“合成事件”了,一方面无法获得便利性,另一方面可能会在事件冒泡上出现一些异常。

什么是 React 中的“合成事件”

React 中的事件句柄( event handler ) 会通过合成事件的方式传递。合成事件是一个跨浏览器兼容且包含原生事件的事件对象。

合成事件和浏览器原生事件一样,包含 stopPropagation() 和 preventDefault()。
如果因为某些原因,需要使用浏览器的底层事件时,只需要使用 nativeEvent 属性来获取。每一个合成事件( SyntheticEvent )对象都包含以下属性: 

boolean bubblesboolean cancelableDOMEventTarget currentTargetboolean defaultPreventednumber eventPhaseboolean isTrustedDOMEvent nativeEventvoid preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget targetnumber timeStampstring type

注意的是,当事件处理函数返回 false 时,不再阻止事件冒泡和默认事件。需要使用 e.stopPropagation() 或 e.preventDefault() 代替。

合成事件使用事件池概念,是通过合并来的。因此合成事件对象会被重用,且在事件回调函数被调用后,所有的属性都会失效。这是处于性能考虑,所以千万不要使用异步方法访问事件。
如果确实需要异步访问事件属性,需要在事件上调用 event.persist()。该方法会从池中移除合成事件,允许用户代码保留对事件的引用。

function onClick(event) {
  console.log(event); // => nullified object.
  console.log(event.type); // => "click"
  const eventType = event.type; // => "click"
  setTimeout(function() {
    console.log(event.type); // => null
    console.log(eventType); // => "click"
  }, 0);
  // 不起作用,this.state.clickEvent 的值将会只包含 null
  this.setState({clickEvent: event});
  // 你仍然可以导出事件属性
  this.setState({eventType: event.type});
}

官方文档中提供支持的事件请参考以下链接( 感谢中文社区在本次官方文档汉化过程中的贡献 ):

zh-hans.reactjs.org/docs/events…

回到案例上

未完待续....( 写到这里发现得补上 DOM 标准中的 Event 详解了 )。这里先停了,把那个补了之后再接着写吧