关于小程序的事件,我们知道的是,在组件上面绑定对应的响应事件,用户在这个组件上操作就会触发预先绑定的事件,执行我们想要执行的代码。
比如:
outer view
middle0 view
middle1 view
inner view
// pages/demo_0/index.js
Page({
onOuter: function () {
console.log('onOuter')
},
onMiddle0: function () {
console.log('onMiddle0')
},
onMiddle1: function () {
console.log('onMiddle1')
},
onInner: function () {
console.log('onInner')
}
})
/* pages/demo_0/index.wxss */
.outer {
background-color: red;
width: 750rpx;
height:750rpx;
color: white;
}
.outer .middle-0 {
background-color: blue;
width: 350rpx;
height: 350rpx;
}
.outer .middle-1 {
background-color: pink;
width: 350rpx;
height: 350rpx;
}
.outer .middle-1 .inner {
background-color: black;
width: 200rpx;
height: 200rpx;
}
当点击 黑色的 inner 的时候,就会触发 onInner
。(当然,由于tap是冒泡事件,同时也会触发 onMiddle1
和onOuter
)
对应的log
onInner
onMiddle1
onOuter
那么这一切是怎么发生的呢?
当用户点击黑色的 inner 的时候,系统会根据点击的点的坐标来确定被点击的组件是哪一个。
事件定位
每一个页面都有一个组件树,在上面的例子中,我们的组件树结构如下:
点击事件发生时,系统会根据这个组件树,从树的最顶端开始往下来寻找被点击的点在哪个组件上。以上面的代码为例:
- 系统首先确认,点击的点在
page
上 - 确认点在
container
上 - 确认点在
outer
上 - 接下来判断点击的点没有在
middle-0
上 - 判断
middle-0
的兄弟组件middle-1
,发现这个点在middle-1
上 - 最后系统会继续遍历期子组件
inner
,这个点也在inner
的区域内
因为inner
没有兄弟组件和子组件,所以遍历过程结束。结果是,用户点击了inner,这里我们借鉴iOS的概念,称inner为这次tap事件的第一响应者。(如果这时候点击的是middle-1
上的非inner
区域,那么遍历到middle-1
后,会再测试inner
,发现点击的点没有在inner
上,从而确定用户点击了middle-1
上的非inner
区域。)
事件响应
上面的过程就是系统的事件的定位过程。在定位到被点击的组件之后,接下来就是事件响应了。
在解释事件响应前,首先要清楚一个概念:事件类型
在小程序中,事件类型主要有:(摘自小程序官方文档)
类型 | 触发条件 | 最低版本 |
---|---|---|
touchstart | 手指触摸动作开始 | |
touchmove | 手指触摸后移动 | |
touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | |
touchend | 手指触摸动作结束 | |
tap | 手指触摸后马上离开 | |
longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 | 1.5.0 |
longtap | 手指触摸后,超过350ms再离开(推荐使用longpress事件代替) | |
transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 | |
animationstart | 会在一个 WXSS animation 动画开始时触发 | |
animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 | |
animationend | 会在一个 WXSS animation 动画完成时触发 | |
touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 |
当然还有其它的特殊事件,仅存在于特定的组件,比如
的submit
事件,或者
的scroll
事件。
当组件上产生了tap
事件,我们也指定了tap
对应的响应函数时,对应的函数就会被调用。其它事件同理。
前面我们提到,点击 inner 的时候,同时也触发了onMiddle1
和onOuter
,我们再来增加一个知识:冒泡事件。
冒泡事件
冒泡事件是指,事件由第一个响应的组件响应之后,会再往上一级组件传递。也就是说,inner 响应了 tap
事件执行了onInner
之后,还会将tap
事件继续往它的父组件传递。于是就出现了刚刚实验的情况,点击了inner
后,发现middle-1
和outer
的tap响应函数也执行了。
如果不希望事件冒泡,可以使用catch
对事件进行捕捉,只需要把bind
替换成catch
即可:
outer view
middle0 view
middle1 view
inner view
target 和 currentTarget
target
为第一响应者,在上例中就是 inner
,而currentTarget
则为响应函数对应的组件。
我们修改一下代码:
outer view
middle0 view
middle1 view
inner view
// pages/demo_0/index.js
Page({
onOuter: function () {
console.log('onOuter')
},
onMiddle0: function () {
console.log('onMiddle0')
},
onMiddle1: function (options) {
console.log('************* onMiddle1 ****************')
console.log(options)
console.log('************* onMiddle1 ****************')
},
onInner: function (options) {
console.log('************* inner ****************')
console.log(options)
console.log('************* inner ****************')
}
})
点击inner后,log为:
根据我们上面的定义,在onInner
中,target和currentTarget都是inner,而在onMiddle1
中,target是inner
而currentTarget则是middle-1
。
同时也可以看到,冒泡的过程,onInner
先于onMiddle1
执行,最后执行onOuter
。
这就是今天要分享的内容,如有不足和错误欢迎讨论。
更多信息,参见微信官方文档