对React Vue Angular项目的页面模拟输入input 来实现自动输入功能

2020/12/17 9:23:37webframework

先贴正确代码:

function inputValue(el, value) {
    const prop = HTMLInputElement.prototype
    const setValue = Object.getOwnPropertyDescriptor(prop, 'value').set
    setValue.call(el, value);
    const event = new Event('input', { bubbles: true })
    el.dispatchEvent(event)
}

教程来源:https://www.zhihu.com/question/273667163?sort=created
getOwnPropertyDescriptor 教程:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor

一般页面的输入框,只需要按照下面方法就可以实现自动输入:

input.value = 'v'
const event = new Event('input', {bubbles: true})
input.dispatchEvent(event)

理想情况下,这段代码应该会模拟一个 DOM input 事件,然后触发 React 的 change 事件。然而事实上却没有触发 change 事件.

原因是 ReactDOM 在模拟 onChange 事件的时候有一个逻辑:只有当 input 的 value 改变,ReactDOM 才会产生 onChange 事件。ReactDOM 里面有个 inputValueTracking 类是做这个事情的,其实现原理是使用一个变量 currentValue 存储 input 的最新值,并覆盖掉 input 的 value getter。当其他地方执行了 input.value = xxx 时,会执行 value getter,setter 里面用新 value 更新 currentValue。

当 DOM input 事件触发时,inputValueTracking 会判断 currentValue 和 input 的真实值是不是一致的,如果不一致,就说明发生了变化。只有发生了变化,上层调用方才会模拟出一个 change 事件。

答案是在 input.value = 'v' 这一步,想办法绕过 inputValueTracking 里面覆写的 value setter. ReactDOM 不是把 value setter 覆盖了吗,那么直接拿到原始的 value setter,call 调用就行。