Something like MutationObserver for observing changes to object [duplicate] - javascript

Is it possible to have an event in JS that fires when the value of a certain variable changes? JQuery is accepted.

This question was originally posted in 2009 and most of the existing answers are either outdated, ineffective, or require the inclusion of large bloated libraries:
Object.watch and Object.observe are both deprecated and should not be used.
onPropertyChange is a DOM element event handler that only works in some versions of IE.
Object.defineProperty allows you to make an object property immutable, which would allow you to detect attempted changes, but it would also block any changes.
Defining setters and getters works, but it requires a lot of setup code and it does not work well when you need to delete or create new properties.
As of 2018, you can now use the Proxy object to monitor (and intercept) changes made to an object. It is purpose built for what the OP is trying to do. Here's a basic example:
var targetObj = {};
var targetProxy = new Proxy(targetObj, {
set: function (target, key, value) {
console.log(`${key} set to ${value}`);
target[key] = value;
return true;
}
});
targetProxy.hello_world = "test"; // console: 'hello_world set to test'
The only drawbacks of the Proxy object are:
The Proxy object is not available in older browsers (such as IE11) and the polyfill cannot fully replicate Proxy functionality.
Proxy objects do not always behave as expected with special objects (e.g., Date) -- the Proxy object is best paired with plain Objects or Arrays.
If you need to observe changes made to a nested object, then you need to use a specialized library such as Observable Slim (which I have published). It works like this:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]

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.

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) {
/**
* Declare a new variable.
*
* #param {string} Variable name.
* #param {any | undefined} varValue Default/Initial value.
* You can use an object reference for example.
*/
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 without initial value
print(varWatch);
varWatch = 456;
print(varWatch);
print('---');
varw('otherVarWatch', 123); // Declare with initial value
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
<pre id="console">
</pre>

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, 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);

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

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; }}
/* add listeners ... */
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!
Adding encapsulated fields programmatically
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).
function addProperty(obj, name, initial) {
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 = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);

Recently found myself with the same issue. Wanted to listen for on change of a variable and do some stuff when the variable changed.
Someone suggested a simple solution of setting the value using a setter.
Declaring a simple object that keeps the value of my variable here:
var variableObject = {
value: false,
set: function (value) {
this.value = value;
this.getOnChange();
}
}
The object contains a set method via which I can change the value. But it also calls a getOnChange() method in there. Will define it now.
variableObject.getOnChange = function() {
if(this.value) {
// do some stuff
}
}
Now whenever I do variableObject.set(true), the getOnChange method fires, and if the value was set as desired (in my case: true), the if block also executes.
This is the simplest way I found to do this stuff.

If you're using jQuery {UI} (which everyone should be using :-) ), you can use .change() with a hidden <input/> element.

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.

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/

Easiest way I have found, starting from this answer:
// variable holding your data
const state = {
count: null,
update() {
console.log(`this gets called and your value is ${this.pageNumber}`);
},
get pageNumber() {
return this.count;
},
set pageNumber(pageNumber) {
this.count = pageNumber;
// here you call the code you need
this.update(this.count);
}
};
And then:
state.pageNumber = 0;
// watch the console
state.pageNumber = 15;
// watch the console

The functionality you're looking for can be achieved through the use of the "defineProperty()" method--which is only available to modern browsers:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
I've written a jQuery extension that has some similar functionality if you need more cross browser support:
https://github.com/jarederaj/jQueue
A small jQuery extension that handles queuing callbacks to the
existence of a variable, object, or key. You can assign any number of
callbacks to any number of data points that might be affected by
processes running in the background. jQueue listens and waits for
these data you specify to come into existence and then fires off the
correct callback with its arguments.

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).

A rather simple and simplistic solution is to just use a function call to set the value of the global variable, and never set its value directly. This way you have total control:
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
There is no way to enforce this, it just requires programming discipline... though you can use grep (or something similar) to check that nowhere does your code directly set the value of globalVar.
Or you could encapsulate it in an object and user getter and setter methods... just a thought.

With the help of getter and setter, you can define a JavaScript class that does such a thing.
First, we define our class called MonitoredVariable:
class MonitoredVariable {
constructor(initialValue) {
this._innerValue = initialValue;
this.beforeSet = (newValue, oldValue) => {};
this.beforeChange = (newValue, oldValue) => {};
this.afterChange = (newValue, oldValue) => {};
this.afterSet = (newValue, oldValue) => {};
}
set val(newValue) {
const oldValue = this._innerValue;
// newValue, oldValue may be the same
this.beforeSet(newValue, oldValue);
if (oldValue !== newValue) {
this.beforeChange(newValue, oldValue);
this._innerValue = newValue;
this.afterChange(newValue, oldValue);
}
// newValue, oldValue may be the same
this.afterSet(newValue, oldValue);
}
get val() {
return this._innerValue;
}
}
Assume that we want to listen for money changes, let's create an instance of MonitoredVariable with initial money 0:
const money = new MonitoredVariable(0);
Then we could get or set its value using money.val:
console.log(money.val); // Get its value
money.val = 2; // Set its value
Since we have not defined any listeners for it, nothing special happens after money.val changes to 2.
Now let's define some listeners. We have four listeners available: beforeSet, beforeChange, afterChange, afterSet.
The following will happen sequentially when you use money.val = newValue to change variable's value:
money.beforeSet(newValue, oldValue);
money.beforeChange(newValue, oldValue); (Will be skipped if its value not changed)
money.val = newValue;
money.afterChange(newValue, oldValue); (Will be skipped if its value not changed)
money.afterSet(newValue, oldValue);
Now we define afterChange listener which be triggered only after money.val has changed (while afterSet will be triggered even if the new value is the same as the old one):
money.afterChange = (newValue, oldValue) => {
console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};
Now set a new value 3 and see what happens:
money.val = 3;
You will see the following in the console:
Money has been changed from 2 to 3
For full code, see https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab.

In my case, I was trying to find out if any library I was including in my project was redefining my window.player. So, at the begining of my code, I just did:
Object.defineProperty(window, 'player', {
get: () => this._player,
set: v => {
console.log('window.player has been redefined!');
this._player = v;
}
});

Based On akira's answer I added that you can manipulate the dom through the listerner.
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>
The registerListener method is fired when the variable x.a changes.

//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};
var x1 = {
eventCurrentStatus:undefined,
get currentStatus(){
return this.eventCurrentStatus;
},
set currentStatus(val){
this.eventCurrentStatus=val;
//your function();
}
};
or
/* var x1 = {
eventCurrentStatus:undefined,
currentStatus : {
get : function(){
return Events.eventCurrentStatus
},
set : function(status){
Events.eventCurrentStatus=status;
},
}*/
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);
or
/* global variable ku*/
var jsVarEvents={};
Object.defineProperty(window, "globalvar1", {//no i18n
get: function() { return window.jsVarEvents.globalvarTemp},
set: function(value) { window.window.jsVarEvents.globalvarTemp = value; }
});
console.log(globalvar1);
globalvar1=1;
console.log(globalvar1);

Please guys remember the initial question was for VARIABLES, not for OBJECTS ;)
in addition to all answers above, I created a tiny lib called forTheWatch.js,
that use the same way to catch and callback for changes in normal global variables in javascript.
Compatible with JQUERY variables, no need to use OBJECTS, and you can pass directly an ARRAY of several variables if needed.
If it can be helpful... :
https://bitbucket.org/esabora/forthewatch Basically you just have to call the function :
watchIt("theVariableToWatch", "varChangedFunctionCallback");
And sorry by advance if not relevant.

The question is about variables, not object properties! So my approach is to take advantage of the window object, with its custom getters/setters, and then use/change the variable like a "normal" variable (not like an object property).
The simplest way is that of #José Antonio Postigo in his answer (i voted that answer). What I'd like to do here, is to reduce that to an even simpler "creator" function (so even someone that does not understand object getters/setters can easily use it).
A live example is here: https://codepen.io/dimvai/pen/LYzzbpz
This is the general "creator" function you must have as is:
let createWatchedVariable = (variableName,initialValue,callbackFunction) => {
// set default callback=console.log if missing
callbackFunction ??= function(){console.log(variableName+" changed to " + window[variableName])};
// the actual useful code:
Object.defineProperty(window, variableName, {
set: function(value) {window["_"+variableName] = value; callbackFunction()},
get: function() {return window["_"+variableName]}
});
window[variableName]=initialValue??null;
};
Then, instead of declaring the variable using var or let, use this:
// 1st approach - default callback//
createWatchedVariable ('myFirstVariable',12);
// instead of: let myFirstVariable = 12;
Or, in order to use your custom callback (instead of the default console.log) use:
// 2nd approach - set a custom callback//
var myCallback = ()=>{/*your custom code...*/}
// now use callback function as the third optional argument
createWatchedVariable('mySecondVariable',0,myCallback);
That's it! Now, you can change it like a "normal" variable:
myFirstVariable = 15; // logs to console
myFirstVariable++; // logs to console
mySecondVariable = 1001; // executes your custom code
mySecondVariable++; // executes your custom code

The solution of #akira and #mo-d-genesis can be further simplified because the DOM manipulation does not depend on state in this example:
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;
The corresponding html:
<div id="numberDiv"></div>
<button onclick="onClick()">
Click to rerender
</button>
Remarks:
I renamed variables and functions to better reflect their semantics.
FYI: Svelte offers a very similar reactive behavior by changing variables

It's not directly possible.
However, this can be done using CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
The below method accepts an array of variable names as an input and adds event listener for each variable and triggers the event for any changes to the value of the variables.
The Method uses polling to detect the change in the value. You can increase the value for timeout in milliseconds.
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);
}
By calling the Method:
watchVariables(['username', 'userid']);
It will detect the changes to variables username and userid.

This is what I did: Call JSON.stringify twice and compare the two strings...
Drawbacks:
You can only know whether the whole object changes
You have to detect changes manually
You better have only primitive fields in the object(no properties, no functions...)

This is NOT a production ideal answer, but what it is doing is setting an interval in JavaScript for every 100 milliseconds and checking to see if the variable is changed and when it is, it does something (anything intended by the OP) and then clears the interval, so it sort of simulates what the OP is asking.
let myvar = "myvar";
const checkChange = setInterval(() => {
if (myvar !== "myvar") {
console.log("My precious var has been changed!");
clearInterval(checkChange);
}
}, 100);
Now if myvar gets changed to something else then this program will say "My precious var has been changed!" :)

This is an old great question, has more than 12 years. Also, there are many ways to solve it. However, most of then are complicated or using old JS concepts we are in 2022 and we can use ES6 to improve our code.
I will implemented two main solutions that I constantly use.
Simple variable
If we have a simple variable and we don't care about reutilization then we can declare our variable as an object. We define a set and get methods and a listener attribute to handle the "change" event.
const $countBtn = document.getElementById('counter')
const $output = document.getElementById('output')
const counter = {
v: 0,
listener: undefined,
set value(v) {
this.v = v
if (this.listener) this.listener(v)
},
get value() { return this.v },
count() { this.value++ },
registerListener(callback) {
this.listener = callback
},
}
const countOnClick = () => { counter.count() }
$countBtn.onclick = countOnClick
counter.registerListener(v => {
$output.textContent = v
})
counter.value = 50
#output {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
<button id="counter">Count</button>
<div id="output"></div>
Advanced Class for reusability
If we will have multiple variables and we need to monitor them, we can create a class and then apply it to our variables. I recommend to add two listeners one beforeChange and afterChange this will give you flexibility to use the variable in different process.
class ObservableObject {
constructor(v) {
this.v = v ?? 0
this.on = {
beforeChange(newValue, oldValue) {},
afterChange(newValue, oldValue) {},
}
}
set value(newValue) {
const oldValue = this.v
// newValue, oldValue are the same
if (oldValue === newValue) return
this.on.beforeChange(newValue, oldValue)
this.v = newValue
this.on.afterChange(newValue, oldValue)
}
get value() { return this.v }
}
const $countABtn = document.getElementById('counter-a')
const $countBBtn = document.getElementById('counter-b')
const $outputA = document.getElementById('output-a')
const $outputB = document.getElementById('output-b')
const counterA = new ObservableObject()
const counterB = new ObservableObject()
const countOnClick = counter => { counter.value++ }
const onChange = (v, output) => { output.textContent = v }
$countABtn.onclick = () => { countOnClick(counterA) }
$countBBtn.onclick = () => { countOnClick(counterB) }
counterA.on.afterChange = v => { onChange(v, $outputA) }
counterB.on.afterChange = v => { onChange(v, $outputB) }
counterA.value = 50
counterB.value = 20
.wrapper {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
width: 100vw
}
.item {
width: 50%
}
.output {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
<div class="wrapper">
<div class="item">
<button id="counter-a">Count A</button>
<div id="output-a" class="output"></div>
</div>
<div class="item">
<button id="counter-b">Count B</button>
<div id="output-b" class="output"></div>
</div>
</div>

This is an old thread but I stumbled onto second highest answer (custom listeners) while looking for a solution using Angular. While the solution works, angular has a better built in way to resolve this using #Output and event emitters. Going off of the example in custom listener answer:
ChildComponent.html
<button (click)="increment(1)">Increment</button>
ChildComponent.ts
import {EventEmitter, Output } from '#angular/core';
#Output() myEmitter: EventEmitter<number> = new EventEmitter<number>();
private myValue: number = 0;
public increment(n: number){
this.myValue += n;
// Send a change event to the emitter
this.myEmitter.emit(this.myValue);
}
ParentComponent.html
<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>
ParentComponent.ts
public n: number = 0;
public monitorChanges(n: number){
this.n = n;
console.log(n);
}
This will now update non parent each time the child button is clicked. Working stackblitz

I came here looking for same answer for node js. So here it is
const events = require('events');
const eventEmitter = new events.EventEmitter();
// Createing state to watch and trigger on change
let x = 10 // x is being watched for changes in do while loops below
do {
eventEmitter.emit('back to normal');
}
while (x !== 10);
do {
eventEmitter.emit('something changed');
}
while (x === 10);
What I am doing is setting some event emitters when values are changed and using do while loops to detect it.

I searched for JavaScript two-way data binding library and came across this one.
I did not succeed to make it work in DOM to variable direction, but in variable to DOM direction it works and that is what we need here.
I have rewritten it slightly, as the original code is very hard to read (for me). It uses
Object.defineProperty, so the second most upvoted answer by Eliot B. at least partially wrong.
<!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 with original code.
In the provided example a property of object x updated by the setInterval and value of text input automatically updated as well. If it is not enough and event is what you looking for, you can add onchange listener to the above input. Input also can be made hidden if needed.

Utils = {
eventRegister_globalVariable : function(variableName,handlers){
eventRegister_JsonVariable(this,variableName,handlers);
},
eventRegister_jsonVariable : function(jsonObj,variableName,handlers){
if(jsonObj.eventRegisteredVariable === undefined) {
jsonObj.eventRegisteredVariable={};//this Object is used for trigger event in javascript variable value changes ku
}
Object.defineProperty(jsonObj, variableName , {
get: function() {
return jsonObj.eventRegisteredVariable[variableName] },
set: function(value) {
jsonObj.eventRegisteredVariable[variableName] = value; handlers(jsonObj.eventRegisteredVariable[variableName]);}
});
}

Related

In javascript how can I run a command when the value of a variable changes? [duplicate]

Is it possible to have an event in JS that fires when the value of a certain variable changes? JQuery is accepted.
This question was originally posted in 2009 and most of the existing answers are either outdated, ineffective, or require the inclusion of large bloated libraries:
Object.watch and Object.observe are both deprecated and should not be used.
onPropertyChange is a DOM element event handler that only works in some versions of IE.
Object.defineProperty allows you to make an object property immutable, which would allow you to detect attempted changes, but it would also block any changes.
Defining setters and getters works, but it requires a lot of setup code and it does not work well when you need to delete or create new properties.
As of 2018, you can now use the Proxy object to monitor (and intercept) changes made to an object. It is purpose built for what the OP is trying to do. Here's a basic example:
var targetObj = {};
var targetProxy = new Proxy(targetObj, {
set: function (target, key, value) {
console.log(`${key} set to ${value}`);
target[key] = value;
return true;
}
});
targetProxy.hello_world = "test"; // console: 'hello_world set to test'
The only drawbacks of the Proxy object are:
The Proxy object is not available in older browsers (such as IE11) and the polyfill cannot fully replicate Proxy functionality.
Proxy objects do not always behave as expected with special objects (e.g., Date) -- the Proxy object is best paired with plain Objects or Arrays.
If you need to observe changes made to a nested object, then you need to use a specialized library such as Observable Slim (which I have published). It works like this:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
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.
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) {
/**
* Declare a new variable.
*
* #param {string} Variable name.
* #param {any | undefined} varValue Default/Initial value.
* You can use an object reference for example.
*/
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 without initial value
print(varWatch);
varWatch = 456;
print(varWatch);
print('---');
varw('otherVarWatch', 123); // Declare with initial value
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
<pre id="console">
</pre>
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, 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);
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
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; }}
/* add listeners ... */
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!
Adding encapsulated fields programmatically
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).
function addProperty(obj, name, initial) {
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 = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
Recently found myself with the same issue. Wanted to listen for on change of a variable and do some stuff when the variable changed.
Someone suggested a simple solution of setting the value using a setter.
Declaring a simple object that keeps the value of my variable here:
var variableObject = {
value: false,
set: function (value) {
this.value = value;
this.getOnChange();
}
}
The object contains a set method via which I can change the value. But it also calls a getOnChange() method in there. Will define it now.
variableObject.getOnChange = function() {
if(this.value) {
// do some stuff
}
}
Now whenever I do variableObject.set(true), the getOnChange method fires, and if the value was set as desired (in my case: true), the if block also executes.
This is the simplest way I found to do this stuff.
If you're using jQuery {UI} (which everyone should be using :-) ), you can use .change() with a hidden <input/> element.
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.
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/
Easiest way I have found, starting from this answer:
// variable holding your data
const state = {
count: null,
update() {
console.log(`this gets called and your value is ${this.pageNumber}`);
},
get pageNumber() {
return this.count;
},
set pageNumber(pageNumber) {
this.count = pageNumber;
// here you call the code you need
this.update(this.count);
}
};
And then:
state.pageNumber = 0;
// watch the console
state.pageNumber = 15;
// watch the console
The functionality you're looking for can be achieved through the use of the "defineProperty()" method--which is only available to modern browsers:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
I've written a jQuery extension that has some similar functionality if you need more cross browser support:
https://github.com/jarederaj/jQueue
A small jQuery extension that handles queuing callbacks to the
existence of a variable, object, or key. You can assign any number of
callbacks to any number of data points that might be affected by
processes running in the background. jQueue listens and waits for
these data you specify to come into existence and then fires off the
correct callback with its arguments.
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).
A rather simple and simplistic solution is to just use a function call to set the value of the global variable, and never set its value directly. This way you have total control:
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
There is no way to enforce this, it just requires programming discipline... though you can use grep (or something similar) to check that nowhere does your code directly set the value of globalVar.
Or you could encapsulate it in an object and user getter and setter methods... just a thought.
With the help of getter and setter, you can define a JavaScript class that does such a thing.
First, we define our class called MonitoredVariable:
class MonitoredVariable {
constructor(initialValue) {
this._innerValue = initialValue;
this.beforeSet = (newValue, oldValue) => {};
this.beforeChange = (newValue, oldValue) => {};
this.afterChange = (newValue, oldValue) => {};
this.afterSet = (newValue, oldValue) => {};
}
set val(newValue) {
const oldValue = this._innerValue;
// newValue, oldValue may be the same
this.beforeSet(newValue, oldValue);
if (oldValue !== newValue) {
this.beforeChange(newValue, oldValue);
this._innerValue = newValue;
this.afterChange(newValue, oldValue);
}
// newValue, oldValue may be the same
this.afterSet(newValue, oldValue);
}
get val() {
return this._innerValue;
}
}
Assume that we want to listen for money changes, let's create an instance of MonitoredVariable with initial money 0:
const money = new MonitoredVariable(0);
Then we could get or set its value using money.val:
console.log(money.val); // Get its value
money.val = 2; // Set its value
Since we have not defined any listeners for it, nothing special happens after money.val changes to 2.
Now let's define some listeners. We have four listeners available: beforeSet, beforeChange, afterChange, afterSet.
The following will happen sequentially when you use money.val = newValue to change variable's value:
money.beforeSet(newValue, oldValue);
money.beforeChange(newValue, oldValue); (Will be skipped if its value not changed)
money.val = newValue;
money.afterChange(newValue, oldValue); (Will be skipped if its value not changed)
money.afterSet(newValue, oldValue);
Now we define afterChange listener which be triggered only after money.val has changed (while afterSet will be triggered even if the new value is the same as the old one):
money.afterChange = (newValue, oldValue) => {
console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};
Now set a new value 3 and see what happens:
money.val = 3;
You will see the following in the console:
Money has been changed from 2 to 3
For full code, see https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab.
In my case, I was trying to find out if any library I was including in my project was redefining my window.player. So, at the begining of my code, I just did:
Object.defineProperty(window, 'player', {
get: () => this._player,
set: v => {
console.log('window.player has been redefined!');
this._player = v;
}
});
Based On akira's answer I added that you can manipulate the dom through the listerner.
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>
The registerListener method is fired when the variable x.a changes.
//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};
var x1 = {
eventCurrentStatus:undefined,
get currentStatus(){
return this.eventCurrentStatus;
},
set currentStatus(val){
this.eventCurrentStatus=val;
//your function();
}
};
or
/* var x1 = {
eventCurrentStatus:undefined,
currentStatus : {
get : function(){
return Events.eventCurrentStatus
},
set : function(status){
Events.eventCurrentStatus=status;
},
}*/
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);
or
/* global variable ku*/
var jsVarEvents={};
Object.defineProperty(window, "globalvar1", {//no i18n
get: function() { return window.jsVarEvents.globalvarTemp},
set: function(value) { window.window.jsVarEvents.globalvarTemp = value; }
});
console.log(globalvar1);
globalvar1=1;
console.log(globalvar1);
Please guys remember the initial question was for VARIABLES, not for OBJECTS ;)
in addition to all answers above, I created a tiny lib called forTheWatch.js,
that use the same way to catch and callback for changes in normal global variables in javascript.
Compatible with JQUERY variables, no need to use OBJECTS, and you can pass directly an ARRAY of several variables if needed.
If it can be helpful... :
https://bitbucket.org/esabora/forthewatch Basically you just have to call the function :
watchIt("theVariableToWatch", "varChangedFunctionCallback");
And sorry by advance if not relevant.
The question is about variables, not object properties! So my approach is to take advantage of the window object, with its custom getters/setters, and then use/change the variable like a "normal" variable (not like an object property).
The simplest way is that of #José Antonio Postigo in his answer (i voted that answer). What I'd like to do here, is to reduce that to an even simpler "creator" function (so even someone that does not understand object getters/setters can easily use it).
A live example is here: https://codepen.io/dimvai/pen/LYzzbpz
This is the general "creator" function you must have as is:
let createWatchedVariable = (variableName,initialValue,callbackFunction) => {
// set default callback=console.log if missing
callbackFunction ??= function(){console.log(variableName+" changed to " + window[variableName])};
// the actual useful code:
Object.defineProperty(window, variableName, {
set: function(value) {window["_"+variableName] = value; callbackFunction()},
get: function() {return window["_"+variableName]}
});
window[variableName]=initialValue??null;
};
Then, instead of declaring the variable using var or let, use this:
// 1st approach - default callback//
createWatchedVariable ('myFirstVariable',12);
// instead of: let myFirstVariable = 12;
Or, in order to use your custom callback (instead of the default console.log) use:
// 2nd approach - set a custom callback//
var myCallback = ()=>{/*your custom code...*/}
// now use callback function as the third optional argument
createWatchedVariable('mySecondVariable',0,myCallback);
That's it! Now, you can change it like a "normal" variable:
myFirstVariable = 15; // logs to console
myFirstVariable++; // logs to console
mySecondVariable = 1001; // executes your custom code
mySecondVariable++; // executes your custom code
The solution of #akira and #mo-d-genesis can be further simplified because the DOM manipulation does not depend on state in this example:
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;
The corresponding html:
<div id="numberDiv"></div>
<button onclick="onClick()">
Click to rerender
</button>
Remarks:
I renamed variables and functions to better reflect their semantics.
FYI: Svelte offers a very similar reactive behavior by changing variables
It's not directly possible.
However, this can be done using CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
The below method accepts an array of variable names as an input and adds event listener for each variable and triggers the event for any changes to the value of the variables.
The Method uses polling to detect the change in the value. You can increase the value for timeout in milliseconds.
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);
}
By calling the Method:
watchVariables(['username', 'userid']);
It will detect the changes to variables username and userid.
This is what I did: Call JSON.stringify twice and compare the two strings...
Drawbacks:
You can only know whether the whole object changes
You have to detect changes manually
You better have only primitive fields in the object(no properties, no functions...)
This is NOT a production ideal answer, but what it is doing is setting an interval in JavaScript for every 100 milliseconds and checking to see if the variable is changed and when it is, it does something (anything intended by the OP) and then clears the interval, so it sort of simulates what the OP is asking.
let myvar = "myvar";
const checkChange = setInterval(() => {
if (myvar !== "myvar") {
console.log("My precious var has been changed!");
clearInterval(checkChange);
}
}, 100);
Now if myvar gets changed to something else then this program will say "My precious var has been changed!" :)
This is an old great question, has more than 12 years. Also, there are many ways to solve it. However, most of then are complicated or using old JS concepts we are in 2022 and we can use ES6 to improve our code.
I will implemented two main solutions that I constantly use.
Simple variable
If we have a simple variable and we don't care about reutilization then we can declare our variable as an object. We define a set and get methods and a listener attribute to handle the "change" event.
const $countBtn = document.getElementById('counter')
const $output = document.getElementById('output')
const counter = {
v: 0,
listener: undefined,
set value(v) {
this.v = v
if (this.listener) this.listener(v)
},
get value() { return this.v },
count() { this.value++ },
registerListener(callback) {
this.listener = callback
},
}
const countOnClick = () => { counter.count() }
$countBtn.onclick = countOnClick
counter.registerListener(v => {
$output.textContent = v
})
counter.value = 50
#output {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
<button id="counter">Count</button>
<div id="output"></div>
Advanced Class for reusability
If we will have multiple variables and we need to monitor them, we can create a class and then apply it to our variables. I recommend to add two listeners one beforeChange and afterChange this will give you flexibility to use the variable in different process.
class ObservableObject {
constructor(v) {
this.v = v ?? 0
this.on = {
beforeChange(newValue, oldValue) {},
afterChange(newValue, oldValue) {},
}
}
set value(newValue) {
const oldValue = this.v
// newValue, oldValue are the same
if (oldValue === newValue) return
this.on.beforeChange(newValue, oldValue)
this.v = newValue
this.on.afterChange(newValue, oldValue)
}
get value() { return this.v }
}
const $countABtn = document.getElementById('counter-a')
const $countBBtn = document.getElementById('counter-b')
const $outputA = document.getElementById('output-a')
const $outputB = document.getElementById('output-b')
const counterA = new ObservableObject()
const counterB = new ObservableObject()
const countOnClick = counter => { counter.value++ }
const onChange = (v, output) => { output.textContent = v }
$countABtn.onclick = () => { countOnClick(counterA) }
$countBBtn.onclick = () => { countOnClick(counterB) }
counterA.on.afterChange = v => { onChange(v, $outputA) }
counterB.on.afterChange = v => { onChange(v, $outputB) }
counterA.value = 50
counterB.value = 20
.wrapper {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
width: 100vw
}
.item {
width: 50%
}
.output {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
<div class="wrapper">
<div class="item">
<button id="counter-a">Count A</button>
<div id="output-a" class="output"></div>
</div>
<div class="item">
<button id="counter-b">Count B</button>
<div id="output-b" class="output"></div>
</div>
</div>
This is an old thread but I stumbled onto second highest answer (custom listeners) while looking for a solution using Angular. While the solution works, angular has a better built in way to resolve this using #Output and event emitters. Going off of the example in custom listener answer:
ChildComponent.html
<button (click)="increment(1)">Increment</button>
ChildComponent.ts
import {EventEmitter, Output } from '#angular/core';
#Output() myEmitter: EventEmitter<number> = new EventEmitter<number>();
private myValue: number = 0;
public increment(n: number){
this.myValue += n;
// Send a change event to the emitter
this.myEmitter.emit(this.myValue);
}
ParentComponent.html
<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>
ParentComponent.ts
public n: number = 0;
public monitorChanges(n: number){
this.n = n;
console.log(n);
}
This will now update non parent each time the child button is clicked. Working stackblitz
I came here looking for same answer for node js. So here it is
const events = require('events');
const eventEmitter = new events.EventEmitter();
// Createing state to watch and trigger on change
let x = 10 // x is being watched for changes in do while loops below
do {
eventEmitter.emit('back to normal');
}
while (x !== 10);
do {
eventEmitter.emit('something changed');
}
while (x === 10);
What I am doing is setting some event emitters when values are changed and using do while loops to detect it.
I searched for JavaScript two-way data binding library and came across this one.
I did not succeed to make it work in DOM to variable direction, but in variable to DOM direction it works and that is what we need here.
I have rewritten it slightly, as the original code is very hard to read (for me). It uses
Object.defineProperty, so the second most upvoted answer by Eliot B. at least partially wrong.
<!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 with original code.
In the provided example a property of object x updated by the setInterval and value of text input automatically updated as well. If it is not enough and event is what you looking for, you can add onchange listener to the above input. Input also can be made hidden if needed.
Utils = {
eventRegister_globalVariable : function(variableName,handlers){
eventRegister_JsonVariable(this,variableName,handlers);
},
eventRegister_jsonVariable : function(jsonObj,variableName,handlers){
if(jsonObj.eventRegisteredVariable === undefined) {
jsonObj.eventRegisteredVariable={};//this Object is used for trigger event in javascript variable value changes ku
}
Object.defineProperty(jsonObj, variableName , {
get: function() {
return jsonObj.eventRegisteredVariable[variableName] },
set: function(value) {
jsonObj.eventRegisteredVariable[variableName] = value; handlers(jsonObj.eventRegisteredVariable[variableName]);}
});
}

listen for changes in value to automatically update value of another variable in javascript [duplicate]

Is it possible to have an event in JS that fires when the value of a certain variable changes? JQuery is accepted.
This question was originally posted in 2009 and most of the existing answers are either outdated, ineffective, or require the inclusion of large bloated libraries:
Object.watch and Object.observe are both deprecated and should not be used.
onPropertyChange is a DOM element event handler that only works in some versions of IE.
Object.defineProperty allows you to make an object property immutable, which would allow you to detect attempted changes, but it would also block any changes.
Defining setters and getters works, but it requires a lot of setup code and it does not work well when you need to delete or create new properties.
As of 2018, you can now use the Proxy object to monitor (and intercept) changes made to an object. It is purpose built for what the OP is trying to do. Here's a basic example:
var targetObj = {};
var targetProxy = new Proxy(targetObj, {
set: function (target, key, value) {
console.log(`${key} set to ${value}`);
target[key] = value;
return true;
}
});
targetProxy.hello_world = "test"; // console: 'hello_world set to test'
The only drawbacks of the Proxy object are:
The Proxy object is not available in older browsers (such as IE11) and the polyfill cannot fully replicate Proxy functionality.
Proxy objects do not always behave as expected with special objects (e.g., Date) -- the Proxy object is best paired with plain Objects or Arrays.
If you need to observe changes made to a nested object, then you need to use a specialized library such as Observable Slim (which I have published). It works like this:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
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.
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) {
/**
* Declare a new variable.
*
* #param {string} Variable name.
* #param {any | undefined} varValue Default/Initial value.
* You can use an object reference for example.
*/
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 without initial value
print(varWatch);
varWatch = 456;
print(varWatch);
print('---');
varw('otherVarWatch', 123); // Declare with initial value
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
<pre id="console">
</pre>
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, 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);
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
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; }}
/* add listeners ... */
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!
Adding encapsulated fields programmatically
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).
function addProperty(obj, name, initial) {
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 = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);
Recently found myself with the same issue. Wanted to listen for on change of a variable and do some stuff when the variable changed.
Someone suggested a simple solution of setting the value using a setter.
Declaring a simple object that keeps the value of my variable here:
var variableObject = {
value: false,
set: function (value) {
this.value = value;
this.getOnChange();
}
}
The object contains a set method via which I can change the value. But it also calls a getOnChange() method in there. Will define it now.
variableObject.getOnChange = function() {
if(this.value) {
// do some stuff
}
}
Now whenever I do variableObject.set(true), the getOnChange method fires, and if the value was set as desired (in my case: true), the if block also executes.
This is the simplest way I found to do this stuff.
If you're using jQuery {UI} (which everyone should be using :-) ), you can use .change() with a hidden <input/> element.
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.
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/
Easiest way I have found, starting from this answer:
// variable holding your data
const state = {
count: null,
update() {
console.log(`this gets called and your value is ${this.pageNumber}`);
},
get pageNumber() {
return this.count;
},
set pageNumber(pageNumber) {
this.count = pageNumber;
// here you call the code you need
this.update(this.count);
}
};
And then:
state.pageNumber = 0;
// watch the console
state.pageNumber = 15;
// watch the console
The functionality you're looking for can be achieved through the use of the "defineProperty()" method--which is only available to modern browsers:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
I've written a jQuery extension that has some similar functionality if you need more cross browser support:
https://github.com/jarederaj/jQueue
A small jQuery extension that handles queuing callbacks to the
existence of a variable, object, or key. You can assign any number of
callbacks to any number of data points that might be affected by
processes running in the background. jQueue listens and waits for
these data you specify to come into existence and then fires off the
correct callback with its arguments.
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).
A rather simple and simplistic solution is to just use a function call to set the value of the global variable, and never set its value directly. This way you have total control:
var globalVar;
function setGlobalVar(value) {
globalVar = value;
console.log("Value of globalVar set to: " + globalVar);
//Whatever else
}
There is no way to enforce this, it just requires programming discipline... though you can use grep (or something similar) to check that nowhere does your code directly set the value of globalVar.
Or you could encapsulate it in an object and user getter and setter methods... just a thought.
With the help of getter and setter, you can define a JavaScript class that does such a thing.
First, we define our class called MonitoredVariable:
class MonitoredVariable {
constructor(initialValue) {
this._innerValue = initialValue;
this.beforeSet = (newValue, oldValue) => {};
this.beforeChange = (newValue, oldValue) => {};
this.afterChange = (newValue, oldValue) => {};
this.afterSet = (newValue, oldValue) => {};
}
set val(newValue) {
const oldValue = this._innerValue;
// newValue, oldValue may be the same
this.beforeSet(newValue, oldValue);
if (oldValue !== newValue) {
this.beforeChange(newValue, oldValue);
this._innerValue = newValue;
this.afterChange(newValue, oldValue);
}
// newValue, oldValue may be the same
this.afterSet(newValue, oldValue);
}
get val() {
return this._innerValue;
}
}
Assume that we want to listen for money changes, let's create an instance of MonitoredVariable with initial money 0:
const money = new MonitoredVariable(0);
Then we could get or set its value using money.val:
console.log(money.val); // Get its value
money.val = 2; // Set its value
Since we have not defined any listeners for it, nothing special happens after money.val changes to 2.
Now let's define some listeners. We have four listeners available: beforeSet, beforeChange, afterChange, afterSet.
The following will happen sequentially when you use money.val = newValue to change variable's value:
money.beforeSet(newValue, oldValue);
money.beforeChange(newValue, oldValue); (Will be skipped if its value not changed)
money.val = newValue;
money.afterChange(newValue, oldValue); (Will be skipped if its value not changed)
money.afterSet(newValue, oldValue);
Now we define afterChange listener which be triggered only after money.val has changed (while afterSet will be triggered even if the new value is the same as the old one):
money.afterChange = (newValue, oldValue) => {
console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};
Now set a new value 3 and see what happens:
money.val = 3;
You will see the following in the console:
Money has been changed from 2 to 3
For full code, see https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab.
In my case, I was trying to find out if any library I was including in my project was redefining my window.player. So, at the begining of my code, I just did:
Object.defineProperty(window, 'player', {
get: () => this._player,
set: v => {
console.log('window.player has been redefined!');
this._player = v;
}
});
Based On akira's answer I added that you can manipulate the dom through the listerner.
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>
The registerListener method is fired when the variable x.a changes.
//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};
var x1 = {
eventCurrentStatus:undefined,
get currentStatus(){
return this.eventCurrentStatus;
},
set currentStatus(val){
this.eventCurrentStatus=val;
//your function();
}
};
or
/* var x1 = {
eventCurrentStatus:undefined,
currentStatus : {
get : function(){
return Events.eventCurrentStatus
},
set : function(status){
Events.eventCurrentStatus=status;
},
}*/
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);
or
/* global variable ku*/
var jsVarEvents={};
Object.defineProperty(window, "globalvar1", {//no i18n
get: function() { return window.jsVarEvents.globalvarTemp},
set: function(value) { window.window.jsVarEvents.globalvarTemp = value; }
});
console.log(globalvar1);
globalvar1=1;
console.log(globalvar1);
Please guys remember the initial question was for VARIABLES, not for OBJECTS ;)
in addition to all answers above, I created a tiny lib called forTheWatch.js,
that use the same way to catch and callback for changes in normal global variables in javascript.
Compatible with JQUERY variables, no need to use OBJECTS, and you can pass directly an ARRAY of several variables if needed.
If it can be helpful... :
https://bitbucket.org/esabora/forthewatch Basically you just have to call the function :
watchIt("theVariableToWatch", "varChangedFunctionCallback");
And sorry by advance if not relevant.
The question is about variables, not object properties! So my approach is to take advantage of the window object, with its custom getters/setters, and then use/change the variable like a "normal" variable (not like an object property).
The simplest way is that of #José Antonio Postigo in his answer (i voted that answer). What I'd like to do here, is to reduce that to an even simpler "creator" function (so even someone that does not understand object getters/setters can easily use it).
A live example is here: https://codepen.io/dimvai/pen/LYzzbpz
This is the general "creator" function you must have as is:
let createWatchedVariable = (variableName,initialValue,callbackFunction) => {
// set default callback=console.log if missing
callbackFunction ??= function(){console.log(variableName+" changed to " + window[variableName])};
// the actual useful code:
Object.defineProperty(window, variableName, {
set: function(value) {window["_"+variableName] = value; callbackFunction()},
get: function() {return window["_"+variableName]}
});
window[variableName]=initialValue??null;
};
Then, instead of declaring the variable using var or let, use this:
// 1st approach - default callback//
createWatchedVariable ('myFirstVariable',12);
// instead of: let myFirstVariable = 12;
Or, in order to use your custom callback (instead of the default console.log) use:
// 2nd approach - set a custom callback//
var myCallback = ()=>{/*your custom code...*/}
// now use callback function as the third optional argument
createWatchedVariable('mySecondVariable',0,myCallback);
That's it! Now, you can change it like a "normal" variable:
myFirstVariable = 15; // logs to console
myFirstVariable++; // logs to console
mySecondVariable = 1001; // executes your custom code
mySecondVariable++; // executes your custom code
The solution of #akira and #mo-d-genesis can be further simplified because the DOM manipulation does not depend on state in this example:
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;
The corresponding html:
<div id="numberDiv"></div>
<button onclick="onClick()">
Click to rerender
</button>
Remarks:
I renamed variables and functions to better reflect their semantics.
FYI: Svelte offers a very similar reactive behavior by changing variables
It's not directly possible.
However, this can be done using CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
The below method accepts an array of variable names as an input and adds event listener for each variable and triggers the event for any changes to the value of the variables.
The Method uses polling to detect the change in the value. You can increase the value for timeout in milliseconds.
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);
}
By calling the Method:
watchVariables(['username', 'userid']);
It will detect the changes to variables username and userid.
This is what I did: Call JSON.stringify twice and compare the two strings...
Drawbacks:
You can only know whether the whole object changes
You have to detect changes manually
You better have only primitive fields in the object(no properties, no functions...)
This is NOT a production ideal answer, but what it is doing is setting an interval in JavaScript for every 100 milliseconds and checking to see if the variable is changed and when it is, it does something (anything intended by the OP) and then clears the interval, so it sort of simulates what the OP is asking.
let myvar = "myvar";
const checkChange = setInterval(() => {
if (myvar !== "myvar") {
console.log("My precious var has been changed!");
clearInterval(checkChange);
}
}, 100);
Now if myvar gets changed to something else then this program will say "My precious var has been changed!" :)
This is an old great question, has more than 12 years. Also, there are many ways to solve it. However, most of then are complicated or using old JS concepts we are in 2022 and we can use ES6 to improve our code.
I will implemented two main solutions that I constantly use.
Simple variable
If we have a simple variable and we don't care about reutilization then we can declare our variable as an object. We define a set and get methods and a listener attribute to handle the "change" event.
const $countBtn = document.getElementById('counter')
const $output = document.getElementById('output')
const counter = {
v: 0,
listener: undefined,
set value(v) {
this.v = v
if (this.listener) this.listener(v)
},
get value() { return this.v },
count() { this.value++ },
registerListener(callback) {
this.listener = callback
},
}
const countOnClick = () => { counter.count() }
$countBtn.onclick = countOnClick
counter.registerListener(v => {
$output.textContent = v
})
counter.value = 50
#output {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
<button id="counter">Count</button>
<div id="output"></div>
Advanced Class for reusability
If we will have multiple variables and we need to monitor them, we can create a class and then apply it to our variables. I recommend to add two listeners one beforeChange and afterChange this will give you flexibility to use the variable in different process.
class ObservableObject {
constructor(v) {
this.v = v ?? 0
this.on = {
beforeChange(newValue, oldValue) {},
afterChange(newValue, oldValue) {},
}
}
set value(newValue) {
const oldValue = this.v
// newValue, oldValue are the same
if (oldValue === newValue) return
this.on.beforeChange(newValue, oldValue)
this.v = newValue
this.on.afterChange(newValue, oldValue)
}
get value() { return this.v }
}
const $countABtn = document.getElementById('counter-a')
const $countBBtn = document.getElementById('counter-b')
const $outputA = document.getElementById('output-a')
const $outputB = document.getElementById('output-b')
const counterA = new ObservableObject()
const counterB = new ObservableObject()
const countOnClick = counter => { counter.value++ }
const onChange = (v, output) => { output.textContent = v }
$countABtn.onclick = () => { countOnClick(counterA) }
$countBBtn.onclick = () => { countOnClick(counterB) }
counterA.on.afterChange = v => { onChange(v, $outputA) }
counterB.on.afterChange = v => { onChange(v, $outputB) }
counterA.value = 50
counterB.value = 20
.wrapper {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
width: 100vw
}
.item {
width: 50%
}
.output {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
<div class="wrapper">
<div class="item">
<button id="counter-a">Count A</button>
<div id="output-a" class="output"></div>
</div>
<div class="item">
<button id="counter-b">Count B</button>
<div id="output-b" class="output"></div>
</div>
</div>
This is an old thread but I stumbled onto second highest answer (custom listeners) while looking for a solution using Angular. While the solution works, angular has a better built in way to resolve this using #Output and event emitters. Going off of the example in custom listener answer:
ChildComponent.html
<button (click)="increment(1)">Increment</button>
ChildComponent.ts
import {EventEmitter, Output } from '#angular/core';
#Output() myEmitter: EventEmitter<number> = new EventEmitter<number>();
private myValue: number = 0;
public increment(n: number){
this.myValue += n;
// Send a change event to the emitter
this.myEmitter.emit(this.myValue);
}
ParentComponent.html
<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>
ParentComponent.ts
public n: number = 0;
public monitorChanges(n: number){
this.n = n;
console.log(n);
}
This will now update non parent each time the child button is clicked. Working stackblitz
I came here looking for same answer for node js. So here it is
const events = require('events');
const eventEmitter = new events.EventEmitter();
// Createing state to watch and trigger on change
let x = 10 // x is being watched for changes in do while loops below
do {
eventEmitter.emit('back to normal');
}
while (x !== 10);
do {
eventEmitter.emit('something changed');
}
while (x === 10);
What I am doing is setting some event emitters when values are changed and using do while loops to detect it.
I searched for JavaScript two-way data binding library and came across this one.
I did not succeed to make it work in DOM to variable direction, but in variable to DOM direction it works and that is what we need here.
I have rewritten it slightly, as the original code is very hard to read (for me). It uses
Object.defineProperty, so the second most upvoted answer by Eliot B. at least partially wrong.
<!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 with original code.
In the provided example a property of object x updated by the setInterval and value of text input automatically updated as well. If it is not enough and event is what you looking for, you can add onchange listener to the above input. Input also can be made hidden if needed.
Utils = {
eventRegister_globalVariable : function(variableName,handlers){
eventRegister_JsonVariable(this,variableName,handlers);
},
eventRegister_jsonVariable : function(jsonObj,variableName,handlers){
if(jsonObj.eventRegisteredVariable === undefined) {
jsonObj.eventRegisteredVariable={};//this Object is used for trigger event in javascript variable value changes ku
}
Object.defineProperty(jsonObj, variableName , {
get: function() {
return jsonObj.eventRegisteredVariable[variableName] },
set: function(value) {
jsonObj.eventRegisteredVariable[variableName] = value; handlers(jsonObj.eventRegisteredVariable[variableName]);}
});
}

Can I detect if a variable in javascript increases or decreases in value?

I have a a variable, which always is an integer, that increases and/or decreases in value from time to time, goes from 1-5.
I wanted to know if I can, in any way, detect if it goes up or down, for example:
The variable is currently at three, if it increases to four, it runs a function, but if it decreases to two, it runs another function.
Can that be done?
The following four methods will work depending on support by browser compatibility, and this post did take me a while, but it also taught me what ___ does, and I hope I listed most methods, and if you know more, you could post an answer. However, it would be greatful of you to post your method in the comments so I can add it to this answer. Cheers!
1 Use Object.observe:
Object.observe(i, function(b){ b.object[change.name] > b.type ? console.log('decreased') : console.log('increased'); });
Additional notes about Object.observe
If you like to enable it in Chrome 33,
Visit chrome://flags/
And enable Enable Experimental JavaScript
2 Use setInterval:
var i = 4;
var a = i;
setInterval(function()
{
if(i != a) a > i ? console.log('decreased') : console.log('increased');
}, 1000);
i = 10;
should log 'increase' in one second.
3 Use Object.watch:
var i = {i:5};
i.watch("i", function (id, oldval, newval) {
newval > oldval ? console.log('decreased') : console.log('increased');
return newval;
});
Additional notes about Object.watch
To support more browsers, add this script:
if (!Object.prototype.watch) {
Object.defineProperty(Object.prototype, "watch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop, handler) {
var
oldval = this[prop]
, newval = oldval
, getter = function () {
return newval;
}
, setter = function (val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
}
;
if (delete this[prop]) { // can't watch constants
Object.defineProperty(this, prop, {
get: getter
, set: setter
, enumerable: true
, configurable: true
});
}
}
});
}
// object.unwatch
if (!Object.prototype.unwatch) {
Object.defineProperty(Object.prototype, "unwatch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop) {
var val = this[prop];
delete this[prop]; // remove accessors
this[prop] = val;
}
});
}
obtained from https://gist.github.com/eligrey/384583
4. Use a custom value set function:
i = 4;
function setValue(obj, val)
{
val < obj ? console.log('decrease') : console.log('increase');
obj = val;
}
setValue(i, 10);
should log 'increase'.
Most of these require the variable to be an Object, so I'd rather detect over time as seen in method 2.
Sure, but you'll have to check it periodically. Since you didn't provide code, I won't, either. :-)
Set a second variable equal to the first
Check the second against the first at regular intervals (or upon some event)
Respond accordingly
As was mentioned in the comments, there are better ways, such as running functionality alongside (or as a callback to) whatever changes the variable's value in the first place.
let prev;
const change = (i) => {
if (prev === undefined) prev = i;
const next = i;
if (prev < next) console.log("increase");
if (prev > next) console.log("decrease");
prev = next;
};
Rather than detecting if a variable changes, I would use a function to modify the variable. That way, the function can then check if the value has changed and call the new function if it has. Something like:
var triggerVariable;
function changeVariable(newVal){
if(triggerVariable !== newVal){
triggerVariable = newVal;
callFunction();
}
}
function callFunction(){
alert('variable changed!');
}
changeVariable(10);
Object.observe is supposed to be well supported. You can track your state as you please (probably in a closure of some sort).
http://updates.html5rocks.com/2012/11/Respond-to-change-with-Object-observe
you can also create a class for this special variable, and write custom functions for mathematical functions with callback. here is an example with the add function
function specVal (value)
{
this.onChange = null;
this.value = value;
this.val = function()
{
return this.value;
};
this.add = function(i)
{
this.value += i;
if (this.onChange != null)
this.onChange(this);
return this;
}
}
function somethingChanged(val)
{
alert('changed: ' + val.val());
}
myValue = new specVal(10);
myValue.onChange = somethingChanged;
myValue.add(5);
myValue.add(4);
I would either use an MV* framework as suggested (knockout is a lightweight option)
or never change the variable directly.
Use a setter function that changes its value and triggers a custom event each time.
JQuery example:
Function setMyVar(val){
if(_myVar !== val){
_myVar = val;
$('#anyelement').triggerHandler('myvarupdated', [myVar]);
}
}
Very similar to prior answers.
Set a second variable equal to the first
Check the second against the first at regular intervals (or upon some event)
Respond accordingly
But organise it differently. And make sure you leave the last variable outside of the function. Moreover, at the end of your if conditional statements update the value for the one being checked for changes.
Hope it helps!
var lastValue = 0;
$(function () {
if (lastValue > originalValue){
console.log ("Increasing");
}
else if (lastValue < originalValue) {
console.log("Decreasing");
}
lastValue = originalValue;
});
Check this JSFiddle

In javascript, how to trigger event when a variable's value is changed? [duplicate]

This question already has answers here:
Listening for variable changes in JavaScript
(29 answers)
Closed 6 years ago.
I have two variables:
var trafficLightIsGreen = false;
var someoneIsRunningTheLight = false;
I would like to trigger an event when the two variables agree with my conditions:
if(trafficLightIsGreen && !someoneIsRunningTheLight){
go();
}
Assuming that those two booleans can change in any moment, how can I trigger my go() method when they change according to the my conditions?
There is no event which is raised when a given value is changed in Javascript. What you can do is provide a set of functions that wrap the specific values and generate events when they are called to modify the values.
function Create(callback) {
var isGreen = false;
var isRunning = false;
return {
getIsGreen : function() { return isGreen; },
setIsGreen : function(p) { isGreen = p; callback(isGreen, isRunning); },
getIsRunning : function() { return isRunning; },
setIsRunning : function(p) { isRunning = p; callback(isGreen, isRunning); }
};
}
Now you could call this function and link the callback to execute go():
var traffic = Create(function(isGreen, isRunning) {
if (isGreen && !isRunning) {
go();
}
});
traffic.setIsGreen(true);
//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};
var x1 = {
eventCurrentStatus:undefined,
get currentStatus(){
return this.eventCurrentStatus;
},
set currentStatus(val){
this.eventCurrentStatus=val;
}
};
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);
The most reliable way is to use setters like that:
var trafficLightIsGreen = false;
var someoneIsRunningTheLight = false;
var setTrafficLightIsGreen = function(val){
trafficLightIsGreen = val;
if (trafficLightIsGreen and !someoneIsRunningTheLight){
go();
};
};
var setSomeoneIsRunningTheLight = function(val){
trafficLightIsGreen = val;
if (trafficLightIsGreen and !someoneIsRunningTheLight){
go();
};
};
and then instead of assigning a value to a variable, you just invoke the setter:
setTrafficLightIsGreen(true);
There is no way to do it without polling with setInterval/Timeout.
If you can support Firefox only, you can use https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/watch
Which will tell you when a property of an object changes.
Your best solution is probably making them part of an object and adding getters, setters that you can send out notifications yourself, as JaredPar showed in his answer
You could always have the variables be part of an object and then use a special function to modify the contents of it. or access them via window.
The following code can be used to fire custom events when values have been changed as long as you use the format changeIndex(myVars, 'variable', 5); as compared to variable = 5;
Example:
function changeIndex(obj, prop, value, orgProp) {
if(typeof prop == 'string') { // Check to see if the prop is a string (first run)
return changeIndex(obj, prop.split('.'), value, prop);
} else if (prop.length === 1 && value !== undefined &&
typeof obj[prop[0]] === typeof value) {
// Check to see if the value of the passed argument matches the type of the current value
// Send custom event that the value has changed
var event = new CustomEvent('valueChanged', {'detail': {
prop : orgProp,
oldValue : obj[prop[0]],
newValue : value
}
});
window.dispatchEvent(event); // Send the custom event to the window
return obj[prop[0]] = value; // Set the value
} else if(value === undefined || typeof obj[prop[0]] !== typeof value) {
return;
} else {
// Recurse through the prop to get the correct property to change
return changeIndex(obj[prop[0]], prop.slice(1), value);
}
};
window.addEventListener('valueChanged', function(e) {
console.log("The value has changed for: " + e.detail.prop);
});
var myVars = {};
myVars.trafficLightIsGreen = false;
myVars.someoneIsRunningTheLight = false;
myVars.driverName = "John";
changeIndex(myVars, 'driverName', "Paul"); // The value has changed for: driverName
changeIndex(myVars, 'trafficLightIsGreen', true); // The value has changed for: traggicIsGreen
changeIndex(myVars, 'trafficLightIsGreen', 'false'); // Error. Doesn't set any value
var carname = "Pontiac";
var carNumber = 4;
changeIndex(window, 'carname', "Honda"); // The value has changed for: carname
changeIndex(window, 'carNumber', 4); // The value has changed for: carNumber
If you always wanted to pull from the window object you can modify changeIndex to always set obj to be window.
function should_i_go_now() {
if(trafficLightIsGreen && !someoneIsRunningTheLight) {
go();
} else {
setTimeout(function(){
should_i_go_now();
},30);
}
}
setTimeout(function(){
should_i_go_now();
},30);
If you were willing to have about a 1 millisecond delay between checks, you could place
window.setInterval()
on it, for example this won't crash your browser:
window.setInterval(function() {
if (trafficLightIsGreen && !someoneIsRunningTheLight) {
go();
}
}, 1);

Javascript: How do constantly monitor variables value

How do I constantly check a variables value. For example:
if(variable == 'value'){
dosomething();
}
This would work if I constantly looped it or something, but is there an efficient way of triggering that as soon as the variable is set to that value?
This solution use deprecated APIs. Computed properties and proxies are a better alternative except on the oldest browsers. See K2Span's answer for an example of how to use those.
Object.watch:
Watches for a property to be assigned a value and runs a function when that occurs.
Object.watch() for all browsers? talks about cross-browser ways to do Object.watch on browsers that don't support it natively.
Object.defineProperty(Object.prototype, 'watch', {
value: function(prop, handler){
var setter = function(val){
return val = handler.call(this, val);
};
Object.defineProperty(this, prop, {
set: setter
});
}
});
How to use:
var obj = {};
obj.watch('prop', function(value){
console.log('wow!',value);
});
obj.prop = 3;
Use setInterval:
var key = ''
setInterval(function(){
if(key == 'value'){
dosomething();
}
}, 1000);
As #Pekka commented, you can have a timer constantly poll the variable. A better solution, if it's all your code that's changing the variable, is to not just set the variable directly, but rather have all setters call a function. The function could then set the variable and do any additional processing you need.
function setValue(value) {
myVariable = value;
notifyWatchers();
}
If you encapsulate your variable so that the value can only be set by calling a function, it gives you the opportunity to check the value.
function ValueWatcher(value) {
this.onBeforeSet = function(){}
this.onAfterSet = function(){}
this.setValue = function(newVal) {
this.onBeforeSet(value, newVal)
value = newVal;
this.onAfterSet(newVal)
}
this.getValue = function() {
return value;
}
}
var name = new ValueWatcher("chris");
wacthedName.onBeforeChange = function(currentVal, newVal) {
alert("about to change from" + currentVal + " to " + newVal);
}
name.setValue("Connor");
I had a similar problem and was able to eliminate it using a timed function that I had used earlier. Even if you don't have a timed function there are easy to create;
var rand = 0
setInterval(function senseConst () {if (rand = 0) {rand = x}, 10);
//x could be equal to the variables value that you want to check
I used this to constantly have a variable that is the length of the page by making using the following code
var widthSensorOutput = 0
setInterval(function senseConst () {if (widthSensorOutput = 0) {widthSensorOutput = document.getElementById('widthSensor').clientWidth}, 10);
//'widthSensor' is a div with the width equal to 100%
I am not sure if this is the best way to solve your problem but it worked for me. To be clear, this is the the same basic code that Chandu gave, just I added what you should do once you are inside the function, which already is pretty obvious. I did not understand Chandu's post and did not realize that they used the same root code.

Categories