Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Loading-m opened this issue May 22, 2019 · 19 comments
Open

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

Loading-m opened this issue May 22, 2019 · 19 comments
Labels

Comments

@Loading-m
Copy link

防抖就不说了,主要是这里提到的中文输入问题,其实看过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变量来控制。
image
根据上图可以看到

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

那么问题来了 使用这几个事件能做什么?
因为input组件常常跟form表单一起出现,需要做表单验证
image
为了解决中文输入法输入内容时还没将中文插入到输入框就验证的问题

我们希望中文输入完成以后才验证

@Loading-m
Copy link
Author

165b363b21f8b414

@MarioJames
Copy link

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

@yygmind yygmind changed the title input 搜索如何防抖,如何处理中文输入 第 79 题:input 搜索如何防抖,如何处理中文输入 May 22, 2019
@Komorebi94
Copy link

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

有一个onpaste 事件

@sohoorc
Copy link

sohoorc commented May 28, 2019

简易防抖

<div>
    <input type="text" id="ipt">
  </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>

@halionn
Copy link

halionn commented Jun 26, 2019

@zzNire
Copy link

zzNire commented Aug 12, 2019

参考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);

@spongege
Copy link

参考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

@TheHtmler
Copy link

简易防抖

<div>
    <input type="text" id="ipt">
  </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>

我看怎么像节流

@Hideer
Copy link

Hideer commented Feb 29, 2020

简易防抖

<div>
    <input type="text" id="ipt">
  </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) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)

@DanielXuuuuu
Copy link

简易防抖

<div>
    <input type="text" id="ipt">
  </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) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)

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

@SerinaJingyiLu
Copy link

<!DOCTYPE html>
<html>
  <meta charset="utf-8" />

  <body>
    <div class="control">
      <label for="name">Input:</label>
      <input type="text" id="example" 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

有问题欢迎指正!

@baoleilei
Copy link

baoleilei commented Mar 16, 2020

vue版本:
防抖就不说了

<template>
  <div id="app">
    <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>
`

@JiangMengLei
Copy link

简易防抖

<div>
    <input type="text" id="ipt">
  </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) // 不会触发一次(我把函数防抖看出技能读条,如果读条没完成就用技能,便会失败而且重新读条)

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

哈哈哈 就是没区别

@xuyaodegithub
Copy link

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);
}

}
}

@promotion-xu
Copy link

// 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);
      });

@mbaxszy7
Copy link

mbaxszy7 commented Sep 7, 2020

贴个完整的

<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" id="spell_input" />
    <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>

@XuedaoYuan
Copy link

贴个完整的

<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" id="spell_input" />
    <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这个是节流

@eJayYoung
Copy link

eJayYoung commented Feb 23, 2021

防抖

function debounce(fn, delay) {
  let timer
  return function() {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, arguments)
    }, delay)
  }
}

@Yangfan2016
Copy link

  1. 防抖
function debounce(fn, delay) {
    let timer = null;
    return function(...args) {
        const ctx = this;
        if (timer) {
            clearTimeout(timer);
        } else {
            timer = setTimeout(()=>{
                fn.bind(ctx, ...args);
            }
            , delay);
        }
    }
}

const handlerChange=(ev)=>{console.log(ev.target.value)}
const handlerDebChange=debounce(handlerChange,1e3);

<input  onChange={handlerDebChange} />
  1. 通过 合成事件 来判断区分 非英文 输入法
    可以通过一个 flag isInCompos 来判断 字符真正输入到 input 或 textarea 中的时机
    compositionstart isInCompos=true
    compositionupdate isInCompos=true
    compositionend isInCompos=false

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests