Is it possible to listen for changes to an object's attributes in JavaScript?
Solution 1
Thanks for the comments guys. I've gone with the following:
var EntriesRegistry = (function(){
var instance = null;
function __constructor() {
var
self = this,
observations = {};
this.set = function(n,v)
{
self[n] = v;
if( observations[n] )
for( var i=0; i < observations[n].length; i++ )
observations[n][i].apply(null, [v, n]);
}
this.get = function(n)
{
return self[n];
}
this.observe = function(n,f)
{
if(observations[n] == undefined)
observations[n] = [];
observations[n].push(f);
}
}
return new function(){
this.getInstance = function(){
if (instance == null)
{
instance = new __constructor();
instance.constructor = null;
}
return instance;
}
}
})();
var entries = EntriesRegistry.getInstance();
var test = function(v){ alert(v); };
entries.set('bob', 'meh');
entries.get('bob');
entries.observe('seth', test);
entries.set('seth', 'dave');
Taking on-board your comments, I'll be using event delegation on the form objects to update the registry and trigger the registered observing methods.
This is working well for me so far... can you guys see any problems with this?
Solution 2
As far as I know, there are no events fired on Object attribute changes (edit: except, apparently, for Object.watch
).
Why not use event delegation wherever possible? That is, events on the form rather than on individual form elements, capturing events as they bubble up?
For instance (my jQuery is rusty, forgive me for using Prototype instead, but I'm sure you'll be able to adapt it easily):
$(form).observe('change', function(e) {
// To identify changed field, in Proto use e.element()
// but I think in jQuery it's e.target (as it should be)
});
You can also capture input
and keyup
and paste
events if you want it to fire on text fields before they lose focus. My solution for this is usually:
-
Gecko/Webkit-based browsers: observe
input
on theform
. -
Also in Webkit-based browsers: observe
keyup
andpaste
events ontextarea
s (they do not fireinput
ontextarea
s for some reason). -
IE: observe
keyup
andpaste
on theform
- Observe
change
on theform
(this fires onselect
s). - For
keyup
andpaste
events, compare a field's current value against its default (what its value was when the page was loaded) by comparing a text field'svalue
to itsdefaultValue
Edit: Here's example code I developed for preventing unmodified form submission and the like:
What is the best way to track changes in a form via javascript?
Solution 3
You could attach a listener to a container (the body or the form) and then use the event parameter to react to the change. You get all the listener goodness but only have to attach one for the container instead of one for every element.
$('body').change(function(event){
/* do whatever you want with event.target here */
console.debug(event.target); /* assuming firebug */
});
The event.target holds the element that was clicked on.
SitePoint has a nice explanation here of event delegation:
JavaScript event delegation is a simple technique by which you add a single event handler to a parent element in order to avoid having to add event handlers to multiple child elements.
Solution 4
Mozilla-engined browsers support Object.watch, but I'm not aware of a cross-browser compatible equivalent.
Have you profiled the page with Firebug to get an idea of exactly what's causing the slowness, or is "lots of event handlers" a guess?
Solution 5
Small modification to the previous answer : by moving the observable code to an object, one can make an abstraction out of it and use it to extend other objects with jQuery's extend method.
ObservableProperties = {
events : {},
on : function(type, f)
{
if(!this.events[type]) this.events[type] = [];
this.events[type].push({
action: f,
type: type,
target: this
});
},
trigger : function(type)
{
if (this.events[type]!==undefined)
{
for(var e = 0, imax = this.events[type].length ; e < imax ; ++e)
{
this.events[type][e].action(this.events[type][e]);
}
}
},
removeEventListener : function(type, f)
{
if(this.events[type])
{
for(var e = 0, imax = this.events[type].length ; e < imax ; ++e)
{
if(this.events[type][e].action == f)
this.events[type].splice(e, 1);
}
}
}
};
Object.freeze(ObservableProperties);
var SomeBusinessObject = function (){
self = $.extend(true,{},ObservableProperties);
self.someAttr = 1000
self.someMethod = function(){
// some code
}
return self;
}
See the fiddle : https://jsfiddle.net/v2mcwpw7/3/
Related videos on Youtube
user3047801
Delivery-focussed Elixir/Erlang, Python, XSL/XQuery, and (No)SQL developer, specialising in content/data migration, API/micro-service design, and Scrum mentorship.
Updated on July 09, 2022Comments
-
user3047801 almost 2 years
I'm working on a fiddly web interface which is mostly built with JavaScript. Its basically one (very) large form with many sections. Each section is built based on options from other parts of the form. Whenever those options change the new values are noted in a "registry" type object and the other sections re-populate accordingly.
Having event listeners on the many form fields is starting to slow things down, and refreshing the whole form for each change would be too heavy/slow for the user.
I'm wondering whether its possible to add listeners to the registry object's attributes rather than the form elements to speed things up a bit? And, if so, could you provide/point me to some sample code?
Further information:
- This is a plug-in for jQuery, so any functionality I can build-on from that library would be helpful but not essential.
- Our users are using IE6/7, Safari and FF2/3, so if it is possible but only for "modern" browsers I'll have to find a different solution.
-
Sumi Straessle over 8 yearsOne could argue that we can bind the
ObservableProperties
object directly to the business object by doing$.extend(true,this,ObservableProperties);
It does look better. However, be careful about scope. I use aself
variable when I do things a bit complicated with scope (or a Singleton for that matter), otherwise if everything is public, I go withthis
. See this fiddle -
andersfylling almost 8 yearsObject.observe() has been deprecated. Try to avoid using this as much as possible for your own sake.