问题

在应用 React 绑定 input 输入框的 onChange 办法时,如果应用中文输入法(或者其余输入法),会呈现一个问题:还在输出拼音的时候,onChange 办法曾经触发了,如下,即输出过程就曾经触发了屡次 onChange 办法。如果 onChage 办法有较为简单的逻辑,就可能会带来一些用户体验或者逻辑的问题。

起因

只有有按下键盘的动作,就会触发 onChange 办法,如果输出英文就没什么问题,但应用中/日/韩等输入法的话,比方输出中文拼音曾经开始在触发 onChange 事件了。

需要及解决方案

  • 需要:等到抉择确认输出中文后,才让它触发 onChange 办法的后续操作,如扭转 value 的值。
  • 解决方案:应用 compositionEvent 事件来解决。
DOM 接口 CompositionEvent 示意用户间接输出文本(如应用输入法)时产生的事件。此接口的罕用事件有 compositionstart, compositionupdate 和 compositionend

CompositionEvent 事件介绍

  • compositionstart

当用户应用输入法如拼音输入汉字时,这个事件就会被触发,即是在用户开始非间接输出的时候触发,在非间接输出的时候完结,整个过程只触发了一次。

  • compositionupdate

事件触发于字符被输出到一段文字的时候,如在用户开始输出拼音到确定完结的过程都会触发该事件。

  • compositionend

当文本段落的组成实现或勾销时, compositionend 事件将被触发 。如用户点击拼音输入法选词确定后,则触发了该事件,此时是间接输出了,整个过程只触发了一次。

能够晓得这三个事件就把咱们输出中文拼音的三个过程进行了拆分。

实现

咱们通过监听输入法开始输出到完结的事件,即是去监听compositionstartcompositionend办法,通过设置一个变量,在两个办法外面设置 true/false,来判断是否处在中文输出拼音这个过程当中,如果是,则不触发 onChange 后续事件。

这个未必是优化。搜寻框提醒的一个很重要用途,不是帮忙省进去那么一点打字的工夫,而是为了提醒打字人应该写什么。很多时候打字者只有一个含糊的需要,全靠搜寻框提醒能力明确本人真正想搜什么。而这时候对拼音进行搜寻提醒,不说揭示用户该拼音词也能够搜到后果,单说在某种可能的用况下会防止用户搜寻整个输入法却找不到对应汉字(因为输出谬误拼音)这点就十分好用了。

如下代码,咱们通过设置个对照:value1 对应的为失常 onChange 操作,value2 对应的则是做了输入法解决

import React, { Component } from 'react'import './style.css'let isComposition = falseclass TestComposition extends Component {  constructor(props) {    super(props)    this.state = {      value1: '',      value2: '',    }    this.handleChange1 = this.handleChange1.bind(this)    this.handleChange2 = this.handleChange2.bind(this)    this.handleComposition = this.handleComposition.bind(this)  }  handleChange1 = ev => {    this.setState({      value1: ev.target.value,    })  }  handleChange2 = ev => {    // 未应用输入法或应用输入法结束能力触发    if (!isComposition) {      this.setState({        value2: ev.target.value,      })    }  }  handleComposition(ev) {    if (ev.type === 'compositionend') {      isComposition = false    } else {      isComposition = true    }  }  render() {    return (      <div>        <input type='text' onChange={this.handleChange1} />        <span>{this.state.value1}</span>        <input          type='text'          onChange={this.handleChange2}          onCompositionStart={this.handleComposition}          onCompositionEnd={this.handleComposition}          placeholder='应用了composition的input框'        />        <span>{this.state.value2}</span>      </div>    )  }}export default TestComposition

那么这样就能解决了吗?还不行。

其余浏览器不会有问题,但谷歌浏览器却不行。这里要留神的是谷歌浏览器跟其余浏览器的执行程序不同:

谷歌浏览器: compositionstart -> onChange -> compositionend

其余浏览器: compositionstart -> compositionend -> onChange

所以上述代码运行在谷歌浏览器的话,一开始中文输出咱们就将 isComposition 设置为 true,最初一步 compositionend 办法咱们才将 isComposition 复原为 true,而 onChange 曾经执行完了, 按这个逻辑中文输入法打字都改不了 input 的 value 值

if (!isComposition) {  // isComposition为false 才能够执行onChange后续逻辑  this.setState({    value2: ev.target.value,  })}

所以咱们须要对谷歌浏览器做一次特地解决:

  1. 判断是否为谷歌浏览器
  2. 如果是,则在 compositionend 办法最初再执行一次 onChange 办法

最初附上残缺代码:

import React, { Component } from 'react'let isComposition = falseconst isChrome = navigator.userAgent.indexOf('Chrome') > -1class TestComposition extends Component {  constructor(props) {    super(props)    this.state = {      value1: '',      value2: '',    }    this.handleChange1 = this.handleChange1.bind(this)    this.handleChange2 = this.handleChange2.bind(this)    this.handleComposition = this.handleComposition.bind(this)  }  handleChange1 = ev => {    this.setState({      value1: ev.target.value,    })  }  handleChange2 = ev => {    // 未应用输入法或应用输入法结束能力触发    if (!isComposition) {      this.setState({        value2: ev.target.value,      })    }  }  handleComposition(ev) {    if (ev.type === 'compositionend') {      isComposition = false      if (!isComposition && isChrome) {        this.handleChange2(ev)      }    } else {      isComposition = true    }  }  render() {    return (      <div>        <input type='text' onChange={this.handleChange1} />        <span>{this.state.value1}</span>        <input          type='text'          onChange={this.handleChange2}          onCompositionStart={this.handleComposition}          onCompositionEnd={this.handleComposition}          placeholder='应用了composition的input框'        />        <span>{this.state.value2}</span>      </div>    )  }}export default TestComposition

成果:

注:

  • Vue 自带解决了上述问题,v-model 有用到 compositionEvent 事件
  • 该做法视乎我的项目需要场景,因为如果有须要进行搜寻中文拼音过程也要提醒英文单词的来说,那就没必要了
  • 也能够应用 ref 的形式进行绑定,上述只是其中一种解决形式

参考:

  • MDN:CompositionEvent
  • 中文输入法在 React 文本输入框的非凡解决


  • ps: 集体技术博文 Github 仓库,感觉不错的话欢送 star,激励我持续写作吧~