是否有可能在JS中有一个事件,当某个变量的值发生变化时触发?JQuery被接受。


当前回答

我搜索了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监听器。如果需要,输入也可以被隐藏。

其他回答

这是不可能的。

但是,这可以使用CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent来完成

下面的方法接受一个变量名数组作为输入,并为每个变量添加事件侦听器,并在变量值发生任何更改时触发事件。

该方法使用轮询来检测值的变化。您可以以毫秒为单位增加timeout的值。

function watchVariable(varsToWatch) {
    let timeout = 1000;
    let localCopyForVars = {};
    let pollForChange = function () {
        for (let varToWatch of varsToWatch) {
            if (localCopyForVars[varToWatch] !== window[varToWatch]) {
                let event = new CustomEvent('onVar_' + varToWatch + 'Change', {
                    detail: {
                        name: varToWatch,
                        oldValue: localCopyForVars[varToWatch],
                        newValue: window[varToWatch]
                    }
                });
                document.dispatchEvent(event);
                localCopyForVars[varToWatch] = window[varToWatch];
            }
        }
        setTimeout(pollForChange, timeout);
    };
    let respondToNewValue = function (varData) {
        console.log("The value of the variable " + varData.name + " has been Changed from " + varData.oldValue + " to " + varData.newValue + "!!!"); 
    }
    for (let varToWatch of varsToWatch) {
        localCopyForVars[varToWatch] = window[varToWatch];
        document.addEventListener('onVar_' + varToWatch + 'Change', function (e) {
            respondToNewValue(e.detail);
        });
    }
    setTimeout(pollForChange, timeout);
}

通过调用方法:

watchVariables(['username', 'userid']);

它将检测变量username和userid的更改。

基于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方法将被触发。

No.

但是,如果这真的很重要,你有两个选择(第一个是测试,第二个不是):

首先,使用setter和getter,如下所示:

var myobj = {a : 1};

function create_gets_sets(obj) { // make this a framework/global function
    var proxy = {}
    for ( var i in obj ) {
        if (obj.hasOwnProperty(i)) {
            var k = i;
            proxy["set_"+i] = function (val) { this[k] = val; };
            proxy["get_"+i] = function ()    { return this[k]; };
        }
    }
    for (var i in proxy) {
        if (proxy.hasOwnProperty(i)) {
            obj[i] = proxy[i];
        }
    }
}

create_gets_sets(myobj);

然后你可以这样做:

function listen_to(obj, prop, handler) {
    var current_setter = obj["set_" + prop];
    var old_val = obj["get_" + prop]();
    obj["set_" + prop] = function(val) { current_setter.apply(obj, [old_val, val]); handler(val));
}

然后像这样设置监听器:

listen_to(myobj, "a", function(oldval, newval) {
    alert("old : " + oldval + " new : " + newval);
}

其次,你可以把手表的价值:

给定上面的myobj,上面有'a':

function watch(obj, prop, handler) { // make this a framework/global function
    var currval = obj[prop];
    function callback() {
        if (obj[prop] != currval) {
            var temp = currval;
            currval = obj[prop];
            handler(temp, currval);
        }
    }
    return callback;
}

var myhandler = function (oldval, newval) {
    //do something
};

var intervalH = setInterval(watch(myobj, "a", myhandler), 100);

myobj.set_a(2);

最近我发现自己也有同样的问题。想要监听变量的变化并在变量变化时做一些事情。

有人提出了一个使用setter设置值的简单解决方案。

声明一个简单的对象来保存变量的值:

var variableObject = {
    value: false,
    set: function (value) {
        this.value = value;
        this.getOnChange();
    }
}

对象包含一个set方法,通过该方法可以更改值。但是它也调用了一个getOnChange()方法。将定义它。

variableObject.getOnChange = function() {
    if(this.value) {
        // do some stuff
    }
}

现在,每当我做variableObject.set(true)时,getOnChange方法就会触发,如果值被设置为所需的值(在我的例子中:true), if块也会执行。

这是我发现的最简单的方法。

对于那些几年后收听的人来说:

大多数浏览器(和IE6+)都有一个解决方案,它使用onpropertychange事件和更新的规范defineProperty。有一点需要注意的是,您需要将变量设置为dom对象。

详情:

http://johndyer.name/native-browser-get-set-properties-in-javascript/