是否有可能在JS中有一个事件,当某个变量的值发生变化时触发?JQuery被接受。
当前回答
如果你正在使用jQuery {UI}(每个人都应该使用:-)),你可以使用.change()和一个隐藏的<input/>元素。
其他回答
是的,现在这是完全可能的!
我知道这是一个旧线程,但现在这种效果是可能使用访问器(getter和setter): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters
你可以像这样定义一个对象,其中inner表示字段a:
x = {
aInternal: 10,
aListener: function(val) {},
set a(val) {
this.aInternal = val;
this.aListener(val);
},
get a() {
return this.aInternal;
},
registerListener: function(listener) {
this.aListener = listener;
}
}
然后你可以使用下面的方法注册一个监听器:
x.registerListener(function(val) {
alert("Someone changed the value of x.a to " + val);
});
因此,每当x.a的值发生变化时,监听器函数就会被触发。运行下面的代码行将弹出警告:
x.a = 42;
请看一个例子:https://jsfiddle.net/5o1wf1bn/1/
您还可以使用一个侦听器数组,而不是单个侦听器插槽,但是我想给您一个最简单的示例。
我搜索了JavaScript双向数据绑定库,发现了这个库。
我没有成功地使它在DOM到变量的方向上工作,但在变量到DOM的方向上它是工作的,这就是我们在这里需要的。
我稍微重写了一下,因为原始代码(对我来说)很难阅读。它使用 object。defineproperty,这是Eliot b给出的第二多赞的答案,至少有部分错误。
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
const dataBind = (function () {
const getElementValue = function (selector) {
let element = document.querySelectorAll(selector)[0];
return 'value' in element ? element.value : element.innerHTML;
};
const setElementValue = function (selector, newValue) {
let elementArray = document.querySelectorAll(selector);
for (let i = 0; i < elementArray.length; i++) {
let element = elementArray[i];
if ('value' in element) {
element.value = newValue;
if (element.tagName.toLowerCase() === 'select'){
let options = element.querySelectorAll('option');
for (let option in options){
if (option.value === newValue){
option.selected = true;
break;
}
}
}
} else {
element.innerHTML = newValue;
}
}
};
const bindModelToView = function (selector, object, property, enumerable) {
Object.defineProperty(object, property, {
get: function () {
return getElementValue(selector);
},
set: function (newValue) {
setElementValue(selector, newValue);
},
configurable: true,
enumerable: (enumerable)
});
};
return {
bindModelToView
};
})();
</script>
</head>
<body>
<div style="padding: 20%;">
<input type="text" id="text" style="width: 40px;"/>
</div>
<script>
let x = {a: 1, b: 2};
dataBind.bindModelToView('#text', x, 'a'); //data to dom
setInterval(function () {
x.a++;
}, 1000);
</script>
</body>
</html>
JSFiddle.
JSFiddle原始代码。
在提供的示例中,对象x的属性由setInterval更新,文本输入的值也会自动更新。如果这还不够,并且事件是你想要的,你可以在上面的输入中添加onchange监听器。如果需要,输入也可以被隐藏。
你正在寻找的功能可以通过使用“defineProperty()”方法来实现——这只适用于现代浏览器:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
我写了一个jQuery扩展,有一些类似的功能,如果你需要更多的跨浏览器支持:
https://github.com/jarederaj/jQueue
对象的队列回调的jQuery小扩展 变量、对象或键的存在。你可以分配任意数量的 对可能受影响的任意个数的数据点的回调 进程在后台运行。jQueue监听并等待 您指定的这些数据开始存在,然后发射 纠正回调函数的参数。
@akira和@mo-d-genesis的解决方案可以进一步简化,因为在这个例子中DOM操作不依赖于状态:
CodePen
const render = (val) => {
document.getElementById("numberDiv").innerHTML = val;
};
state = {
_value_internal: undefined,
set value(val) {
// 1. set state value
this._value_internal = val;
// 2. render user interface
render(val);
},
get value() {
return this._value_internal;
},
};
const onClick = () => {
state.value = state.value + 1; // state change leads to re-render!
};
// set default value
state.value = 0;
对应的html:
<div id="numberDiv"></div>
<button onclick="onClick()">
Click to rerender
</button>
备注:
我重命名了变量和函数,以更好地反映它们的语义。 仅供参考:通过改变变量,Svelte提供了非常类似的反应行为
基于akira的回答,我补充说,你可以通过listerner操纵dom。
https://jsfiddle.net/2zcr0Lnh/2/
javascript:
x = {
aInternal: 10,
aListener: function(val) {},
set a(val) {
this.aInternal = val;
this.aListener(val);
},
get a() {
return this.aInternal;
},
registerListener: function(listener) {
this.aListener = listener;
}
}
x.registerListener(function(val) {
document.getElementById('showNumber').innerHTML = val;
});
x.a = 50;
function onClick(){
x.a = x.a + 1;
}
html:
<div id="showNumber">
</div>
<button onclick="onClick()">
click me to rerender
</button>
当变量x.a发生变化时,registerListener方法将被触发。