views:

33

answers:

2

So I have been adding my events thusly:

element.addEvent('click', function() {
    alert('foobar');
});

However, when attempting to remove said event, this syntactically identical code (with "add" switched to "remove") does not work.

element.removeEvent('click', function() {
    alert('foobar');
});

I assume this is because the two functions defined are not referenced the same, so the event is not technically removed. Alright, so I redefine the event addition and removal:

element.addEvent('click', alert('foobar'));
element.removeEvent('click', alert('foobar'));

Which works great, except now when the page loads, the click event is fired even before it's clicked!

The function is removed, though, which is great......

+1  A: 

assig the function to a variable and use the same reference to add and remove the event. if you use an anonymous function you will get to different references

var test = function(){ alert('test: ' + this.id); } 
$('element').addEvent('click', test);

...
$('element').removeEvent('click', test);
Nikolaus Gradwohl
+2  A: 

the reason for this is that Events are actually keys in element storage. have a look at this fiddle i did a while back for another SO question - http://www.jsfiddle.net/mVJDr/

it will check to see how many events are stacked up for a particular event type on any given element (or all events).

similarly, removeEvent looks for a match in the events storage - have a look on http://jsfiddle.net/dimitar/wLuY3/1/. hence, using named functions like Nikolaus suggested allows you to remove them easily as it provides a match.

also, you can remove events via element.removeEvents("click") for all click events.

your page now alerts because you pass on alert as the function as well as execute it with the params 'foobar'. METHOD followed by () in javascript means RUN THE METHOD PRECEDING IT IMMEDIATELY, NOT LATER. when you bind functions to events, you pass the reference (the method name) only.

to avoid using an anonymous function and to pass argument,s you can do something like:

document.id('foobar').addEvent('click', alert.bind(this, 'foo'));

as bind raps it for you, but removing this will be even more complicated.

as for event delegation, it's:

parentEl.addEvents({
    "click:relay(a.linkout)": function(e, el) {

    },
    "mouseover:relay(li.menu)": function(e, el) {

    }
});

more on that here http://mootools.net/docs/more/Element/Element.Delegation#Element:removeEvent

keep in mind it's not great / very stable. works fine for click stuff, mouseenter is not to be used delegated, just mouseover - which means IE can fire mouseout when it should not. the way i understand it, it's coming improved in mootools 2.0

edit updating to show an example of bound and unbound method within a class pattern in mootools

http://www.jsfiddle.net/wmhgw/

var foo = new Class({
    message: "hi",
    toElement: function() {
        return this.element = new Element("a", {
            href: "http://www.google.com",
            text: "google",
            events: {
                "click": this.bar.bind(this), // bind it
                "mouseenter": this.bar // unbound -> this.element becomes this
            }
        });

    },
    bar: function(event) {
        event.stop();
        // hi when bound to class instance (this.message will exist)
        // 'undefined' otherwise.
        console.log(this.message || "undefined");
    }
});

document.id(new foo()).inject(document.body);

the mouseenter here will be unbound where this will refer to the default scope (i.e the element that triggered the event - the a href). when bound, you can get the element via event.target instead - the event object is always passed on to the function as a parameter.

btw, this is a slightly less familiar use of class and element relation but it serves my purposes here to illustrate binding in the context of classes.

Dimitar Christoff
Thanks for the clarification on js functions. I did not know that appending a () caused the function to be executed. Very interesting.Is there any advantage to binding functions in your example above?
Julian H. Lam
it does. this is why you get anonymous closures like this that work: `(function() { ... })();` the `()` is what runs the function after creation, giving you block scope in javascript. as for the bind, it's useful in changing what `this` will refer to in the execution function. for mootools in particular where you often use classes / oop, you tend to assign class methods to mouse events where you also try to keep the scope to the class, i.e. within the callback function `this` will be the class instance and not the element that was clicked on as would be
Dimitar Christoff