Implementing events in custom object
I'm not sure on all 100% but next is a result of my old research within this problem:
- You cannot make available somehow this.
- You can simply implement your own logic. For this you can use code that exist in MDN element.removeEventListener article with little changes. Below it copy\past of code from MDN link:
// code source: MDN: https://developer.mozilla.org/en/DOM/element.removeEventListener
// without changes
if (!Element.prototype.addEventListener) {
var oListeners = {};
function runListeners(oEvent) {
if (!oEvent) { oEvent = window.event; }
for (var iLstId = 0, iElId = 0, oEvtListeners = oListeners[oEvent.type]; iElId < oEvtListeners.aEls.length; iElId++) {
if (oEvtListeners.aEls[iElId] === this) {
for (iLstId; iLstId < oEvtListeners.aEvts[iElId].length; iLstId++) { oEvtListeners.aEvts[iElId][iLstId].call(this, oEvent); }
break;
}
}
}
Element.prototype.addEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {
if (oListeners.hasOwnProperty(sEventType)) {
var oEvtListeners = oListeners[sEventType];
for (var nElIdx = -1, iElId = 0; iElId < oEvtListeners.aEls.length; iElId++) {
if (oEvtListeners.aEls[iElId] === this) { nElIdx = iElId; break; }
}
if (nElIdx === -1) {
oEvtListeners.aEls.push(this);
oEvtListeners.aEvts.push([fListener]);
this["on" + sEventType] = runListeners;
} else {
var aElListeners = oEvtListeners.aEvts[nElIdx];
if (this["on" + sEventType] !== runListeners) {
aElListeners.splice(0);
this["on" + sEventType] = runListeners;
}
for (var iLstId = 0; iLstId < aElListeners.length; iLstId++) {
if (aElListeners[iLstId] === fListener) { return; }
}
aElListeners.push(fListener);
}
} else {
oListeners[sEventType] = { aEls: [this], aEvts: [ [fListener] ] };
this["on" + sEventType] = runListeners;
}
};
Element.prototype.removeEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {
if (!oListeners.hasOwnProperty(sEventType)) { return; }
var oEvtListeners = oListeners[sEventType];
for (var nElIdx = -1, iElId = 0; iElId < oEvtListeners.aEls.length; iElId++) {
if (oEvtListeners.aEls[iElId] === this) { nElIdx = iElId; break; }
}
if (nElIdx === -1) { return; }
for (var iLstId = 0, aElListeners = oEvtListeners.aEvts[nElIdx]; iLstId < aElListeners.length; iLstId++) {
if (aElListeners[iLstId] === fListener) { aElListeners.splice(iLstId, 1); }
}
};
}
- I think what all you need to change is to replace
Element.prototype
withCustomObject.prototype
. And for supportingdispathEvent
you must addCustomObject.prototype.dispatchEvent = runListener;
code line. Also is can be better to enclose this code into closure function;
I don't tested this in my apps but maybe this can help you.
UPDATE:
Next link points into code source that contains XObject()
class that supports event adding/removing and dispatching events. Test example is included. All code is based on answer above.
http://jsfiddle.net/8jZrR/
The addEventListener
function is a method of Element
class. One way is to make CustomObject
inherit from Element
like this:
CustomObject.prototype = Element.prototype;
The problem is that Element
class may have different implementations among different browsers. So for example firing events may not be easy (see this post).
So I advice doing this by yourself. It is not difficult, try something like this:
var CustomObject = function () {
var _this = this;
_this.events = {};
_this.addEventListener = function(name, handler) {
if (_this.events.hasOwnProperty(name))
_this.events[name].push(handler);
else
_this.events[name] = [handler];
};
_this.removeEventListener = function(name, handler) {
/* This is a bit tricky, because how would you identify functions?
This simple solution should work if you pass THE SAME handler. */
if (!_this.events.hasOwnProperty(name))
return;
var index = _this.events[name].indexOf(handler);
if (index != -1)
_this.events[name].splice(index, 1);
};
_this.fireEvent = function(name, args) {
if (!_this.events.hasOwnProperty(name))
return;
if (!args || !args.length)
args = [];
var evs = _this.events[name], l = evs.length;
for (var i = 0; i < l; i++) {
evs[i].apply(null, args);
}
};
}
Now using it is as simple as:
var co = new CustomObject();
co.addEventListener('textChange', function(name) {
console.log(name);
});
co.fireEvent('textChange', ['test']);
This is a basic solution. You may want to alter it, but I think you should grasp the idea.
I have improved my sample with the code from freakish. I still would extract the eventhandling part into a "base class"... maybe when there is some more time =)
There is also a sample for using jQuery!
<!doctype html>
<html lang="en">
<head>
<title>Custom Events Test</title>
<meta charset="utf-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>
/* jQuery
var CustomObjectTextChangedEventName = 'textChanged';
var CustomObject = function () {
var _this = this;
var _text = "";
_this.ChangeText = function (newText) {
_text = newText;
fireTextChanged();
};
function fireTextChanged() {
$(_this).trigger(CustomObjectTextChangedEventName, _text);
}
}
var myCustomObject;
$(document).ready(function () {
myCustomObject = new CustomObject();
$(myCustomObject).bind(CustomObjectTextChangedEventName, handleTextChanged);
})
function handleTextChanged(event, msg) {
window.alert(msg);
}
function buttonClick() {
var newText = document.getElementById('tbText').value;
myCustomObject.ChangeText(newText);
}
*/
var CustomObjectTextChangedEventName = 'textChanged';
var CustomObject = function (alias) {
var _this = this;
var _events = {};
var _text = "";
_this.Alias = alias;
_this.OnTextChanged = document.createEvent("Event");
_this.OnTextChanged.initEvent(CustomObjectTextChangedEventName, true, false);
_this.ChangeText = function (newText) {
var args = new TextChangedEventArgs();
args.OldText = _text;
args.NewText = newText;
_text = newText;
fireEvent(CustomObjectTextChangedEventName, args);
};
_this.addEventListener = function (name, handler) {
if (_events.hasOwnProperty(name))
_events[name].push(handler);
else
_events[name] = [handler];
};
_this.removeEventListener = function (name, handler) {
/* This is a bit tricky, because how would you identify functions?
This simple solution should work if you pass THE SAME handler. */
if (!_events.hasOwnProperty(name))
return;
var index = _events[name].indexOf(handler);
if (index != -1)
_events[name].splice(index, 1);
};
function fireEvent(name, args) {
if (!_events.hasOwnProperty(name))
return;
var evs = _events[name], l = evs.length;
for (var i = 0; i < l; i++) {
evs[i](_this, args);
}
}
}
var TextChangedEventArgs = function () {
var _this = this;
_this.OldText = null;
_this.NewText = null;
}
var myCustomObject;
var myCustomObject2;
window.onload = function () {
myCustomObject = new CustomObject("myCustomObject");
myCustomObject.addEventListener(CustomObjectTextChangedEventName, handleTextChanged);
myCustomObject2 = new CustomObject("myCustomObject2");
myCustomObject2.addEventListener(CustomObjectTextChangedEventName, handleTextChanged);
};
function handleTextChanged(sender, args) {
window.alert('At ' + sender.Alias + ' from [' + args.OldText + '] to [' + args.NewText + ']');
}
function buttonClick() {
var newText = document.getElementById('tbText').value;
myCustomObject.ChangeText(newText);
}
function buttonClick2() {
var newText = document.getElementById('tbText2').value;
myCustomObject2.ChangeText(newText);
}
</script>
</head>
<body>
<input type="text" id="tbText" />
<input type="button" value="Change" onclick="buttonClick();" />
<input type="text" id="tbText2" />
<input type="button" value="Change" onclick="buttonClick2();" />
</body>