是否有一种标准方法来捕获HTML输入元素的值的所有更改,而不管它是由用户输入更改还是通过程序更改?
考虑以下代码(仅出于示例目的,并且未采用良好的编码习惯编写,您可以将其视为恰好能够在某些Web浏览器中运行的伪代码:P)
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
<script>
window.onload = () => {
var test = document.getElementById("test");
test.onchange = () => {
console.log("OnChange Called");
console.log(test.value);
} // Called when the input element loses focus and its value changed
test.oninput = () => {
console.log("OnInput Called");
console.log(test.value);
} // Called whenever the input value changes
test.onkeyup = () => {
console.log("OnKeyUp Called");
console.log(test.value);
} // some pre-HTML5 way of getting real-time input value changes
}
</script>
</head>
<body>
<input type="text" name="test" id="test">
</body>
</html>
但是,当以编程方式更改输入元素的值时,这些事件都不会触发,就像有人在做
document.getElementById("test").value = "Hello there!!";
要捕获以编程方式更改的值,通常可以在过去完成以下两项操作之一:
1)告诉编码人员每次以编程方式更改输入值时手动触发onchange事件,例如
document.getElementById("test").value = "Hello there!!";
document.getElementById("test").onchange();
但是对于手头的这个项目,客户不会接受这种解决方案,因为他们有很多来来去去的承包商/分包商,我想他们只是不信任他们的承包商严格遵守这种规则,更重要的是,他们从以前的合同中获得了一个可行的解决方案,这是做事的第二种老方法
2)设置一个计时器,该计时器定期检查输入元素的值,并在每次更改时调用一个函数,诸如此类
var pre_value = test.value;
setInterval(() => {
if (test.value !== pre_value) {
console.log("Value changed");
console.log(test.value);
pre_value = test.value;
}
}, 200); // checks the value every 200ms and see if it's changed
这看起来像是jQuery v1.6时代的某种恐龙,由于各种原因恕我直言,这是相当糟糕的,但是可以满足客户的需求。
现在我们到了2019年,我想知道是否有某种现代的方法可以代替上述代码? JavaScript的setter / getter似乎很有希望,但是当我尝试以下代码时,它只是破坏了HTML输入元素
Object.defineProperty(test, "value", {
set: v => {
this.value = v;
console.log("Setter called");
console.log(test.value);
},
get: ()=> {
console.log("Getter called");
return this.value;
}
});
以编程方式分配test.value时将调用setter函数,但HTML页面上的input元素将以某种方式被破坏。
那么关于如何捕获HTML输入元素的值的所有更改并调用处理程序函数(不是古老的“使用轮询计时器”方法)的任何想法吗?
注意:请注意,此处的所有代码仅用于示例目的,不应在实际系统中使用,在实际系统中,最好使用addEventListener / attachEvent / dispatchEvent / fireEvent等方法。
答案 0 :(得分:2)
要观察元素.value
的分配和检索,可以使用Object.defineProperty
,但是您需要在内部调用原始 setter和getter函数。自定义方法,这些方法可在HTMLInputElement.prototype
上使用:
const { getAttribute, setAttribute } = test;
test.setAttribute = function(...args) {
if (args[0] === 'value') {
console.log('Setting value');
}
return setAttribute.apply(this, args);
};
test.getAttribute = function(...args) {
if (args[0] === 'value') {
console.log('Getting value');
}
return getAttribute.apply(this, args);
};
test.setAttribute('value', 'foo');
console.log(test.getAttribute('value'));
<input type="text" name="test" id="test">
请注意使用方法而不是箭头功能-这很重要,它可以保留this
上下文。 (您也可以使用类似set.call(input, v)
的方法,但这不太灵活)
仅用于更改.value
。您可以根据需要为setAttribute('value
进行类似的修补:
const { setAttribute } = test;
test.setAttribute = function(...args) {
if (args[0] === 'value') {
console.log('attribute set!');
}
return setAttribute.apply(this, args);
};
test.setAttribute('value', 'foo');
<input type="text" name="test" id="test">
答案 1 :(得分:1)
标准方法是在以编程方式更改值时不触发更改事件。
不仅有太多方法可以通过编程方式设置输入的值,而且,这只是对无限循环的要求。
如果在任何回调中设置了您的输入值,那么您将使页面崩溃。
想尝试吗?
<add key="Telerik.Web.UI.StyleSheetFolders" value="~/App_Themes/;" />
let called = 0; // to avoid blocking this page
const { set, get } = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
Object.defineProperty(inp, 'value', {
set(v) {
const ret = set.call(this, v);
if(++called < 20) // I limit it to 20 calls to not kill this page
this.dispatchEvent(new Event('all-changes'));
return ret;
},
get() { return get.call(this); }
});
inp.addEventListener('all-changes', e => {
inp.value = inp.value.toUpperCase();
console.log('changed');
});
btn.onclick = e => inp.value = 'foo';
因此,最好的办法仍然是仅直接从负责更改的代码中调用任何回调。