JS 自定义事件

今天在写代码时候,遇到两个组件关联的情况,最终通过 js 自定义事件进行了解决。

场景简单复现

当时的情况比较复杂。大致是使用Vue-elementUI封装了input组件,自己封装了表单元素读取与写入组件。input 组件有相应验证功能,如果是用户输入,那么没有任何问题。如果是通过自己封装的表单操作组件,直接写入input 的 value,那么没办法触发 input 的 input change 事件,也就没办法进行监控了。

把这个场景简单复原一下。

一个组件是输入框组件,有一个验证功能。

一个组件是表单修改数据组件,负责修改 input 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<form id="J_Form" action="javascript:void 0">
<input id="J_Input" name="age" value="18" />
<button id="J_Btn">修改 input 值为200</button>
</form>

<script>
const checkInputValue = el => {
el.addEventListener('change', e => {
console.log('触发 change:', e.target.value)
})
el.addEventListener('input', e => {
console.log('触发 input:', e.target.value)
})
}

const formInputSetter = (form, name, value) => {
form.elements[name].value = value
}
</script>

<script>
const $input = document.querySelector('#J_Input')
const $btn = document.querySelector('#J_Btn')
const $form = document.querySelector('#J_Form')

checkInputValue($input)

$btn.addEventListener('click', () => {
formInputSetter($form, 'age', 200)
})
</script>

代码比较简单,无需多解释。

为什么不触发 change input 事件呢?

首先说 change 事件,要想触发它,必须是 input 元素值变化了,并且从焦点状态变为非焦点状态。

在说下 input 事件,这个简单,必须是用户键盘敲击输入,类似 keypress 等事件。

很明显的,通过 js 修改 value 值,不在这两个事件范围内。

通过自定义事件解决

对此,我考虑采用自定义事件,实现广播。让第二个组件修改数值后,广播给第一个组件。

为什么不模拟广播 change 事件呢?主要是项目中 change 事件还有其他作用,如果此时模拟,会导致误触发。所以自定义一个事件来实现。

知道了原理,那么解决起来也很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<form id="J_Form" action="javascript:void 0">
<input id="J_Input" name="age" value="18" />
<button id="J_Btn">修改 input 值为200</button>
</form>

<script>
const checkInputValue = el => {
el.addEventListener('change', e => {
console.log('触发 change:', e.target.value)
})
el.addEventListener('input', e => {
console.log('触发 input:', e.target.value)
})

// 增加自定义事件接收
el.addEventListener('push', e => {
console.log('触发 push:', e)
})
}

const formInputSetter = (form, name, value) => {
form.elements[name].value = value
}
</script>

<script>
const $input = document.querySelector('#J_Input')
const $btn = document.querySelector('#J_Btn')
const $form = document.querySelector('#J_Form')

checkInputValue($input)

$btn.addEventListener('click', () => {
formInputSetter($form, 'age', 200)

// 创建并分发自定义事件
let event = new CustomEvent('push', { detail: { k1: 'a', k2: 'b' } })
$input.dispatchEvent(event)
})
</script>

通过 new CustomEvent 创建自定义事件,在进行接收就可以处理了。

这里要注意一点:自定义事件的传参,只能定义到 detail 上,其他字段不能写入。接收的时候,也是 e.detail 进行接收。

== EOF ===