per*_*mon 10 javascript events
<input>通过JavaScript代码更改元素值时的事件是什么?例如:
$input.value = 12;
Run Code Online (Sandbox Code Playgroud)
该input事件在此不帮助,因为它不是谁在改变值的用户.
在Chrome上进行测试时,change不会触发该事件.也许是因为元素没有失去焦点(它没有获得焦点,所以它不能失去它)?
没有内置事件.您至少有四种选择:
$input.value代码时,请调用由更改触发的代码其中,您会注意到#1,#3和#4都要求您在代码中执行某些操作,而不仅仅是$input.value = "new value";轮询,选项#2是唯一可以使用value直接设置的代码的选项.
细节:
最简单的解决方案:每当您更改$input.value代码时,请调用您希望由更改触发的代码:
$input.value = "new value";
handleValueChange();
Run Code Online (Sandbox Code Playgroud)投票变更:
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)
为自己设置一个函数来设置值并通知您,并使用该函数代替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)
#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"。在输入框中键入新值时不起作用。
一种可能的策略是使用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语句旁边的属性即可获得所需的结果。
基于@ 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)
这是挂钩所有输入更改的 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)