Object.watch() for all browsers?
Asked Answered
B

9

125

Please note that Object.Watch and Object.Observe are both deprecated now (as of Jun 2018).


I was looking for an easy way to monitor an object or variable for changes, and I found Object.watch(), that's supported in Mozilla browsers, but not IE. So I started searching around to see if anyone had written some sort of equivalent.

About the only thing I've found has been a jQuery plugin, but I'm not sure if that's the best way to go. I certainly use jQuery in most of my projects, so I'm not worried about the jQuery aspect...

Anyway, the question: Can someone show me a working example of that jQuery plugin? I'm having problems making it work...

Or, does anyone know of any better alternatives that would work cross browser?

Update after answers:

Thanks everyone for the responses! I tried out the code posted here: http://webreflection.blogspot.com/2009/01/internet-explorer-object-watch.html

But I couldn't seem to make it work with IE. The code below works fine in Firefox, but does nothing in IE. In Firefox, each time watcher.status is changed, the document.write() in watcher.watch() is called and you can see the output on the page. In IE, that doesn't happen, but I can see that watcher.status is updating the value, because the last document.write() call shows the correct value (in both IE and FF). But, if the callback function isn't called, then that's kind of pointless... :)

Am I missing something?

var options = {'status': 'no status'},
watcher = createWatcher(options);

watcher.watch("status", function(prop, oldValue, newValue) {
  document.write("old: " + oldValue + ", new: " + newValue + "<br>");
  return newValue;
});

watcher.status = 'asdf';
watcher.status = '1234';

document.write(watcher.status + "<br>");
Basildon answered 22/6, 2009 at 20:29 Comment(2)
IIRC you can use onPropertyChange in IEVidicon
Replace document.write() with alert(). It should work just fine.Dogface
S
116

(Sorry for the cross-posting, but this answer I gave to a similar question works fine here)

I have created a small object.watch shim for this a while ago. It works in IE8, Safari, Chrome, Firefox, Opera, etc.

Superstar answered 13/8, 2009 at 5:32 Comment(5)
An explanation on how to use it and how this works internally would be nice for those of us that are not JS masters, thanks.Copra
developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…Superstar
jsfiddle.net/kSWxP HINT: use Firefox (the latter statement is not printed in Chrome)Billups
developer.mozilla.org/ru/docs/Web/JavaScript/Reference/…Barricade
I had know idea Object.defineProperty() existed. I ended up looking at the MDN reference to see how it worked.Tinea
R
20

That plugin simply uses a timer/interval to repeatedly check for changes on an object. Maybe good enough but personally I would like more immediacy as an observer.

Here's an attempt at bringing watch/unwatch to IE: http://webreflection.blogspot.com/2009/01/internet-explorer-object-watch.html.

It does change the syntax from the Firefox way of adding observers. Instead of :

var obj = {foo:'bar'};
obj.watch('foo', fooChanged);

You do:

var obj = {foo:'bar'};
var watcher = createWatcher(obj);
watcher.watch('foo', fooChanged);

Not as sweet, but as an observer you are notified immediately.

Rehearsal answered 22/6, 2009 at 21:16 Comment(1)
Yep, watch those timestamps... no need to downvote, also crescentfresh added more info to the post, even if it was the same link. Meanwhile, I've tried the code found on that page and still see a problem. I may be overlooking something though. I've updated my original post with more info...Basildon
T
20

The answers to this question are a bit outdated. Object.watch and Object.observe are both deprecated and should not be used.

Today, you can now use the Proxy object to monitor (and intercept) changes made to an object. 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'

If you need to observe changes made to a nested object, then you need to use a specialized library. I published Observable Slim and 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}}]

 
Trever answered 14/6, 2018 at 5:4 Comment(2)
Thanks for ObservableSlim, it seems quite nice! Do you have any info on CPU usage / battery usage for ObservableSlim? From what I can tell, using .pause() does not impact the use of your setTimeout calls, but this might be irrelevant? In that case, do you know if there is any additional cpu/battery consumption related to the use of Proxy objects?Alainaalaine
@Alainaalaine I'm glad to hear you're finding a use for it! Yep, it's quite efficient with large nested objects -- I spent a great deal of time optimizing performance. Unless you're dealing with arrays of hundreds of thousands of deeply nested objects, then you'll probably be just fine. But to be safe, you could open up the Chrome profiler and see how much time your application spends churning cycles in the Observable Slim library. If it's not a significant percentage and the Observable Slim listeners are triggering quickly (i.e., not laggy) then you probably don't have anything to worry about.Trever
F
14

Current Answer

Use the new Proxy object, which can watch changes to it's target.

let validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError('The age is not an integer');
            }
            if (value > 200) {
                throw new RangeError('The age seems invalid');
            }
        }

        // The default behavior to store the value
        obj[prop] = value;

        // Indicate success
        return true;
    }
};

let person = new Proxy({}, validator);

person.age = 100;
console.log(person.age); // 100
person.age = 'young'; // Throws an exception
person.age = 300; // Throws an exception

Old answer from 2015

You could have used Object.observe() from ES7. Here's a polyfill. But Object.observe() is now cancelled. Sorry people!

Farleigh answered 27/6, 2013 at 14:54 Comment(2)
Hm, I couldn't get this polyfill to work on ios safari. I get Error: undefined is not a function (evaluating 'Object.observe(a.imgTouch,function(a){console.debug("lastX: "+a)})')Pelfrey
@Pelfrey Hi, would you like to open an issue on the project's page, with all the details you can give? I haven't tested it on iOS, but I'll look into the problem.Rasp
S
7

Note that in Chrome 36 and higher you can use Object.observe as well. This is actually a part of a future ECMAScript standard, and not a browser-specific feature like Mozilla's Object.watch.

Object.observe only works on object properties, but is a lot more performant than Object.watch (which is meant for debugging purposes, not production use).

var options = {};

Object.observe(options, function(changes) {
    console.log(changes);
});

options.foo = 'bar';
Subminiaturize answered 16/7, 2014 at 12:27 Comment(1)
As in 2018 this is deprecated and no longer supported by all major browsersRabble
C
4

you can use Object.defineProperty.

watch the property bar in foo

Object.defineProperty(foo, "bar", {
  get: function (val){
      //some code to watch the getter function
  },

  set: function (val) {
      //some code to watch the setter function
  }
})
Caridadcarie answered 20/1, 2017 at 15:3 Comment(2)
This is great! But I would modify it abit :) To not overwrite original setter. I would do reference foo._someObject = foo.someObjectObau
simple and elegentInscrutable
F
1

I have used Watch.js in one of my projects. And it is working fine.One of the main advantage of using this library is :

"With Watch.JS you will not have to change the way you develop."

The example is given below

//defining our object however we like
var ex1 = {
	attr1: "initial value of attr1",
	attr2: "initial value of attr2"
};

//defining a 'watcher' for an attribute
watch(ex1, "attr1", function(){
	alert("attr1 changed!");
});

//when changing the attribute its watcher will be invoked
ex1.attr1 = "other value";
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/watch.min.js"></script>

This is as simple as this!

Fachanan answered 10/12, 2018 at 12:5 Comment(0)
Z
0

This works for me

 $('#img').hide().attr('src', "path/newImage.jpg").fadeIn('slow');
Zoophilous answered 11/4, 2022 at 15:27 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Xanthic
S
-2

I also think that right now the best solution is to use Watch.JS, find a nice tutorial here: Listen/Watch for object or array changes in Javascript (Property changed event on Javascript objects)

Smearcase answered 14/7, 2013 at 15:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.