完美的输入框监听计划:兼容、高效和组合输入友爱

文章分享了,怎样一步步优化输入框的监听事宜,以到达兼容、高效和组合输入友爱等目标。

完美的输入框监听计划

keyup

监听输入框的输入,最原始的要领是运用keyup事宜。
不运用change事宜,它只会在输入框落空核心后被触发。
此体式格局兼容性广,但效力较低,毕竟恣意的按键都邑触发该事宜。

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

<script>
  document.querySelector('#input')
  .addEventListener('keyup', function() {
    console.log('value:', this.value);
  });
</script>

input

我们只愿望当值发作变化后再触发监听,如许,input事宜涌现了。
它只会在输入框的值发作变化后被触发,不过IE8及以下不支撑该事宜。

网上很多人运用IE独占的propertychange事宜,作为替换input的计划,这里不引荐。
一方面它会在恣意属性值变化后被触发,没有专一性,不够语义,比较糟蹋。
二方面网上都是用jQuery等东西库操纵,比较简单,而我们的目标是用原生代码完成。
三方面是不支撑input事宜的浏览器已很少了,硬碰上了就用keyup应付。

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

<script>
  document.querySelector('#input')
  .addEventListener('input', function() {
    console.log('value:', this.value);
  });
</script>

接着上步,怎样在不支撑input事宜时运用keyup事宜呢?
直接检测事宜不太靠谱,能够应用inputkeyup之前发作的性子,奇妙的完成此功用。

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

<script>
  let inInputEvent = false;
  let input = document.querySelector('#input');
  
  input.addEventListener('keyup', function() {
    if (inInputEvent) {
      // You can remove keyup listener.
    } else {
      console.log('keyup:', this.value);
    }
  });
  input.addEventListener('input', function() {
    if (!inInputEvent) inInputEvent = true;
    console.log('input:', this.value);
  });
</script>

耽误函数

在搜刮功用中,理想化的情形是当用户悉数输入后,再马上实行搜刮。
那末题目来了,怎样在不须要用户点击搜刮按钮的情况下,得知其历程的完成呢?没有办法。
虽然没有办法,但有优化的体式格局:假定用户每一个单词的输入距离,以此时间耽误实行搜刮功用。

英文平常为 300ms ,中文可设置成 500ms 。

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

<script>
  let input = document.querySelector('#input');
  let trigger = createDelayFunction(console.log);
  
  input.addEventListener('input', function() {
    trigger(this.value);
  });

  function createDelayFunction(fn, timeout = 300) {
    let timeoutId = -1;
    return (...args) => {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        fn.apply(null, args);
      }, timeout);
    }
  }
</script>

composition

中文、日文等须要借助输入法组合输入,即便是英文,如今也可借助组合输入举行选词等。
现实中,我们愿望将用户组合输入完的一段笔墨,而不是每输入一个字母,算做一次输入的完成。

组合输入事宜应运而生,经常使用的是compositionstart(组输最先)和compositionend(组输完毕)事宜。
连系组合事宜不监听一般的输入,以及compositionstart发作在input事宜之前,能够云云优化中文输入。

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

<script>
  let inCompositionEvent = false;
  let input = document.querySelector('#input');
  
  input.addEventListener('input', function() {
    !inCompositionEvent && console.log('input', this.value);
  });
  input.addEventListener('compositionstart', function() {
    inCompositionEvent = true;
  });
  input.addEventListener('compositionend', function() {
    inCompositionEvent = false;
    console.log('composition', this.value);
  });
</script>

连系

末了是连系以上几步天生一个融会要领,代码加示例:地点

内里还做了些加强:
比方监听函数返回的是一个,移除这一步所加的一切事宜的要领。
比方设置是不是监听组合输入事宜,由于好的搜刮框会直接依据拼音最先搜刮,无需比及汉字的构成。

代码运用ES6语法,需运用支撑ES6的浏览器(Chrome最新版)或转码后才运用,体谅。

function listenInput(dom, callback, {
  timeout = 300,
  useCompositionEvent = true
} = {}) {
  let value = '';
  let inInputEvent = false;
  let inCompositionEvent = false;
  let trigger = createDelayFunction(valueChanged, timeout);

  // Return a function that can remove listeners added here.
  return enabledEvent(dom);

  function valueChanged(val) {
    if (val === value) {
      return ;
    } else {
      value = val;
    }

    callback(value, { dom: dom });
  }

  function enabledEvent(dom) {
    dom.addEventListener('keyup', keyup);
    dom.addEventListener('input', input);
    useCompositionEvent && dom.addEventListener('compositionstart', compositionstart);
    useCompositionEvent && dom.addEventListener('compositionend', compositionend);

    return function() {
      dom.removeEventListener('keyup', keyup);
      dom.removeEventListener('input', input);
      useCompositionEvent && dom.removeEventListener('compositionstart', compositionstart);
      useCompositionEvent && dom.removeEventListener('compositionend', compositionend);
    };

    function keyup() {
      if (inInputEvent) {
        dom.removeEventListener('keyup', keyup);
      } else {
        trigger(this.value);
      }
    }
    
    function input() {
      if (!inInputEvent) inInputEvent = true;
      if (!inCompositionEvent) trigger(this.value);
    }
    
    function compositionstart() {
      inCompositionEvent = true;
    }
    
    function compositionend() {
      inCompositionEvent = false;
      trigger(this.value);
    }
  }
}

function createDelayFunction(fn, timeout = 300) {
  let timeoutId = -1;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      fn.apply(null, args);
    }, timeout);
  }
}
    原文作者:wmaker
    原文地址: https://segmentfault.com/a/1190000015478085
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞