在js中触发onchange事件的最好方法是什么?
美好的一天。
我们使用backbone + reactjs包来构build客户端应用程序。 严重依赖臭名昭着的valueLink我们通过自己的包装器直接向模型传递值,支持反应js接口的双向绑定。
现在我们面临这个问题:
我们有jquery.mask.js插件,它以编程方式格式化input值,因此它不会触发React事件。 所有这些都会导致模型从用户input中接收到未格式化的值,并从插件中忽略格式化的值。
看来,React有很多事件处理策略取决于浏览器。 有什么常见的方法来触发特定DOM元素的更改事件,以便React能够听到它?
至less在文本input上,看起来onChange
正在监听input事件:
var event = new Event('input', { bubbles: true }); element.dispatchEvent(event);
我知道这个答案有点晚,但我最近面临类似的问题。 我想触发一个嵌套组件的事件。 我有一个无线电和checkboxtypes小部件(他们是行为像checkbox和/或单选button)的列表,并在应用程序的其他地方,如果有人closures工具箱,我需要取消选中一个。
我发现了一个非常简单的解决scheme,不知道这是否是最佳实践,但它的工作原理。
var event = new MouseEvent('click', { 'view': window, 'bubbles': true, 'cancelable': false }); var node = document.getElementById('nodeMyComponentsEventIsConnectedTo'); node.dispatchEvent(event);
这触发了domNode上的点击事件,我的处理程序通过反应连接确实被调用,所以它的行为就像我所期望的,如果有人点击元素。 我没有testingonChange,但它应该工作,并不知道如何这将公平的真正的老版本的IE浏览器,但我相信至lessIE9和支持MouseEvent。
因为我的组件非常小(我的应用程序中只有一部分被使用,因为我还在学习它),所以我最终从这个特殊的用例中移开了,我可以通过另一种方式实现同样的事情,而不需要引用dom节点。
更新:
正如其他人在评论中所述,最好使用this.refs.refname
来获取对dom节点的引用。 在这种情况下,refname是通过<MyComponent ref='refname' />
连接到组件的<MyComponent ref='refname' />
。
对于React 16和React> = 15.6
Setter .value=
不能正常工作,因为React库会覆盖input值设置器,但我们可以直接在input
上调用函数作为上下文。
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; nativeInputValueSetter.call(input, 'react 16 value'); var ev2 = new Event('input', { bubbles: true}); input.dispatchEvent(ev2);
对于textarea元素,你应该使用HTMLTextAreaElement
类的prototype
。
新的codepen示例 。
所有信贷给这个贡献者和他的解决scheme
仅适用于React <= 15.5的过时答案
通过react-dom ^15.6.0
您可以在事件对象上使用simulated
标志来让事件通过
var ev = new Event('input', { bubbles: true}); ev.simulated = true; element.value = 'Something new'; element.dispatchEvent(ev);
我用一个例子做了一个codepen
要理解为什么需要新标志我发现这个评论非常有帮助:
React中的input逻辑现在重复数据删除的变化事件,所以它们不会每个值触发一次以上。 它监听浏览器onChange / onInput事件以及设置DOM节点值prop(当你通过javascript更新值)。 这有副作用,如果你手动更新input的值input.value ='foo'然后用{target:input}分派一个ChangeEvent React将注册set和事件,看它的值仍然是''foo ',认为这是一个重复的事件,并吞下去。
这在正常情况下工作正常,因为一个“真正的”浏览器启动的事件不触发element.value集。 您可以通过用模拟标记标记您触发的事件来暗中摆脱这种逻辑,反应将始终触发事件。 https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128
您可以使用ReactTestUtils来模拟事件,但这是为unit testing而devise的。
我build议不要在这种情况下使用valueLink,只是简单地听取插件触发的更改事件,并更新input的状态作为响应。 双向绑定的应用程序更像是一个演示,而不是其他任何东西; 它们只是为了强调纯双向绑定不适用于大多数应用程序,而且通常需要更多的应用程序逻辑来描述应用程序中的交互。
在任意元素上触发更改事件会造成组件之间的依赖关系,这是很难推理的。 坚持使用React的单向数据stream更好。
没有简单的片段来触发React的变化事件。 该逻辑在ChangeEventPlugin.js中实现,不同的inputtypes和浏览器有不同的代码分支。 此外,不同版本的React的实现细节也不尽相同。
我已经构build了反应触发器更改来完成这个任务,但是它的目的是用于testing,而不是作为生产依赖项:
let node; ReactDOM.render( <input onChange={() => console.log('changed')} ref={(input) => { node = input; }} />, mountNode ); reactTriggerChange(node); // 'changed' is logged
CodePen
如果您使用Backbone和React,我会推荐以下其中一项,
- Backbone.React.Component
- react.backbone
他们都有助于将Backbone模型和集合与React视图集成在一起。 您可以像使用Backbone视图一样使用Backbone事件。 我已经涉足了两个,除了一个是mixin,另外一个将React.createClass
改为React.createBackboneClass
之外,没有看到太大的区别。
既然我们使用函数来处理onchange事件,我们可以这样做:
class Form extends Component { constructor(props) { super(props); this.handlePasswordChange = this.handlePasswordChange.bind(this); this.state = { password: '' } } aForceChange() { // something happened and a passwordChange // needs to be triggered!! // simple, just call the onChange handler this.handlePasswordChange('my password'); } handlePasswordChange(value) { // do something } render() { return ( <input type="text" value={this.state.password} onChange={changeEvent => this.handlePasswordChange(changeEvent.target.value)} /> ); } }
扩展Grin / Dan Abramov的答案,这个工作可以跨多种inputtypes。 testingReact> = 15.5
const inputTypes = [ window.HTMLInputElement, window.HTMLSelectElement, window.HTMLTextAreaElement, ]; export const triggerInputChange = (node, value = '') => { // only process the change on elements we know have a value setter in their constructor if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) { const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set; const event = new Event('input', { bubbles: true }); setValue.call(node, value); node.dispatchEvent(event); } };