Home Listening for variable changes in JavaScript

# Listening for variable changes in JavaScript

rashcroft22
1#
rashcroft22 Published in 2009-11-18 23:58:15Z
 Is it possible to have an event in JS that fires when the value of a certain variable changes? JQuery is accepted. Thanks!
Eli Grey
2#
Eli Grey Reply to 2012-04-21 18:43:31Z
 Yes, object.watch (it's non-standard though). Here's my implementation that works in every current major browser.
Luke Schafer
3#
Luke Schafer Reply to 2009-11-19 00:33:25Z
 No. But, if it's really that important, you have 2 options (first is tested, second isn't): First, use setters and getters, like so: 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); then you can do something like: 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)); } then set the listener like: listen_to(myobj, "a", function(oldval, newval) { alert("old : " + oldval + " new : " + newval); } Second, I actually forgot, I'll submit while I think about it :) EDIT: Oh, I remember :) You could put a watch on the value: Given myobj above, with 'a' on it: 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);
Akira
4#
 Yes, this is now completely possible! I know this is an old thread but now this effect is possible using accessors (getters and setters): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters You can define an object like this, in which aInternal represents the field 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; } } Then you can register a listener using the following: x.registerListener(function(val) { alert("Someone changed the value of x.a to " + val); }); So whenever anything changes the value of x.a, the listener function will be fired. Running the following line will bring the alert popup: x.a = 42; See an example here: https://jsfiddle.net/5o1wf1bn/1/ You can also user an array of listeners instead of a single listener slot, but I wanted to give you the simplest possible example.
andrew
5#
 Sorry to bring up an old thread, but here is a little manual for those who (like me!) don't see how Eli Grey's example works: var test = new Object(); test.watch("elem", function(prop,oldval,newval){ //Your code return newval; }); Hope this can help someone
Eduardo Cuomo
6#
Eduardo Cuomo Reply to 2016-11-04 13:00:27Z

Using Prototype: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

// Console
function print(t) {
var c = document.getElementById('console');
c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
var myVar = 123;

Object.defineProperty(this, 'varWatch', {
get: function () { return myVar; },
set: function (v) {
myVar = v;
print('Value changed! New value: ' + v);
}
});

print(varWatch);
varWatch = 456;
print(varWatch);
<pre id="console">
</pre>

# Other example

// Console
function print(t) {
var c = document.getElementById('console');
c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
var varw = (function (context) {
return function (varName, varValue) {
var value = varValue;

Object.defineProperty(context, varName, {
get: function () { return value; },
set: function (v) {
value = v;
print('Value changed! New value: ' + value);
}
});
};
})(window);

varw('varWatch'); // Declare
print(varWatch);
varWatch = 456;
print(varWatch);

print('---');

varw('otherVarWatch', 123); // Declare with initial value
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
<pre id="console">
</pre>

Community
7#

As Luke Schafer's answer (note: this refers to his original post; but the whole point here remains valid after the edit), I would also suggest a pair of Get/Set methods to access your value.

However I would suggest some modifications (and that's why I'm posting...).

A problem with that code is that the field a of the object myobj is directly accessible, so it's possible to access it / change its value without triggering the listeners:

var myobj = { a : 5, get_a : function() { return this.a;}, set_a : function(val) { this.a = val; }}
myobj.a = 10; // no listeners called!

## Encapsulation

So, to guarantee that the listeners are actually called, we would have to prohibit that direct access to the field a. How to do so? Use a closure!

var myobj = (function() { // Anonymous function to create scope.

var a = 5;            // 'a' is local to this function
// and cannot be directly accessed from outside
// this anonymous function's scope

return {
get_a : function() { return a; },   // These functions are closures:
set_a : function(val) { a = val; }  // they keep reference to
// something ('a') that was on scope
// where they were defined
};
})();

Now you can use the same method to create and add the listeners as Luke proposed, but you can rest assured that there's no possible way to read from or write to a going unnoticed!

Still on Luke's track, I propose now a simple way to add encapsulated fields and the respective getters/setters to objects by the means of a simple function call.

Note that this will only work properly with value types. For this to work with reference types, some kind of deep copy would have to be implemented (see this one, for instance).

var field = initial;
obj["get_" + name] = function() { return field; }
obj["set_" + name] = function(val) { field = val; }
}

This works the same as before: we create a local variable on a function, and then we create a closure.

How to use it? Simple:

var myobj = {};
myobj.set_total(10);
ses
8#
 AngularJS (I know this is not JQuery, but that might help. [Pure JS is good in theory only]): $scope.$watch('data', function(newValue) { .. where "data" is name of your variable in the scope. There is a link to doc.
Chuck Han
9#
Chuck Han Reply to 2011-05-25 00:37:44Z
 If you're using jQuery {UI} (which everyone should be using :-) ), you can use .change() with a hidden element.
MandoMando
10#
 For those tuning in a couple years later: A solution for most browsers (and IE6+) is available that uses the onpropertychange event and the newer spec defineProperty. The slight catch is that you'll need to make your variable a dom object. Full details: http://johndyer.name/native-browser-get-set-properties-in-javascript/
jldupont
11#
 Not directly: you need a pair getter/setter with an "addListener/removeListener" interface of some sort... or an NPAPI plugin (but that's another story altogether).
jarederaj
12#
 Yet again, I am fashionably late to this party. I know this is not pure JavaScript or jQuery; however, I've have been toying with a project in knockout.js, and have found the subscribe() method works great for firing an event (or notifying) when the value of something changes. Here is a simple example: var foo = ko.observable(false); foo.subscribe(function () { console.log('foo has changed: ' + foo()); if (foo() === true) { // do something here } }); Granted, subscribe() works on knockout's "observables" not on regular variables. Here is a link to the knockout documentation page covering observables and subscriptions, for anyone interested: http://knockoutjs.com/documentation/observables.html