JavaScript 更改输入值时的事件?

IT技术 javascript events
2021-02-26 11:54:48

<input>通过 JavaScript 代码更改元素值时的事件是什么?例如:

$input.value = 12;

input事件在此不帮助,因为它不是谁在改变值的用户。

在 Chrome 上测试时,change不会触发事件。也许是因为元素没有失去焦点(它没有获得焦点,所以它不能失去焦点)?

6个回答

没有内置事件。你至少有四个选择:

  1. 每当您更改$input.value代码时,请调用您希望由更改触发的代码
  2. 投票更改
  3. 给自己一个你用来改变值的方法,它也会做通知
  4. (#3 的变体)给自己一个用于更改值属性,该属性也将发出通知

其中,您会注意到#1、#3 和#4 都要求您在代码中做一些不同于$input.value = "new value";轮询的事情,选项#2 是唯一一个可以处理value直接设置的代码的选项

细节:

  1. 最简单的解决方案:每当您更改$input.value代码时,调用您希望由更改触发的代码:

    $input.value = "new value";
    handleValueChange();
    
  2. 投票更改:

    var last$inputValue = $input.value;
    setInterval(function() {
        var newValue = $input.value;
        if (last$inputValue != newValue) {
            last$inputValue = newValue;
            handleValueChange();
        }
    }, 50); // 20 times/second
    

    轮询的名声很差(有充分的理由),因为它是一个持续的 CPU 消耗者。当标签没有焦点时,现代浏览器拨打计时器事件(甚至将它们带到停止),这会减轻一点。20 次/秒在现代系统,甚至是手机上都不是问题。

    但是,投票仍然是一个丑陋的最后手段。

    例子:

  3. 给自己一个函数来设置值并通知你,并使用该函数代替value,结合input事件处理程序来捕获用户的更改:

    $input.setValue = function(newValue) {
        this.value = newValue;
        handleValueChange();
    };
    $input.addEventListener("input", handleValueChange, false);
    

    用法:

    $input.setValue("new value");
    

    当然,您必须记住使用setValue而不是分配给value.

    例子:

  4. #3 的变体:给自己一个可以设置的不同属性(再次结合用户更改的事件处理程序):

    Object.defineProperty($input, "val", {
        get: function() {
            return this.value;
        },
        set: function(newValue) {
            this.value = newValue;
            handleValueChange();
        }
    });
    $input.addEventListener("input", handleValueChange, false);
    

    用法:

    $input.val = "new value";
    

    这适用于所有现代浏览器,甚至旧的 Android,甚至 IE8(支持definePropertyDOM 元素,但不支持 JavaScript 对象)。当然,您需要在目标浏览器上对其进行测试。

    $input.val = ...对于习惯于阅读普通 DOM 代码(或 jQuery 代码)的任何人来说,这似乎是一个错误。

    在你问之前:不,你不能用上面的来替换value属性本身。

    例子:

基于@tj-crowder 和@maciej-swist 的答案,让我们添加这个,使用“.apply”函数防止无限循环而不重新定义对象。

 function customInputSetter(){

  var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");
  var originalSet = descriptor.set;

  // define our own setter
  descriptor.set = function(val) {
    console.log("Value set", this, val);
    originalSet.apply(this,arguments);
  }

  Object.defineProperty(HTMLInputElement.prototype, "value", descriptor);
}

我会根据 TJ Crowder 的建议添加第 5 个选项。但不是添加新属性,您可以更改实际的“值”属性以在设置时触发其他操作 - 无论是针对特定输入元素,还是针对所有输入对象:

//First store the initial descriptor of the "value" property:

var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");
var inputSetter = descriptor.set;

//Then modify the "setter" of the value to notify when the value is changed:

descriptor.set = function(val) {

    //changing to native setter to prevent the loop while setting the value
    Object.defineProperty(this, "value", {set:inputSetter});
    this.value = val;

    //Custom code triggered when $input.value is set
    console.log("Value set: "+val);

    //changing back to custom setter
    Object.defineProperty(this, "value", descriptor);   
}

//Last add the new "value" descriptor to the $input element
Object.defineProperty($input, "value", descriptor);

不是更改特定输入元素的“值”属性,而是可以对所有输入元素进行一般更改:

Object.defineProperty(HTMLInputElement.prototype, "value", descriptor);

此方法仅适用于使用 javascript 更改值,例如 input.value="new value"。在输入框中键入新值时不起作用。

这是挂钩所有输入更改的 value 属性的解决方案:

var valueDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");

HTMLInputElement.prototype.addInputChangedByJsListener = function(cb) {
    if(!this.hasOwnProperty("_inputChangedByJSListeners")) {
        this._inputChangedByJSListeners = [];
    }
    this._inputChangedByJSListeners.push(cb);
}

Object.defineProperty(HTMLInputElement.prototype, "value", {
    get: function() {
        return valueDescriptor.get.apply(this, arguments);
    },
    set: function() {
        var self = this;
        valueDescriptor.set.apply(self, arguments);
        if(this.hasOwnProperty("_inputChangedByJSListeners")){
            this._inputChangedByJSListeners.forEach(function(cb) {
                cb.apply(self);
            })
        }
    }
});

用法示例:

document.getElementById("myInput").addInputChangedByJsListener(function() {
    console.log("Input changed to \"" + this.value + "\"");
});
聪明和 hacky,我喜欢它,只需确保仅在调试时使用它 :)
2021-04-23 11:54:48

一种可能的策略是使用一个mutationObserver来检测属性的变化,如下所示:

var observer = new MutationObserver(function(mutations) {
          mutations.forEach(function(){
                 console.log('hello')});
          });

          observer.observe($input, {
                 attributes: true
          });

虽然这本身不会检测到如下变化:

$input.value = 12

它会检测到实际值属性的变化:

$input.setAttribute('value', 12)

因此,如果是您以编程方式设置值,只需确保更改value = 12语句旁边的属性,您就可以获得所需的结果。

这是一个非常奇特的解决方案。实际上我需要反映外部变化,这样对我没有帮助。但它仍然是非常好的解决方案
2021-05-06 11:54:48
这确实适用于 chrome 和 firefox。他们似乎也改变了 dom 中的值!
2021-05-17 11:54:48