About Live Objects
A live object could be described as a particular object able to change without our interaction. The most common live object example is this:
// this is the most common live object
// the HTMLCollection
var divs = document.getElementsByTagName("div");
divs.length; // let's say 4
// let's add another div inside a generic node
document.body.appendChild(
document.createElement("div")
);
divs.length; // 5!
In few words DOM searches are dynamic, which is the reason almost every selector library needs to transform the current result into a static Array.
About LiveMonitor
Specially suited for live objects, LiveMonitor is a function which aim is to notify us when the specified property change:
// LiveMonitor example
var lm = new LiveMonitor(
// the entire list of elements trapped
document.getElementsByTagName("*"),
// the property to monitor
"length"
);
// add one or more notifier
lm.onchange(function(collection){
// this, will be the lm object
this.value; // will be collection.length
collection.length; // is the new length
alert([
"All nodes collection contained ",
this.value,
" nodes but now there are ",
collection.length
].join("\n"));
});
The example is quite silly, but the concept is that as soon as the monitor will check the "length" property, in this case in the one of the trapped collection, it will fire the onchange event, notifying us that somebody did something somewhere, and this something changed our monitored property.
LiveMonitor Code
var LiveMonitor = (function(){
/** LiveMonitor :: Asynchronous Property Monitor
* @author Andrea Giammarchi
* @license Mit Style
* @blog http://WebReflection.blogspot.com/
*/
function LiveMonitor(object, property){
this.value = object[property];
this._property = property;
this._object = object;
this._event = [];
this._i = 0;
};
LiveMonitor.prototype.onchange = function onchange(callback){
this._event.push(callback);
if(this._i === 0){
var _property = this._property,
_object = this._object,
_event = this._event,
self = this
;
this._i = setTimeout(function _i(){
if(self.value !== _object[_property]){
for(var i = 0, length = _event.length; i < length; ++i){
if(_event[i].call(self, _object) === false)
break
;
};
self.value = _object[_property];
};
if(0 < self._i)
self._i = setTimeout(_i, 15)
;
}, 15);
};
};
LiveMonitor.prototype.offchange = function offchange(callback){
for(var _event = this._event, i = 0, length = _event.length; i < length; ++i){
if(_event[i] === callback)
_event.splice(i, 1)
;
};
if(_event.length === 0){
clearTimeout(this._i);
this._i = 0;
};
};
LiveMonitor.prototype.clear = function clear(){
this._event = [];
this.offchange(null);
this.value = this._object[this._property];
};
return LiveMonitor;
})();
Not Only DOM Searches
LiveMonitor could be actually used as asynchronous notifier for any kind of variable.Let's say we have an environment able to load runtime scripts but we cannot change loaded scripts (external source inclusion).
At some point we press the "load jQuery" button and we would like to be able to be notiied when it is available ...
if(!window.jQuery){
// create a LiveMonitor instance over
// jQuery property
var jqlm = new LiveMonitor(window, "jQuery");
// add onchange event
jqlm.onchange(function(window){
// remove this event
this.offchange(arguments.callee);
// remove jqlm as well, it is not useful
// anymore for this example
// use jQuery, it's here for sure!
$("body").html("Hello LiveMonitor!");
});
// load external script
loadScript("http://external/jQuery.js");
};
Hey,
ReplyDeletein modern browsers you could listen for DOM mutation events to only look for property changes when neccessary:
http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents
hey, i still deal with IE6 on daily basis :D
ReplyDeletejokes a part, that is why I said it is like a cross browser watch, also mutation events could be truly greedy. As example, if you use LiveMonitor with getElementsByTagName("*") the event will be fired once about every 15 ms, if you use a DOMNodeInserted in the document, the entire application will slow down and the event fired for each innerHTML or inserted node.
So let's say this is an alternative ;)
P.S. also LiveMonitor is not only for DOM, it could be used with any kind of gloibal or nested variable too :)
ReplyDelete... plus live objects won't be supported ...
ReplyDeleteA good use would be in a selector engine like Sizzle, and only busting the cached results when the DOM is changed in any way.
ReplyDelete- Karmagination
Unfortunately Sizzle needs to be synchronous due to constant add/remove/replace DOM manipulation via jQuery or whatever other library based on Sizzle.
ReplyDeleteIt used DOMTreeModified event, initially, but for general DOM slowdown problems John had to remove it. This trick is less greedy but again, async ... let's see if anybody will use it for other purposes
If there is a 15ms timer cost for each property to monitor, I wonder what will happen when we have a few hundred objects each having several properties being monitored at any given time?
ReplyDeleteAny tests on these Andrea?