当前位置: 首页 > article >正文

合成事件在san.js中的应用

一、 什么是合成事件

DOM3 Event 新增了合成事件(CompositionEvent ), 用于处理通常使用 IME 输入时的复杂输入序列。

二、合成事件常见事件

  1. compositionstart:文本合成系统如 IME(即输入法编辑器)开始新的输入合成时会触发 compositionstart 事件。
  2. compositionupdate :事件触发于字符被输入到一段文字的时候
  3. compositionend:当文本段落的组成完成或取消时,compositionend 事件将被触发

合成事件在很多方面与输入事件很类似。在合成事件触发时,事件目标是接收文本的输入字段。唯
一增加的事件属性是 data,其中包含的值视情况而异:

  • 在 compositionstart 事件中,包含正在编辑的文本(例如,已经选择了文本但还没替换);
  • 在 compositionupdate 事件中,包含要插入的新字符;
  • 在 compositionend 事件中,包含本次合成过程中输入的全部内容。

与文本事件类似,合成事件可以用来在必要时过滤输入内容。可以像下面这样使用合成事件:

<input id="myText" type="text"/>
 <script>![请添加图片描述](https://img-blog.csdnimg.cn/481e6c9238ad45dcaabedfb9adcbff26.png)

     let textbox = document.getElementById("myText");
     textbox.addEventListener("compositionstart", (event) => {
         console.log('compositionstart', event.data);
     });
     textbox.addEventListener("compositionupdate", (event) => {
         console.log('compositionupdate', event.data);
     });

     textbox.addEventListener("compositionend", (event) => {
         console.log('compositionend', event.data);
     });
 </script>

请添加图片描述

三、合成事件的使用场景

需求背景:根据input输入的文字进行列表过滤。
需求实现

<template>
  <div class="kh-idx">
    <input v-model="inputVal" type="text" />
    <ul class="kh-idx-ul">
      <li v-for="item in filteredList" :key="item" class="kh-idx-li">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'KhIndex',
  setup() {
    return {
      inputVal: ref(''),
      list: ref([
        '爱与希望',
        '花海',
        'Mojito',
        '最长的电影',
        '爷爷泡的茶'
      ])
    }
  },
  computed: {
    filteredList() {
      if (!this.inputVal) {
        return this.list;
      }
      return this.list.filter(item => item.indexOf(this.inputVal) > -1);
    }
  },
});
</script>

<style lang="less" scoped>
/** define kh-idx */
.kh-idx {

  &-ul {
    padding: 15px 8px;
    border: 1px solid #9c27b0;
  }

  &-li {
    padding: 4px 2px;
    border: 1px solid gainsboro;
  }
}
</style>

虽然能实现该功能,但是在使用中文进行输入时会有拼音到字符转变的时间,这段时间是没有办法实现过滤的。效果如下
请添加图片描述
为了优化该场景,可以使用compositionstart和compositionend相关事件进行优化。代码如下

<template>
  <div class="kh-idx">
    <input 
      v-model="inputVal"
      type="text"
      @compositionupdate="handleCompositionUpdate"
      @compositionend="handleCompositionEnd"
    />
    <ul class="kh-idx-ul">
      <li v-for="item in filteredList" :key="item" class="kh-idx-li">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'KhIndex',
  setup() {
    let inputVal = ref('');
    const handleCompositionUpdate = (e: CompositionEvent) => {
      console.log('handleCompositionUpdate', e.data);
      inputVal.value = e.data;
    };

    const handleCompositionEnd = (e: CompositionEvent) => {
      console.log('handleCompositionEnd', e.data);
      inputVal.value = e.data;
    };

    return {
      inputVal,
      list: ref([
        '爱与希望',
        '花海',
        'Mojito',
        '最长的电影',
        '爷爷泡的茶'
      ]),
      handleCompositionUpdate,
      handleCompositionEnd
    }
  },
  computed: {
    filteredList() {
      if (!this.inputVal) {
        return this.list;
      }
      return this.list.filter(item => item.indexOf(this.inputVal) > -1);
    }
  },
});
</script>

<style lang="less" scoped>
/** define kh-idx */
.kh-idx {

  &-ul {
    padding: 15px 8px;
    border: 1px solid #9c27b0;
  }

  &-li {
    padding: 4px 2px;
    border: 1px solid gainsboro;
  }
}
</style>

请添加图片描述
请添加图片描述

四、san.js 中合成事件的应用

function elementOwnAttached() {
    if (this._rootNode) {
        return;
    }

    var isComponent = this.nodeType === NodeType.CMPT;
    var data = isComponent ? this.data : this.scope;

    /* eslint-disable no-redeclare */

    // 处理自身变化时双向绑定的逻辑
    var xProps = this.aNode._xp;
    for (var i = 0, l = xProps.length; i < l; i++) {
        var xProp = xProps[i];

        switch (xProp.name) {
            case 'value':
                switch (this.tagName) {
                    case 'input':
                    case 'textarea':
                        if (isBrowser && window.CompositionEvent) {
                            elementOnEl(this, 'change', inputOnCompositionEnd);
                            elementOnEl(this, 'compositionstart', inputOnCompositionStart);
                            elementOnEl(this, 'compositionend', inputOnCompositionEnd);
                        }

                        // #[begin] allua
                        /* istanbul ignore else */
                        if ('oninput' in this.el) {
                        // #[end]
                            elementOnEl(this, 'input', getInputXPropOutputer(this, xProp, data));
                        // #[begin] allua
                        }
                        else {
                            elementOnEl(this, 'focusin', getInputFocusXPropHandler(this, xProp, data));
                            elementOnEl(this, 'focusout', getInputBlurXPropHandler(this));
                        }
                        // #[end]

                        break;

                    case 'select':
                        elementOnEl(this, 'change', getXPropOutputer(this, xProp, data));
                        break;
                }
                break;

            case 'checked':
                switch (this.tagName) {
                    case 'input':
                        switch (this.el.type) {
                            case 'checkbox':
                            case 'radio':
                                elementOnEl(this, 'click', getXPropOutputer(this, xProp, data));
                        }
                }
                break;
        }
    }

    var owner = isComponent ? this : this.owner;
    for (var i = 0, l = this.aNode.events.length; i < l; i++) {
        var eventBind = this.aNode.events[i];

        // #[begin] error
        warnEventListenMethod(eventBind, owner);
        // #[end]

        elementOnEl(
            this, 
            eventBind.name,
            getEventListener(eventBind, owner, data, eventBind.modifier),
            eventBind.modifier.capture
        );
    }

    if (isComponent && this.nativeEvents) {
        for (var i = 0, l = this.nativeEvents.length; i < l; i++) {
            var eventBind = this.nativeEvents[i];

            // #[begin] error
            warnEventListenMethod(eventBind, this.owner);
            // #[end]

            elementOnEl(
                this, 
                eventBind.name,
                getEventListener(eventBind, this.owner, this.scope),
                eventBind.modifier.capture
            );
        }
    }

    var transition = elementGetTransition(this);
    if (transition && transition.enter) {
        try {
            transition.enter(this.el, empty);
        }
        catch (e) {
            handleError(e, isComponent ? owner.parentComponent : owner, 'transitionEnter');
        }
    }
}

在上面的代码中,san对input 双向数据绑定时,监听了compositionstart和compositionend事件,然后两个事件处理函数也不是很麻烦,如下

/**
 * 双绑输入框CompositionEnd事件监听函数
 *
 * @inner
 */
function inputOnCompositionEnd() {
    if (!this.composing) { // 只有 composing 不为 1 时,后续才不执行
        return;
    }

    this.composing = 0;
    trigger(this, 'input'); // 触发 input 事件,trigger 函数中是自定义的 input 事件,这样用于定义的input事件就得意执行
}

/**
 * 双绑输入框CompositionStart事件监听函数
 *
 * @inner
 */
function inputOnCompositionStart() {
    this.composing = 1; // composing 为 1 表示正在合成(composing)
}

/**
 * 触发元素事件
 *
 * @inner
 * @param {HTMLElement} el DOM元素
 * @param {string} eventName 事件名
 */
function trigger(el, eventName) {
    var event = document.createEvent('HTMLEvents');
    event.initEvent(eventName, true, true);
    el.dispatchEvent(event); // 触发自定义事件
}

这样san就解决了使用input事件时,在输入汉字中没有出发input事件的情况。效果如下
请添加图片描述
参考文章

  1. js中compositionstart和compositionend事件
  2. javascript 高级程序设计(第四版)
  3. CompositionEvent

http://www.kler.cn/a/132478.html

相关文章:

  • 初学者编程语言的选择
  • stm32启动过程解析startup启动文件
  • UE5 材质里面画圆锯齿严重的问题
  • 在云服务器搭建 Docker
  • 两大新兴开发语言大比拼:Move PK Rust
  • 数据科学与SQL:如何计算排列熵?| 基于SQL实现
  • 单元测试实战(二)Service 的测试
  • Android Jetpack的组件介绍,常见组件解析
  • 力扣labuladong一刷day12拿下N数之和问题共4题
  • 一文搞懂设计模式之代理模式
  • AIGC创作系统ChatGPT源码,AI绘画源码,支持最新GPT-4-Turbo模型,支持DALL-E3文生图
  • “开源 vs. 闭源:大模型的未来发展趋势预测“——探讨大模型未来的发展方向
  • 2023版Idea创建JavaWeb时,右键new没有Servlet快捷键选项
  • Linux输入设备应用编程(键盘,按键,触摸屏,鼠标)
  • 电子画册真的好好用,制作也简单,都快来学学!
  • Springboot集成JDBC
  • V100 配置 Scanpy + Scvi + Pytorch
  • 快速搜索多个word、excel等文件中内容
  • Element UI 偶发性图标乱码问题
  • flutter web 中嵌入一个html
  • 基于单片机体温脉搏检测控制系统及源程序
  • 【OpenGauss源码学习 —— 执行算子(Append算子)】
  • 【Linux】vimrc 配置方案
  • springboot项目中没有识别到yml文件解决办法
  • 【机器学习】朴素贝叶斯算法:多项式、高斯、伯努利,实例应用(心脏病预测)
  • AlphaControls控件TsDBCombobox出错:访问违规