---
url: /guide/basic/event.md
---
# 事件处理 {#event-handling}

Mpx 在事件处理上基于原生小程序，支持原生小程序的全部事件处理技术规范，在此基础上新增了事件处理内联传参的增强机制。

## 事件分类 {#event-classification}

事件分为冒泡事件和非冒泡事件：

1. 冒泡事件：当一个组件上的事件被触发后，该事件会向父节点传递
2. 非冒泡事件：当一个组件上的事件被触发后，该事件不会向父节点传递

常用事件如下:

| 事件类型    | 触发条件                                                       |
| ----------- | -------------------------------------------------------------- |
| touchstart  | 手指触摸动作开始                                               |
| touchmove   | 手指触摸后移动                                                 |
| touchcancel | 手指触摸动作被打断，如来电提醒，弹窗                           |
| touchend    | 手指触摸动作结束                                               |
| tap         | 手指触摸后马上离开                                             |
| longpress   | 手指触摸后，超过 350ms 再离开，推荐使用 longpress 代替 longtap |
| longtap     | 手指触摸后，超过 350ms 再离开                                  |

## 事件绑定方式 {#event-binding}

### 基础绑定 {#basic-binding}

事件绑定的写法同组件的属性，以 key、value 的形式：

1. `bind` 绑定：

```html
<view bindtap="handleTap">点击事件</view>
<!-- 也可以写成 -->
<view bind:tap="handleTap">点击事件</view>
```

2. `catch` 绑定（阻止冒泡）：

```html
<view catchtap="handleTap">阻止冒泡的点击事件</view>
<!-- 也可以写成 -->
<view catch:tap="handleTap">阻止冒泡的点击事件</view>
```

### 捕获阶段绑定 {#capture-binding}

捕获阶段位于冒泡阶段之前，且在捕获阶段中，事件到达节点的顺序与冒泡阶段恰好相反。需要在捕获阶段监听事件时，可以采用 `capture-bind`、`capture-catch` 关键字：

```html
<view capture-bind:tap="handleCapture">捕获阶段事件</view>
<view capture-catch:tap="handleCaptureAndStop">捕获阶段且停止传递</view>
```

### 动态事件绑定 {#dynamic-event-binding}

```html
<!-- 支持传入动态数据 -->
<view wx:for="{{items}}" bindtap="handleTap_{{index}}"> {{item}}</view>

<script>
  import { createComponent } from '@mpxjs/core'
  createComponent({
    data: {
      items: ['Item 1', 'Item 2', 'Item 3', 'Item 4']
    },
    methods: {
      const handleTap_0 = (event) => {
        console.log('Tapped on item 1')
      },

      const handleTap_1 = (event) => {
        console.log('Tapped on item 2')
      },

      const handleTap_2 = (event) => {
        console.log('Tapped on item 3')
      },

      const handleTap_3 = (event) => {
        console.log('Tapped on item 4')
      }
    }
  })
</script>
```

## 事件对象 {#event-object}

当事件回调触发时，会收到一个事件对象，它的详细属性如下：

| 属性           | 类型    | 说明                                         |
| -------------- | ------- | -------------------------------------------- |
| type           | String  | 事件类型                                     |
| timeStamp      | Integer | 事件生成时的时间戳                           |
| target         | Object  | 触发事件的组件的一些属性值集合               |
| currentTarget  | Object  | 当前组件的一些属性值集合                     |
| detail         | Object  | 额外的信息                                   |
| touches        | Array   | 触摸事件，当前停留在屏幕中的触摸点信息的数组 |
| changedTouches | Array   | 触摸事件，当前变化的触摸点信息的数组         |

### Target 和 CurrentTarget {#target-and-currenttarget}

* `target` 是触发事件的源组件
* `currentTarget` 是事件绑定的当前组件

两者都具备以下属性：
| 属性 | 类型 | 说明 |
|------|------|------|
| id | String | 元素的 id 属性 |
| dataset | Object | 包含所有以 data- 开头的自定义属性的对象集合，dataset 属性会自动将 data- 后面的属性名转换为驼峰命名。 例如：data-user-name 会变成 dataset.userName |

```html
<!-- 点击 inner-view 时 -->
<view id="outer-view" bindtap="handleTap">
  outer-view
  <view id="inner-view"> inner-view </view>
</view>
```

上述示例中，点击 inner-view 时：

* `e.target.id` 为 inner-view
* `e.currentTarget.id` 为 outer-view

### touches 和 changedTouches {#touches-and-changedtouches}

touches 数组中每个元素包含以下属性
| 属性 | 说明 |
|------|------|
| identifier | 触摸点的标识符 |
| pageX, pageY | 距离文档左上角的距离 |
| clientX, clientY | 距离页面可显示区域左上角的距离 |

### detail

自定义事件所携带的数据，如表单组件的提交事件会携带用户的输入，媒体的错误事件会携带错误信息等。点击事件的 detail 带有的 x, y 同 pageX, pageY 代表距离文档左上角的距离。

## 事件参数传递 {#event-param-passing}

Mpx 提供了比原生小程序更强大的事件传参能力，支持以下几种传参方式：

### 基础传参 {#basic-param-passing}

使用data-\*属性进行事件传参

```html
<!--通过dataset进行事件传参-->
<view data-name="basic" bindtap="handleTap">基础传参</view>

<script>
  import { createComponent } from '@mpxjs/core'
  createComponent({
    methods: {
      handleTap(e) {
        console.log('name:', e.target.dataset.name)
      }
    }
  })
</script>
```

### 模板内联传参 {#template-inline-param-passing}

Mpx增强语法，支持在模板中使用方法调用的形式直接传递参数，方便简洁。参数支持传递字面量、组件数据以及 for 作用域下的 item/index。

```html
<!-- 传递字面量 -->
<view bindtap="handleTapInline('inline')">内联传参</view>

<!-- 传递组件数据 -->
<view bindtap="handleTapInline(text)">{{text}}</view>

<!-- 传递 for 作用域下的 item -->
<view wx:for="{{items}}" bindtap="handleTapInline(item)">{{item}}</view>

<script>
  import { createComponent } from '@mpxjs/core'
  createComponent({
    data: {
      text: 'dynamic text',
      items: ['Item 1', 'Item 2', 'Item 3', 'Item 4']
    },
    methods: {
      handleTapInline(params) {
        console.log('params:', params)
      }
    }
  })
</script>
```

#### 内联传参中访问 event 对象 {#use-event-object}

```html
<!-- 获取 event 对象 -->
<view bindtap="handleTapInlineWithEvent($event, 'inline')">获取event对象</view>

<script>
  import { createComponent } from '@mpxjs/core'
  createComponent({
    methods: {
      handleTapInlineWithEvent(event, params) {
        console.log('event:', event)
        console.log('params:', params)
      }
    }
  })
</script>
```

## Mpx 事件代理 {#event-proxy-enhancement}

在 Mpx 中，当使用了事件内联传参、`wx:model` 双向绑定等框架增强特性时，Mpx 会自动使用事件代理的方式进行事件绑定。在这种模式下，除了触发用户绑定的事件处理函数外，还会调用 `mpx.config.proxyEventHandler` 全局事件处理函数。

此外，Mpx 也支持通过添加 `.proxy` 修饰符来强行使用事件代理模式。当你希望通过 `proxyEventHandler` 全局拦截某些普通事件时，可以使用该方式强制开启事件代理。

示例：

```html
<!-- 强制开启事件代理 -->
<view bindtap.proxy="handleTap">代理事件</view>
```

当点击时，除了执行 `handleTap`，还会触发 `Mpx.config.proxyEventHandler(event, componentIns)`。

更多细节见 [proxyEventHandler](../../api/app-config.md#proxyeventhandler)。
