VerizonW
VerizonW

Reputation: 1835

How to run a function in JavaScript every time a variable changes?

Is there a way in JavaScript to have something like an event that listens to changes in a variable? So when its value is modified, the event triggers and then I can call a function. To put this more into context, I have a function which handles the HTML rendering of an array of objects, and I want that function to be called automatically every time the array is modified.

Upvotes: 15

Views: 33786

Answers (7)

Gabriele Petrioli
Gabriele Petrioli

Reputation: 195982

Update

Since Object.watch has been deprecated (since 2018), you should have a look at using a Proxy to allow control over when something is getting updated. A good implementation can be found at How to create a Deep Proxy (aka Proxy Membrane)?

Original answer

Use object.watchdocs and if it is not supported natively look at this implementation: Object.watch() for all browsers?

Upvotes: 8

Laycookie
Laycookie

Reputation: 735

I personally prefer to not use proxy and instead use EventEmitter. It would work something like this.

import { EventEmitter } from "events";

const testChangeEvent = new EventEmitter();
function changeTest(newVal){
  test = newVal
  testChangeEvent.emit("change")
}
let test = "val" // variable we want to listen for

and now every time we call changeTest() we can detect it and run code with

testChangeEvent.on("change", () => {
  console.log("Code that is being called")
})

I'm 99% sure you can make it look a lot better and a lot more idiot-proof with getters and setters but this should be enough for small projects if your project is a commercial thought look into it please don't mack people debug for hours.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get

Upvotes: 0

Jan Turoň
Jan Turoň

Reputation: 32912

It is possible now using Proxy object, see the demo in my answer to similar issue here.

It is possible to create Proxy for array, see the snippet below. In your case, just replace the console.log with your DOM modifying methods. Be careful though: adding a value to array modifies the array length which can invoke the proxy multiple times and modifying the array with it's methods like push() can have other side effects to call the Proxy even more. It depends on UA implementation, but at least one Proxy call per modification should be guaranteed.

const ProxyArr = (arr, fn) => new Proxy(arr, {
    get: (arr, key) => arr[key],
    set: (arr, key, val) => fn(arr[key] = val, key, arr) || 1
});


// listen to array changes
let handler = (v, k, a) => console.log(`setting ${v} at ${k} (${a.length} items)`);
let arr = [];
let proxy = ProxyArr(arr, handler);

// test (see the console output)
proxy[0] = "aa";
proxy.push("bb");
console.log(arr);
console.log(proxy);

Upvotes: 3

Andrew
Andrew

Reputation: 14526

Because JavaScript doesn't universally support setter/getter methods yet, I'd recommend you think about how you set your variables. One technique that would work is:

Array.prototype.setMember = function(index,newValue) {
    alert("I will perform some action here");
    this[index] = newValue;
}

var myArray = [1,2,3];
// x[0] = 11; // Don't do this any more
x.setMember(0,11);
alert(x[0]);

I'm personally not a huge fan of adding new methods to base prototypes, but it makes things easier to refactor in the short term.

Upvotes: 1

xanatos
xanatos

Reputation: 111850

In ECMAScript 5 there are getter/setter properties... Read here: http://ejohn.org/blog/ecmascript-5-objects-and-properties/

Non-IE browsers support something similar:

http://robertnyman.com/2009/05/28/getters-and-setters-with-javascript-code-samples-and-demos/

For IE, you'll have to wait for IE9, or use only DOM-bases getters/setters.

Upvotes: 2

pimvdb
pimvdb

Reputation: 154828

You could use setInterval to check for its value so many times a second, and save it into a separate variable. You can check each time whether the real variable is different from the other one. In that case, call the function.

It's a dirty trick, though.

Upvotes: 2

Pascal MARTIN
Pascal MARTIN

Reputation: 400972

I don't think what you ask is possible.

A solution might be to :

  • encapsulate your data into a specific object
  • access that data using a setter method of that object
  • have that setter method both :
    • set the data
    • call your function

But it'll require you to rewrite a bit of your code.

Upvotes: 6

Related Questions