On 26/05/2005 20:32, abs wrote:
My element:
<span onclick="alert( 'test')" id="mySpan">tes t</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
addEventListene r method.
Though the code looks daunting, a lot of it is internal. For basic
usage, you need only three methods:
DispatcherFacto ry.createDispat cher()
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 createDispatche r.
Dispatcher.add( listener)
This method adds a function to the internal list. Simple.
Dispatcher.atta ch(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');">T est</span>
and a reference to that element:
var element = document.getEle mentById('mySpa n');
you can add a new function with:
var dispatcher = DispatcherFacto ry.createDispat cher();
dispatcher.add( function() {
alert('added');
});
dispatcher.atta ch(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.remo ve(listener)
This searches for the given function. If it exists within the
list, it's discarded.
Dispatcher.deta ch(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.getEle mentById('mySpa n').onclick
oncl = oncl + '\n;alert(\'add ed\')'
document.getEle mentById('mySpa n').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
createDispatche r), the detachDispatche r function, and the line:
Dispatcher.deta ch = detachDispatche r;
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 DispatcherFacto ry = (function(globa l) {
function Node(data) {
var next = null;
this.fire = function(elemen t, event) {
var performDefault = next
? next.fire(eleme nt, event)
: true;
return data.call(eleme nt, event) && performDefault;
};
this.add = function(listen er) {
if(data == listener) {return;}
if(next) {
next.add(listen er);
} else {
next = new Node(listener);
}
};
this.remove = function(listen er) {
if(data == listener) {
return next;
} else if(next) {
next = next.remove(lis tener);
}
return this;
};
}
function attachDispatche r(element, type) {
var listener = element[type];
if(('function' == typeof listener)
&& (this.construct or != listener.constr uctor))
{
this.add(listen er);
}
element[type] = this;
}
function detachDispatche r(element, type) {
if(this == element[type]) {
element[type] = null;
}
}
return {
createDispatche r : function() {
var list = null;
function Dispatcher(even t) {
return list
? list.fire(this, event || global.event)
: true;
}
}
Dispatcher.cons tructor = this;
Dispatcher.add = function(listen er) {
if(list) {
list.add(listen er);
} else {
list = new Node(listener);
}
};
Dispatcher.remo ve = function(listen er) {
if(list) {
list = list.remove(lis tener);
}
};
Dispatcher.atta ch = attachDispatche r;
Dispatcher.deta ch = detachDispatche r;
}
};
})(this);
if('function' != typeof Function.protot ype.call) {
Function.protot ype.call = function(object ) {
var property = '__call', result, undef;
while('undefine d' != typeof object[property]) {
property = '__' + property;
}
object[property] = this;
switch(argument s.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.