第 79 题:input 搜索如何防抖,如何处理中文输入

发布于 2022-11-29 18:07:13 字数 1541 浏览 170 评论 18

防抖就不说了,主要是这里提到的中文输入问题,其实看过 elementui 框架源码的童鞋都应该知道,elementui 是通过 compositionstart & compositionend 做的中文输入处理:
相关代码:

<input
ref="input"
@compositionstart="handleComposition"
@compositionupdate="handleComposition"
@compositionend="handleComposition"
>

这3个方法是原生的方法,这里简单介绍下,官方定义如下 compositionstart 事件触发于一段文字的输入之前(类似于 keydown 事件,但是该事件仅在若干可见字符的输入之前,而这些可见字符的输入可能需要一连串的键盘操作、语音识别或者点击输入法的备选词)

简单来说就是切换中文输入法时在打拼音时(此时 input 内还没有填入真正的内容),会首先触发 compositionstart,然后每打一个拼音字母,触发 compositionupdate,最后将输入好的中文填入 input 中时触发 compositionend。触发 compositionstart 时,文本框会填入 虚拟文本(待确认文本),同时触发 input 事件;在触发 compositionend 时,就是填入实际内容后(已确认文本),所以这里如果不想触发 input 事件的话就得设置一个 bool 变量来控制。

根据上图可以看到

输入到 input 框触发 input 事件,失去焦点后内容有改变触发 change 事件,识别到你开始使用中文输入法触发 compositionstart 事件,未输入结束但还在输入中触发 compositionupdate 事件
输入完成(也就是我们回车或者选择了对应的文字插入到输入框的时刻)触发 compositionend 事件。

那么问题来了 使用这几个事件能做什么?因为 input 组件常常跟 form 表单一起出现,需要做表单验证

为了解决中文输入法输入内容时还没将中文插入到输入框就验证的问题,我们希望中文输入完成以后才验证。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

断念 2022-05-04 13:56:07

贴个完整的

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="text" />
    <script>
      function throttle(fn, delay) {
        let timer;
        let isFirstCall = true;
        return function (...args) {
          const ctx = this;
          if (isFirstCall) {
            isFirstCall = false;
            fn.apply(ctx, args);
            return;
          }
          if (timer) return;
          timer = setTimeout(() => {
            timer = null;
            fn.apply(ctx, args);
          }, delay);
        };
      }
      function input(e) {
        if (e.target.composing) {
          return;
        }
        console.log(e.target.value);
      }

      function onCompositionStart(e) {
        e.target.composing = true;
      }
      function onCompositionEnd(e) {
        const event = new CustomEvent("input");
        e.target.composing = false;
        e.target.dispatchEvent(event);
      }
      function onCompositionUpdate(e) {
        console.log(e.target.value);
      }
      const inputEle = document.getElementById("spell_input");
      inputEle.addEventListener("input", throttle(input, 1000));
      inputEle.addEventListener("compositionstart", onCompositionStart);
      inputEle.addEventListener("compositionend", onCompositionEnd);
      inputEle.addEventListener("compositionupdate", onCompositionUpdate);
    </script>
  </body>
</html>

throttle这个是节流

一身软味 2022-05-04 13:56:07

防抖

function debounce(fn, delay) {
  let timer
  return function() {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, arguments)
    }, delay)
  }
}
岁吢 2022-05-04 13:56:03

贴个完整的

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="text" />
    <script>
      function throttle(fn, delay) {
        let timer;
        let isFirstCall = true;
        return function (...args) {
          const ctx = this;
          if (isFirstCall) {
            isFirstCall = false;
            fn.apply(ctx, args);
            return;
          }
          if (timer) return;
          timer = setTimeout(() => {
            timer = null;
            fn.apply(ctx, args);
          }, delay);
        };
      }
      function input(e) {
        if (e.target.composing) {
          return;
        }
        console.log(e.target.value);
      }

      function onCompositionStart(e) {
        e.target.composing = true;
      }
      function onCompositionEnd(e) {
        const event = new CustomEvent("input");
        e.target.composing = false;
        e.target.dispatchEvent(event);
      }
      function onCompositionUpdate(e) {
        console.log(e.target.value);
      }
      const inputEle = document.getElementById("spell_input");
      inputEle.addEventListener("input", throttle(input, 1000));
      inputEle.addEventListener("compositionstart", onCompositionStart);
      inputEle.addEventListener("compositionend", onCompositionEnd);
      inputEle.addEventListener("compositionupdate", onCompositionUpdate);
    </script>
  </body>
</html>
虫児飞 2022-05-04 13:56:01

function debounce(func,wait,immediate){
// wait 延迟执行毫秒数, immediate true 表立即执行,false 表非立即执行
let timeout;
return function () {
let context = this;
let args = arguments;

if (timeout) clearTimeout(timeout);
if (immediate) {
    let callNow = !timeout;
    timeout = setTimeout(() => {
        timeout = null;
    }, wait)
    if (callNow) func.apply(context, args)
}
else {
    timeout = setTimeout(() => {
        func.apply(context, args)
    }, wait);
}

}
}

谢绝鈎搭 2022-05-04 13:56:01
// input 搜索如何防抖,如何处理中文输入
      // 事件触发后,n秒后再执行回调
      const ipt = document.querySelector(".input");

      /**
       * @param {fn} 需要防抖的函数
       * @param {time} 防抖的时间
       * */
      function debounce(fn, time) {
        let timer = 0;
        return function (args) {
          clearInterval(timer);
          timer = setTimeout(function () {
            fn.call(this, args);
          }, time);
        };
      }

      function ajax(val) {
        console.log("val", val);
      }

      const test = debounce(ajax, 1500);

      ipt.addEventListener("keyup", (e) => {
        test(e.target.value);
      });
东走西顾 2022-05-04 13:55:59

vue版本:
防抖就不说了

<template>
  <div>
    <input v-model="value" @input="input" @compositionstart="compositionstart" @compositionend="compositionend" />
  </div>
</template>

<script>
```

export default {
  name: "App",
  components: {
  },
  data() {
    return {
      value: "",
      isInputZh: ""
    };
  },
  methods: {
    input() {
      if (this.isInputZh) return;
      console.log(this.value)
    },
    compositionstart() {
      this.isInputZh = true;
    },
    compositionend() {
      this.isInputZh = false;
    }
  }
};
</script>

<style>

</style>
`
要走干脆点丶 2022-05-04 13:55:59

简易防抖

<div>
    <input type="text">
  </div>

  <script>
    let ipt = document.getElementById('ipt');
    let dbFun = debounce()
    ipt.addEventListener('keyup', function (e) {
      dbFun(e.target.value);
    })

    function debounce() {
      let timer;
      return function (value) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          console.log(value)
        }, 500);
      }
    }
  </script>

你这不对哦,不是防抖

function debounce(fn, wait) {
  var timer = null;
  return function () {
      var context = this
      var args = arguments
      if (timer) {
          clearTimeout(timer);
          timer = null;
      }
      timer = setTimeout(function () {
          fn.apply(context, args)
      }, wait)
  }
}

var fn = function () {
  console.log('boom')
}

setInterval(debounce(fn,500),1000) // 第一次在1500ms后触发,之后每1000ms触发一次

setInterval(debounce(fn,2000),1000) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)

我怎么感觉你们两个没什么区别?

哈哈哈 就是没区别

岁月静好 2022-05-04 13:55:47
<!DOCTYPE html>
<html>
  <meta charset="utf-8" />

  <body>
    <div class="control">
      <label for="name">Input:</label>
      <input type="text" name="example" />
    </div>

    <div class="event-log">
      <label>Log:</label>
      <textarea
        readonly
        class="event-log-contents"
        rows="8"
        cols="25"
      ></textarea>
      <button class="clear-log">Clear</button>
    </div>
  </body>

  <script>
    const search = document.querySelector('input[type="text"]');
    const log = document.querySelector(".event-log-contents");
    const clearLog = document.querySelector(".clear-log");
    var isInputZh = false;

    clearLog.addEventListener("click", () => {
      log.textContent = "";
    });

    search.addEventListener(
      "compositionstart",
      function(e) {
        isInputZh = true;
      },
      false
    );
    search.addEventListener(
      "compositionend",
      function(e) {
        isInputZh = false;
        log.textContent += e.data;
      },
      false
    );
    search.addEventListener(
      "keyup",
      function(e) {
        if (!isInputZh) {
          log.textContent = e.target.value;
        }
        console.log("input: "+log.textContent);
      },
      false
    );
  </script>
</html>

我发现需要注意的是input事件是会在compositionend之前被触发:
Screen Shot 2020-03-06 at 2 27 51 AM
Screen Shot 2020-03-06 at 2 28 22 AM

有问题欢迎指正!

听不够的曲调 2022-05-04 13:55:13

简易防抖

<div>
    <input type="text">
  </div>

  <script>
    let ipt = document.getElementById('ipt');
    let dbFun = debounce()
    ipt.addEventListener('keyup', function (e) {
      dbFun(e.target.value);
    })

    function debounce() {
      let timer;
      return function (value) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          console.log(value)
        }, 500);
      }
    }
  </script>

你这不对哦,不是防抖

function debounce(fn, wait) {
  var timer = null;
  return function () {
      var context = this
      var args = arguments
      if (timer) {
          clearTimeout(timer);
          timer = null;
      }
      timer = setTimeout(function () {
          fn.apply(context, args)
      }, wait)
  }
}

var fn = function () {
  console.log('boom')
}

setInterval(debounce(fn,500),1000) // 第一次在1500ms后触发,之后每1000ms触发一次

setInterval(debounce(fn,2000),1000) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)

我怎么感觉你们两个没什么区别?

套路撩心 2022-05-04 13:52:09

简易防抖

<div>
    <input type="text">
  </div>

  <script>
    let ipt = document.getElementById('ipt');
    let dbFun = debounce()
    ipt.addEventListener('keyup', function (e) {
      dbFun(e.target.value);
    })

    function debounce() {
      let timer;
      return function (value) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          console.log(value)
        }, 500);
      }
    }
  </script>

你这不对哦,不是防抖

function debounce(fn, wait) {
  var timer = null;
  return function () {
      var context = this
      var args = arguments
      if (timer) {
          clearTimeout(timer);
          timer = null;
      }
      timer = setTimeout(function () {
          fn.apply(context, args)
      }, wait)
  }
}

var fn = function () {
  console.log('boom')
}

setInterval(debounce(fn,500),1000) // 第一次在1500ms后触发,之后每1000ms触发一次

setInterval(debounce(fn,2000),1000) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)
泪是无色的血 2022-05-04 13:26:44

简易防抖

<div>
    <input type="text">
  </div>

  <script>
    let ipt = document.getElementById('ipt');
    let dbFun = debounce()
    ipt.addEventListener('keyup', function (e) {
      dbFun(e.target.value);
    })

    function debounce() {
      let timer;
      return function (value) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          console.log(value)
        }, 500);
      }
    }
  </script>

我看怎么像节流

冰雪梦之恋 2022-05-04 12:25:45

参考vue源码对v-model的实现中,对输入中文的处理

<input id='myinput'>
    function jeiliu(timeout){
        var timer;
        function input(e){
        if(e.target.composing){
            return ;
        }
        if(timer){
           clearTimeout(timer);
        }
        timer = setTimeout(() => {
               console.log(e.target.value);
               timer = null;
           }, timeout); 
        }
        return input;
    }

    function onCompositionStart(e){
        e.target.composing = true;
    }
    function onCompositionEnd(e){
        //console.log(e.target)
        e.target.composing = false;
        var event = document.createEvent('HTMLEvents');
        event.initEvent('input');
        e.target.dispatchEvent(event);
    }
    var input_dom = document.getElementById('myinput');
    input_dom.addEventListener('input',jeiliu(1000));
    input_dom.addEventListener('compositionstart',onCompositionStart);
    input_dom.addEventListener('compositionend',onCompositionEnd);

'jieliu',拼音没有那么棒棒hhh

污味仙女 2022-05-04 10:47:55

参考vue源码对v-model的实现中,对输入中文的处理

<input id='myinput'>
    function jeiliu(timeout){
        var timer;
        function input(e){
        if(e.target.composing){
            return ;
        }
        if(timer){
           clearTimeout(timer);
        }
        timer = setTimeout(() => {
               console.log(e.target.value);
               timer = null;
           }, timeout); 
        }
        return input;
    }

    function onCompositionStart(e){
        e.target.composing = true;
    }
    function onCompositionEnd(e){
        //console.log(e.target)
        e.target.composing = false;
        var event = document.createEvent('HTMLEvents');
        event.initEvent('input');
        e.target.dispatchEvent(event);
    }
    var input_dom = document.getElementById('myinput');
    input_dom.addEventListener('input',jeiliu(1000));
    input_dom.addEventListener('compositionstart',onCompositionStart);
    input_dom.addEventListener('compositionend',onCompositionEnd);
遮云壑 2022-05-04 07:17:45

简易防抖

<div>
    <input type="text">
  </div>

  <script>
    let ipt = document.getElementById('ipt');
    let dbFun = debounce()
    ipt.addEventListener('keyup', function (e) {
      dbFun(e.target.value);
    })

    function debounce() {
      let timer;
      return function (value) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          console.log(value)
        }, 500);
      }
    }
  </script>
°如果伤别离去 2022-05-04 06:35:33

这个在复制粘贴中文内容的时候不会触发事件

有一个onpaste 事件

但可醉心 2022-05-04 01:44:43

这个在复制粘贴中文内容的时候不会触发事件

遇到 2022-05-03 05:56:33

165b363b21f8b414

~没有更多了~

关于作者

じее

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文