什么时候会产生事件呢?
小程序需要经常
和用户进行某种交互,比如点击界面上的某个按钮或者区域,比如滑动了某个区域;事件是视图层到逻辑层的通讯方式;
事件可以将用户的行为反馈到逻辑层进行处理;
事件可以绑定在组件上,当触发事件时,就会执行逻辑层中对应的事件处理函数;
事件对象可以携带额外信息,如 id, dataset, touches;
事件时如何处理呢?
事件是通过bind/catch这个属性绑定在组件上的(和普通的属性写法很相似, 以key=“value”形式);
key以bind或catch开头, 从1.5.0版本开始, 可以在bind和catch后加上一个冒号;
同时在当前页面的Page构造器中定义对应的事件处理函数, 如果没有对应的函数, 触发事件时会报错;
比如当用户点击该button区域时,达到触发条件生成事件tap,该事件处理函数会被执行,同时还会收到一个
事件对象event。
事件的基本使用
如
bindtap,表示当用户点击该组件的时候会在该页面对应的 Page 中找到相应的事件处理函数。
<button size="mini" bindtap="onBtnTap">按钮button>
// index.js
// 在对应的 Page 中找到相应的事件处理函数
Page({
onBtnTap() {
console.log("按钮点击");
}
})
// index.js
Page({
onBtnTap(event) {
console.log(event);
}
})
{
"type":"tap",
"timeStamp":895,
"target": {
"id": "tapTest",
"dataset": {
"hi":"Weixin"
}
},
"currentTarget": {
"id": "tapTest",
"dataset": {
"hi":"Weixin"
}
},
"detail": {
"x":53,
"y":14
},
"touches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}],
"changedTouches":[{
"identifier":0,
"pageX":53,
"pageY":14,
"clientX":53,
"clientY":14
}]
}
某些组件会有自己特有的事件类型,大家可以在使用组件时具体查看对应的文档
比如input特有的
bindinput/bindblur/bindfocus等比如scroll-view特有的
bindscrolltowpper/bindscrolltolower等
这里我讲解几个所有组件都有的, 并且也比较常见的事件类型:
| 类型 | 触发条件 | 最低版本 |
|---|---|---|
| touchstart | 手指触摸动作开始 | |
| touchmove | 手指触摸后移动 | |
| touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | |
| touchend | 手指触摸动作结束 | |
| tap | 手指触摸后马上离开 | |
| longpress | 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 | 1.5.0 |
| longtap | 手指触摸后,超过350ms再离开(推荐使用 longpress 事件代替) |
我们刚刚有简单演示, 当某个事件触发时, 会产生一个事件对象, 并且这个对象被传入到回调函数中, 事件对象有哪些常见的属性呢?
如无特殊说明,当组件触发事件时,逻辑层绑定该事件的处理函数必定会收到一个事件对象。
BaseEvent 基础事件对象属性列表:
| 属性 | 类型 | 说明 | 基础库版本 |
|---|---|---|---|
| type | String | 事件类型 | |
| timeStamp | Integer | 事件生成时的时间戳 | |
| target | Object | 触发事件的组件的一些属性值集合 | |
| currentTarget | Object | 当前组件的一些属性值集合 | |
| mark | Object | 事件标记数据 | 2.7.1 |
这里我重点讲解一下target和currentTarget的区别
target
触发事件的源组件(当前触发事件的组件)。
| 属性 | 类型 | 说明 |
|---|---|---|
| id | String | 事件源组件的id |
| dataset | Object | 事件源组件上由data-开头的自定义属性组成的集合 |
currentTarget
事件绑定的当前组件(处理事件的组件)。
| 属性 | 类型 | 说明 |
|---|---|---|
| id | String | 当前组件的id |
| dataset | Object | 当前组件上由data-开头的自定义属性组成的集合 |
例如我们有如下一个wxml结构
<view id="view" bindtap="onViewTap" class="box">
<button id="btn" size="mini" type="primary">按钮button>
view>
给view添加如下样式
/* index.wxss */
.box {
width: 400rpx;
height: 400rpx;
background-color: skyblue;
}
展示效果如下

我们监听view的点击事件, 在相应的page文件中打印target和currentTarget
// index.js
Page({
onViewTap(event) {
console.log(event.target);
console.log(event.currentTarget);
}
})
此时点击button组件之外的view组件区域, target和currentTarget是没有任何区别的, 大致打印如下
{id: "view", offsetLeft: 0, offsetTop: 0, dataset: {…}}
{id: "view", offsetLeft: 0, offsetTop: 0, dataset: {…}}
当我们点击button组件, 会产生冒泡事件, 此时target和currentTarget就会有区别, 大致打印如下
{id: "btn", offsetLeft: 0, offsetTop: 0, dataset: {…}}
{id: "view", offsetLeft: 0, offsetTop: 0, dataset: {…}}
此时触发事件的是button组件冒泡到view组件, 因此target打印的是button组件
而此时处理事件的组件时view组件 (button组件冒泡到view组件, 由view组件处理事件), 因此currentTarget打印的是view组件
当视图层发生事件时,某些情况需要事件携带一些参数到执行的函数中, 这个时候就可以通过data-属性来完成:
格式:
data-属性的名称获取:
event.currentTarget.dataset.属性的名称, 一般是通过currentTarget获取, 某些特殊场景是通过target获取
自定义属性的基本使用
<view
bindtap="onArgumentTap"
data-name="chenyq"
data-age="18"
data-height="1.88"
>
参数传递
view>
// index.js
Page({
onArgumentTap(event) {
console.log(event.currentTarget.dataset.name);
console.log(event.currentTarget.dataset.age);
console.log(event.currentTarget.dataset.height);
}
})
我们在小程序中, 做一个下面这样的导航栏, 要求点击哪一个导航栏, 文字颜色就改变, 并且对应的标签会显示一个横条

<view class="box">
<block wx:for="{{ titles }}" wx:key="*this">
<text
bindtap="onTitleTap"
data-index="{{ index }}"
class="title {{ currentIndex === index ? 'active' : ' ' }}"
>
{{ item }}
text>
block>
view>
Page({
data: {
titles: ["衣服", "鞋子", "裤子"],
currentIndex: 0
},
onTitleTap(event) {
this.setData({
currentIndex: event.currentTarget.dataset.index
})
}
})
.active {
color: pink;
border-bottom: 3px solid pink;
}
.box {
display: flex;
justify-content: space-between;
}
.box .title {
margin: 0 10px;
padding: 10rpx 40rpx;
}
当界面产生一个事件时,事件分为了捕获阶段和冒泡阶段。
在js阶段我有写过文章详细讲解事件冒泡和事件捕获, 如果对这两个概念不清楚的可以去了解一下:
https://blog.csdn.net/m0_71485750/article/details/125112983

对于冒泡和捕获的概念我就不再过多赘述, 在这里演示一下如何阻止冒泡和捕获
bind 外,也可以用 catch 来绑定事件, 与 bind 不同的是, catch 会阻止事件向上冒泡。例如在下边这个例子中,
点击 inner view 会先后调用
onViewTap3和onViewTap2(因为 tap 事件会冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父节点传递)点击 middle view 会触发
onViewTap2点击 outer view 会触发
onViewTap1。
<view id="outer" bindtap="onViewTap1">
outer view
<view id="middle" catchtap="onViewTap2">
middle view
<view id="inner" bindtap="onViewTap3">
inner view
view>
view>
view>
catch 阻止事件传递, 因此阻止事件捕获也是使用 catch, 捕获是通过capture-bind:tap="事件函数"来监听的<view id="outer" capture-bind:tap="onCaptureTap1">
outer view
<view id="middle" capture-catch:tap="onCaptureTap1">
middle view
<view id="inner" capture-bind:tap="onCaptureTap1">
inner view
view>
view>
view>
mark是一个比较新的语法, 小程序特有的传递数据的方法 (还是推荐使用data-*来传递数据)
当事件触发时,事件冒泡路径上所有的
mark会被合并,并返回给事件回调函数。(即使事件不是冒泡事件,也会mark。)
mark基本使用
<button
size="mini"
bindtap="onBtnTap"
mark:name="chenyq"
mark:age="18"
mark:height="1.88"
>
mark使用
button>
event.mark获取Page({
onBtnTap(event) {
// 通过event.mark获取
console.log(event.mark);
}
})