views:

310

answers:

2

I want to be able to use a bound event handler in prototype and retain my "this" context. But doing so seems to destroy the convenient default binding that prototype gives event handlers.

From the docs:

The handler's context (this value) is set to the extended element being observed (even if the event actually occurred on a descendent element and bubbled up).

That's exactly what I want. Once I bind the function to my own context, can I get access to this "element being observed" in any way? I can't depend on e.element() to give me the observed element (it could be a child element):

initialize: function() {
     Event.observe('foo', 'click', this.addItem.bind(this));
},

...

addItem: function(e) {
   e.element() // -> clicked element, not necessarily observed element
}

I know that there's a bindAsEventListener, but it doesn't seem to be necessary here and I don't see how I can access the targeted element once the function is bound.

I also am aware that there's an e.currentTarget, but that doesn't work in IE, does it?

A: 

I too had this problem recently. My answer was to wrap the function in another function that does the work you need. I may have messed up the order but curry will add this to the args of the anonymous function and then you re-order from there.

initialize: function() {
  Event.observe('foo', 'click', 
    function(e,that) { that.addItem(e,this); }.curry(this)
    );
  },
addItem: function(evt, evtElm) {
  ...
  }
Brett Pontarelli
+1  A: 

When you use .bind your reference to the observed element via this is overwritten. If you have the following markup:

<div id="foo">
    I am the Foo
    <button id="bar">I am the Bar</button>
</div>

Without using .bind you can access the observed & firing elements like so:

function greenEggsAndHam (evt) {
    console.log('Observed Element: %o', this);
    console.log('Clicked Element: %o', $(evt.element()));
}
Event.observe('foo', 'click', greenEggsAndHam);

However, if you want to use a class method as the event handler, and thus need to use .bind, then the solution is to include the observed element's identifier as one of the arguments to the event handler when you first bind the event, like so:

var Test = Class.create({
    initialize: function() {
        Event.observe('foo', 'click', this.greenEggsAndHam.bind(this, 'foo'));
    },
    greenEggsAndHam: function (el, evt) {
        console.log('Observed Element: %o', $(el));
        console.log('Clicked Element: %o', $(evt.element()));
  console.log('Class reference: %o', this);
    }
});
var T = new Test();

That said, for sake of consistency I'd suggest you use .bindAsEventListener instead of .bind, and change your event handler to function (evt, el) { so that the event object is the first argument and the observed element the second.

Sam C