Code前端首页关于Code前端联系我们

js

terry 2年前 (2023-09-07) 阅读数 186 #Vue
合成开始和合成结束事件

需求

最近需要根据输入的文本来过滤列表。这是一个非常常见的要求。所以大概的代码是:

<template>
  <div id="app">
    <input type="text" :value="filterText" @input="onInput" />
    <ul>
      <li v-for="item in filteredList" :key="item">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "app",
  data() {
    return {
      filterText: "",
      list: [
        "爱与希望",
        "花海",
        "Mojito",
        "最长的电影",
        "爷爷泡的茶"
      ]
    };
  },
  computed: {
    filteredList() {
      if (!this.filterText) {
        return this.list;
      }
      return this.list.filter(item => item.indexOf(this.filterText) > -1);
    }
  },
  methods: {
    onInput(e) {
      this.filterText = e.target.value;
    }
  }
};
</script>
 

监听输入字段中的输入事件,然后开始修改filteredList列表。

一切都是那么自然。

但是,如果输入中文,由于先出现拼音,所以在中文输入过程中触发过滤器的列表将为空,只有在最终出现中文时才会出现结果。

作文开头和结尾

于是我在网上查了一下,发现了两个事件:compositionstartcompositionend

MDN:developer.mozilla.org/en-US/docs/…

当用户开始使用拼音输入法输入汉字时,会触发组合开始事件。当段落的组装完成或中止时,会触发组装事件。

也就是说,当我们开始中文输入时,会触发一个作文开始事件。在中文输入过程中,不再触发作文开始事件,最后在中文输入完成时,触发作文结束事件。

经过实验发现,输入中文时,compositio start是在input事件之前激活的。

有了这个前提,就好办了。我只需要标记lock,当合成开始触发时,lock=true,当合成结束触发时,lock=false。仅在假锁的情况下才会执行对输入事件的过滤操作。

代码如下:

<template>
  <div id="app">
    <input type="text" :value="filterText" 
        @input="onInput" 
        @compositionstart="onCompositionStart"
        @compositionend="onCompositionEnd"
        />
    <ul>
      <li v-for="item in filteredList" :key="item">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "app",
  data() {
    return {
      filterText: "",
      list: [
        "爱与希望",
        "花海",
        "Mojito",
        "最长的电影",
        "爷爷泡的茶"
      ],
      lock; false, // 打标
    };
  },
  computed: {
    filteredList() {
      if (!this.filterText) {
        return this.list;
      }
      return this.list.filter(item => item.indexOf(this.filterText) > -1);
    }
  },
  methods: {
    onInput(e) {
      if (!this.lock) {
        this.filterText = e.target.value;
      }
    },
    onCompositionStart() {
      this.lock = true;
    },
    onCompositionEnd(e) {
      this.filterText = e.data;
      this.lock = false;
    }
  }
};
</script>
 

v型形状

上面的代码我们没有使用vue的v-model双向绑定形式。如果您使用v-model形式,您将看到输入中文时不会触发输入事件。

查看vue的源码src/platforms/web/runtime/directives/model.js,有几行代码:

export default {
  inserted (el, binding, vnode) {
    if (vnode.tag === 'select') {
      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) {
        // 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)
        if (!isAndroid) {
          el.addEventListener('compositionstart', onCompositionStart)
          el.addEventListener('compositionend', 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已经做了同样的操作,所以v-model为我们做了很多优化,这也是vue这么优秀的原因之一。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门