3

输入框value属性赋值触发js change事件的实现

 3 years ago
source link: https://www.zhangxinxu.com/wordpress/2021/05/js-value-change/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=9949
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

火箭飞碟

一、原生的行为表现

例如有一个很普通的输入框,HTML代码如下所示:

<input id="input">

此时,用户输入内容,或者粘贴内容,或者拖拽内容到输入框,都会触发input事件,失焦后会触发change事件。

但是有一个是例外,那就是如果使用value属性赋值,无论是input事件还是change事件都不会触发。

input.value = '张鑫旭';  // 不会触发change事件

需要我们手动触发下change事件,示意:

input.value = 'zhangxinxu';
// 手动触发change事件
input.dispatchEvent(new CustomEvent('change'));

显然,这样的处理就很啰嗦,dispatchEventCustomEvent这样的API对不少开发者而言是陌生的。

那有没有办法直接value赋值也能触发change事件呢?

二、自定义value属性

我们组的XboxYan (严文彬)提供了一种解决方案,就是使用getOwnPropertyDescriptor方法先把浏览器原生的value赋值方法存下来,然后重新自定义value赋值方法,在重新自定义的value属性的set方法中注入change事件的触发。

JS代码示意如下:

const props = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
Object.defineProperty(HTMLInputElement.prototype, 'value', {
    ...props,
    set (v) {
        let oldv = this.value;
        props.set.call(this, v);
        // 手动触发change事件
        if (oldv !== v) {
              input.dispatchEvent(new CustomEvent('change'));
        }
    }
});

我们不妨测试下,有如下HTML代码:

<input id="input">
<p id="result"></p>

然后我们绑定change事件,然后再执行value赋值方法,代码如下所示:

input.addEventListener('change', function () {
  result.innerHTML += this.value;
});

input.value = 'zhangxinxu';

运行后可以看到下面截图所示的效果,p#result元素内自动填充了 ‘zhangxinxu’ 这几个字符。

运行结果示意

三、关于getOwnPropertyDescriptor

Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。

所谓自有属性,指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性。

属性描述符也是个对象,英文叫做property descriptor,属性描述符也包含访问访问器函数(getter)的get方法和获取设置器函数(setter)的set方法,因此,上一节示意代码中的props.set其实指的就是浏览器原生的value赋值时候的设置器函数,包含了浏览器原生的行为。

属性描述符对象支持的属性和方法包括:

value 该属性的值。 get 获取该属性的访问器函数,没有返回undefined。 set 获取该属性的设置器函数,没有返回undefined。 writable 当且仅当属性的值可以被改变时为true。 configurable 当且仅当指定对象的属性描述可以被改变或者属性可被删除时,为true。 enumerable 当且仅当指定对象的属性可以被枚举出时,为 true。

还有个类似的静态方法名为 Reflect.getOwnPropertyDescriptor(),大家有兴趣可以访问对应的MDN文档了解一番。

四、不只是触发change事件

getOwnPropertyDescriptor() 方法自定义value属性后,不仅可以用来触发change事件,我们还可以主动触发输入框元素的渲染。

例如LuLu UI Edge主题中的颜色选择输入框支持 #RRGGBBAA 颜色格式,如下图所示:

RRGGBBAA支持截图示意

原生的color输入框在设置value属性值为#RRGGBB色值时会自动变色,但是这里的color-opacity输入框是自定义的,并不识别#RRGGBBAA色值,此时就需要对value属性进行处理,赋值的时候自动改变输入框的UI,其原理和上面触发change时间类似。

代码示意:

class Color extends HTMLInputElement {
    constructor () {
        super();
    }
    ...
}
const props = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
Object.defineProperty(Color.prototype, 'value', {
    ...props,
    set (value) {
        let strValue = value;
        // 目前的颜色值
        let strOldValue = this.value;
        // UI同步匹配
        this.uiMatch();
        // 赋值
        props.set.call(this, strOldValue);
        // change事件触发
        if (strOldValue && strValue != strOldValue) {
            this.dispatchEvent(new CustomEvent('change', {
                'bubbles': true
            }));
        }
    }
});

此时给输入框执行 input.value = '#EB46467D' 就可以看到UI变化了。

颜色变化了示意

// zxx: 实际上上面色值不对,和设计不符,左上50%应该是纯色,而不是和黑色的混合色,我这几天抽空处理下。

除了Object.defineProperty方法,我们还可以使用Proxy对象实现类似的效果,这个之后有机会再介绍。

今天去医院拔了两颗智齿,半张脸都是麻的,现在拔牙位置还疼,疼到颌面骨都疼,好惨。

这回大家要真的心疼哥哥了,求分享求转发。

心疼哥哥

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK