输入框事件compositionstart和compositionend的妙用

前段时间被这样一个bug给折磨到,在ios手机端,切换中文输入法输入打拼音到input输入框的时候,发现打拼音的时候也会频繁触发input事件以至于输入框一直处于抖动状态,加上防抖截流也无济于事反而会让用户觉得特别的卡顿。

问题排查:

1.公司项目用的是taro进行开发,所以是taro的问题吗?

查看了源码,发现taro只是做了一层套娃,底层是react框架

2.那react框架问题吗?

写了个react的demo发现,react其实也有这样的问题

const Demo=()=>{
  const [value,setValue]=useState("");
  return (
    <>
      <input onInput={(e)=>{
        console.log("value:",e.target.value)
        setValue(e.target.value)
      }}/>
      <span>{value}</span>
    </>
  )
}

 看过react的源码的人都会知道,react对于原生dom事件其实也是没有过多的去处理,于是我做了一下论证

3.是不是元素dom事件就是这样的呢?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" >
    <script>
        const input=document.querySelector('input')
        input.addEventListener('input',function(e){
            console.log("e",e)
        })
    </script>
</body>
</html>

验证结果如下确实如此 

 那么接下来就轮到主角上场了

compositionstart

文本合成系统如 input method editor(即输入法编辑器)开始新的输入合成时会触发 compositionstart 事件。

例如,当用户使用拼音输入法开始输入汉字时,这个事件就会被触发。

compositionend

当文本段落的组成完成或取消时,compositionend 事件将被触发 (具有特殊字符的触发,需要一系列键和其他输入,如语音识别或移动中的字词建议)。

于是我对react代码的input输入框添加了以上两方法

const Demo = () => {
  const [value, setValue] = useState("");
  return (
    <>
      <input
        onCompositionStart={(e) => {
          console.log("onCompositionStart")
        }}
        onInput={(e) => {
          console.log("value:", e.target.value);
          setValue(e.target.value);
        }}
      />
      <span>{value}</span>
    </>
  );
};

当切换到中文输入到时候 compositionstart事件触发了

 当输入完成的时候compositionend触发了

当切换会英文输入的时候,就不会触发 

 于是乎对代码做了这样的改造:

const Demo = () => {
  const [value, setValue] = useState("");
  return (
    <>
      <input
        onCompositionStart={(e) => {
          console.log("onCompositionStart")
          e.target.composing = true
        }}
        onCompositionEnd={(e) => {
          console.log("onCompositionEnd",e.target.value)
          if(!e.target.composing)return;
          (e.target.composing = false)
          setValue(e.target.value);
        }}
        onInput={(e) => {
          if(e.target.composing) return;
          console.log("value:", e.target.value);
          setValue(e.target.value);
        }}
      />
      <span>{value}</span>
    </>
  );
};

 

 

也就完全解决了中文输入的时候输入框问题

以上解决思路其实来源于vue源码v-model的指令实现的时候对输入框事件做了的处理

const directive = {
  inserted (el, binding, vnode, oldVnode) {
    if (vnode.tag === 'select') {
      // #6903
      if (oldVnode.elm && !oldVnode.elm._vOptions) {
        mergeVNodeHook(vnode, 'postpatch', () => {
          directive.componentUpdated(el, binding, vnode)
        })
      } else {
        setSelected(el, binding, vnode.context)
      }
      el._vOptions = [].map.call(el.options, getValue)
    } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
      el._vModifiers = binding.modifiers
      if (!binding.modifiers.lazy) {
        el.addEventListener('compositionstart', onCompositionStart)
        el.addEventListener('compositionend', onCompositionEnd)
        // Safari < 10.2 & UIWebView doesn't fire compositionend when
        // switching focus before confirming composition choice
        // this also fixes the issue where some browsers e.g. iOS Chrome
        // fires "change" instead of "input" on autocomplete.
        el.addEventListener('change', onCompositionEnd)
        /* istanbul ignore if */
        if (isIE9) {
          el.vmodel = true
        }
      }
    }
  },
function onCompositionStart (e) {
  e.target.composing = true
}

function onCompositionEnd (e) {
  // prevent triggering an input event for no reason
  if (!e.target.composing) return
  e.target.composing = false
  trigger(e.target, 'input')
}

function trigger (el, type) {
  const e = document.createEvent('HTMLEvents')
  e.initEvent(type, true, true)
  el.dispatchEvent(e)
}

由此看出鱿大大,在写vue框架的时候是多么细心,这些细节都考虑到了

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值