JavaScript改变输入值时的事件?

per*_*mon 10 javascript events

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

$input.value = 12;
Run Code Online (Sandbox Code Playgroud)

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

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

T.J*_*der 7

没有内置事件.您至少有四种选择:

  1. 每次更改$input.value代码时,请调用由更改触发的代码
  2. 轮询更改
  3. 给自己一个用来更改值的方法,它也会发出通知
  4. (#3的变体)给自己一个用来更改值的属性,它也会发出通知

其中,您会注意到#1,#3和#4都要求您在代码中执行某些操作,而不仅仅是$input.value = "new value";轮询,选项#2是唯一可以使用value直接设置的代码的选项.

细节:

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

    $input.value = "new value";
    handleValueChange();
    
    Run Code Online (Sandbox Code Playgroud)
  2. 投票变更:

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

    轮询声誉不佳(有充分理由),因为它是一个不变的CPU消费者.当选项卡没有焦点时,现代浏览器会调低计时器事件(甚至使它们停止),从而减轻了这一点.在现代系统甚至移动设备上,20次/秒不是问题.

    但仍然,民意调查是一个丑陋的最后手段.

    例:

    var $input = document.getElementById("$input");
    var last$inputValue = $input.value;
    setInterval(function() {
        var newValue = $input.value;
        if (last$inputValue != newValue) {
            last$inputValue = newValue;
            handleValueChange();
        }
    }, 50); // 20 times/second
    function handleValueChange() {
        console.log("$input's value changed: " + $input.value);
    }
    // Trigger a change
    setTimeout(function() {
        $input.value = "new value";
    }, 800);
    Run Code Online (Sandbox Code Playgroud)
    <input type="text" id="$input">
    Run Code Online (Sandbox Code Playgroud)

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

    $input.setValue = function(newValue) {
        this.value = newValue;
        handleValueChange();
    };
    $input.addEventListener("input", handleValueChange, false);
    
    Run Code Online (Sandbox Code Playgroud)

    用法:

    $input.setValue("new value");
    
    Run Code Online (Sandbox Code Playgroud)

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

    例:

    var $input = document.getElementById("$input");
    $input.setValue = function(newValue) {
        this.value = newValue;
        handleValueChange();
    };
    $input.addEventListener("input", handleValueChange, false);
    function handleValueChange() {
        console.log("$input's value changed: " + $input.value);
    }
    // Trigger a change
    setTimeout(function() {
        $input.setValue("new value");
    }, 800);
    Run Code Online (Sandbox Code Playgroud)
    <input type="text" id="$input">
    Run Code Online (Sandbox Code Playgroud)

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

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

    用法:

    $input.val = "new value";
    
    Run Code Online (Sandbox Code Playgroud)

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

    $input.val = ...对于任何习惯于读取普通DOM代码(或jQuery代码)的人来说,这看起来都是错误的.

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

    例:

    var $input = document.getElementById("$input");
    Object.defineProperty($input, "val", {
        get: function() {
            return this.value;
        },
        set: function(newValue) {
            this.value = newValue;
            handleValueChange();
        }
    });
    $input.addEventListener("input", handleValueChange, false);
    function handleValueChange() {
        console.log("$input's value changed: " + $input.value);
    }
    // Trigger a change
    setTimeout(function() {
        $input.val = "new value";
    }, 800);
    Run Code Online (Sandbox Code Playgroud)
    <input type="text" id="$input">
    Run Code Online (Sandbox Code Playgroud)


小智 6

我会根据 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);
Run Code Online (Sandbox Code Playgroud)

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

Object.defineProperty(HTMLInputElement.prototype, "value", descriptor);
Run Code Online (Sandbox Code Playgroud)

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


Pab*_*123 5

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

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

          observer.observe($input, {
                 attributes: true
          });
Run Code Online (Sandbox Code Playgroud)

尽管这本身不会检测到以下变化:

$input.value = 12
Run Code Online (Sandbox Code Playgroud)

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

$input.setAttribute('value', 12)
Run Code Online (Sandbox Code Playgroud)

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


fox*_*ock 5

基于@ 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);
}
Run Code Online (Sandbox Code Playgroud)


Kin*_*101 5

这是挂钩所有输入更改的 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);
            })
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

用法示例:

document.getElementById("myInput").addInputChangedByJsListener(function() {
    console.log("Input changed to \"" + this.value + "\"");
});
Run Code Online (Sandbox Code Playgroud)