核心代码:
let input = document.getElementsByClassName("input")[0];
input.value = 'new value';
let event = new Event('input', { bubbles: true });
// hack React15
event.simulated = true;
// hack React16 内部定义了descriptor拦截value,此处重置状态
let tracker = input._valueTracker;
if (tracker) {
tracker.setValue(input);
}
input.dispatchEvent(event);
chrome插件,可以自动触发预设的一系列元素事件。
比如通过点击开始百度按钮,该插件就可以直接给百度搜索输入框填入"关键词"然后自动回车搜索。需求提炼出来其实就是自动化触发前端页面dom元素的一系列事件,主要分为两类:
- 键盘事件,比如input元素输入赋值,keyCode13回车事件
- 鼠标事件,比如click事件,mouseover事件,scroll滚动事件
1.首先从最简单的触发,只考虑原生html/js开发的网站:
input元素赋值很简单
$eventTarget.value="关键词"
触发回车事件也很简单
$eventTarget.click()
// 或者
$eventTarget.dispatchEvent(new MouseEvent('click'));
触发hover事件,类似的,都可以通过Event创建事件来dispatch触发
$eventTarget.dispatchEvent(new Event("mouseover"))
触发页面滚动事件
document.documentElement.scrollTop = 100
2.然而对于不同mvvm框架(react/vue)开发的页面如何触发的实现都或多或少有点不同。
比如对于react组件库,antd官方一个简单的form表单
import React, { Component } from 'react'
class Register extends Component {
// 在构造函数当中设置状态
constructor(props){
super(props)
this.state ={
name : '',
email:'',
password:'',
password2:'',
errors:{},//用户不合法信息提示
}
}
onChange(e){
console.log(e)
console.log(e.target.name)//(e.target.name代表你当前输入Input框对应的Name,如email,password
// e.target.value 代表当前输入的值
this.setState({
[e.target.name] : e.target.value
})
}
//提交对应的内容
onSubmit(e){
e.preventDefault()
const newUser = {
name : this.state.name,
email:this.state.email,
password:this.state.password,
password2:this.state.password2,
}
console.log(newUser)
}
render() {
return (
<div className="register">
{/* {user ? user.name : null} */}
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h1 className="display-4 text-center">注册</h1>
<p className="lead text-center">创建新的账户</p>
<form onSubmit={this.onSubmit.bind(this)}>
<div className="form-group">
<input
id="input"
type="text"
className="form-control form-control-lg input"
placeholder="用户名"
name="name"
value = {this.state.name}
onChange ={this.onChange.bind(this) }
/>
</div>
<div className="form-group">
<input
type="email"
className='form-control form-control-lg'
placeholder="邮箱地址"
name="email"
value = {this.state.email}
onChange ={this.onChange.bind(this) }
/>
<small className="form-text text-muted">测试</small>
</div>
<div className="form-group">
<input
type="password"
className='form-control form-control-lg'
placeholder="密码"
name="password"
value = {this.state.password}
onChange ={this.onChange.bind(this) }
/>
</div>
<div className="form-group">
<input type="password"
className='form-control form-control-lg'
placeholder="确认密码"
name="password2"
value = {this.state.password2}
onChange ={this.onChange.bind(this) }
/>
</div>
<input type="submit" className="btn btn-info btn-block mt-4" />
</form>
</div>
</div>
</div>
</div >
)
}
}
export default Register;
受控组件了,直接修改dom,value肯定不生效的啊?
是的
可以尝试使用dispatchEvent去触发dom的赋值行为,这是dispatch一个冒泡阶段的事件
对于15和16版本有不同的hack方法,这两种hack方法分别对应react-dom内部input元素渲染的两种机制分别是怎样的,这个simulated和valueTracker干嘛的?
带着这两个疑问开始翻看源码探究:
一个input标签的渲染:
ReactDOM.render(
<Input type="text"
value={state}
onClick={e => setState(e.target.value)}
/>,
document.getElementById("root"))