On 26/05/2005 20:32, abs wrote:
My element:
<span onclick="alert('test')" id="mySpan">test</span>
Let's say that I don't know what is in this span's onclick event. Is it
possible to add another action to this element's onclick event ?
The code at the end of this post is a very generic way of adding and
removing listeners from an element, much in the same way as the
addEventListener method.
Though the code looks daunting, a lot of it is internal. For basic
usage, you need only three methods:
DispatcherFactory.createDispatcher()
This creates and returns a dispatcher object. Each dispatcher
maintains a list of listeners that are associated with a type
of event. When the dispatcher is attached to an element, it
forwards all events of this type to the listeners it manages.
The other two important methods are members of the dispatcher objects
returned by createDispatcher.
Dispatcher.add(listener)
This method adds a function to the internal list. Simple.
Dispatcher.attach(element, type)
This methods associates the dispatcher with a particular
event type, such as the click event, and a particular element.
If a listener already exists on that element for that type (an
onclick attribute, for example), it will be added to the
internal list before the dispatcher is attached.
So, given:
<span id="mySpan" onclick="alert('original');">Test</span>
and a reference to that element:
var element = document.getElementById('mySpan');
you can add a new function with:
var dispatcher = DispatcherFactory.createDispatcher();
dispatcher.add(function() {
alert('added');
});
dispatcher.attach(element, 'onclick');
You can call add and attach in any order, and you can add listeners at
any time. You can also attach the same dispatcher to several different
elements with different types, though I doubt you'll have any use for that.
The other two methods you can use are again dispatcher members.
Dispatcher.remove(listener)
This searches for the given function. If it exists within the
list, it's discarded.
Dispatcher.detach(element, type)
This removes the dispatcher and all of its listeners of the
given type from the element.
There are simpler solutions, but they aren't as flexible. It's
incredibly late now, so someone else will have to continue with those
alternatives.
oncl = document.getElementById('mySpan').onclick
oncl = oncl + '\n;alert(\'added\')'
document.getElementById('mySpan').onclick = oncl
There's a misconception here: the onclick property isn't a string. When
a user agent parses attributes like onclick in HTML, it converts the
code to a function. So, it's not simply a matter of added more code. The
dispatcher object below allows you to manage several functions on one
property.
Do ask if you have any questions.
Mike
Parts of the code below can be removed. If you won't be removing
listeners, you can eliminate the two remove methods (in Node and
createDispatcher), the detachDispatcher function, and the line:
Dispatcher.detach = detachDispatcher;
The code at the very end emulates the call method that should exist on
all functions. JScript versions prior to 5.5 (and so usually IE versions
prior to 5.5) don't include this method so a substitute needs to be
included. The dispatcher code only needs case 1 within the switch
statement, so you could remove the other clauses. They were included to
make the substitute more useful.
var DispatcherFactory = (function(global) {
function Node(data) {
var next = null;
this.fire = function(element, event) {
var performDefault = next
? next.fire(element, event)
: true;
return data.call(element, event) && performDefault;
};
this.add = function(listener) {
if(data == listener) {return;}
if(next) {
next.add(listener);
} else {
next = new Node(listener);
}
};
this.remove = function(listener) {
if(data == listener) {
return next;
} else if(next) {
next = next.remove(listener);
}
return this;
};
}
function attachDispatcher(element, type) {
var listener = element[type];
if(('function' == typeof listener)
&& (this.constructor != listener.constructor))
{
this.add(listener);
}
element[type] = this;
}
function detachDispatcher(element, type) {
if(this == element[type]) {
element[type] = null;
}
}
return {
createDispatcher : function() {
var list = null;
function Dispatcher(event) {
return list
? list.fire(this, event || global.event)
: true;
}
}
Dispatcher.constructor = this;
Dispatcher.add = function(listener) {
if(list) {
list.add(listener);
} else {
list = new Node(listener);
}
};
Dispatcher.remove = function(listener) {
if(list) {
list = list.remove(listener);
}
};
Dispatcher.attach = attachDispatcher;
Dispatcher.detach = detachDispatcher;
}
};
})(this);
if('function' != typeof Function.prototype.call) {
Function.prototype.call = function(object) {
var property = '__call', result, undef;
while('undefined' != typeof object[property]) {
property = '__' + property;
}
object[property] = this;
switch(arguments.length - 1) {
case 0:
result = object[property]();
break;
case 1:
result = object[property](arguments[1]);
break;
case 2:
result = object[property](arguments[1], arguments[2]);
break;
case 3:
result = object[property](arguments[1], arguments[2],
arguments[3]);
break;
case 4:
result = object[property](arguments[1], arguments[2],
arguments[3], arguments[4]);
break;
case 5:
result = object[property](arguments[1], arguments[2],
arguments[3], arguments[4], arguments[5]);
break;
default: alert('Too many arguments!');
}
object[property] = undef;
return result;
};
}
--
Michael Winter
Replace ".invalid" with ".uk" to reply by e-mail.