473,387 Members | 3,787 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Object method as event listener

I want each instance of an object to be able to listen for input events.
When the event occurs, a method of the object should be called, such
that "this" is in scope and refers to the object instance.

Is this possible?

Example:

function MyConstructor(element)
{
//element is some HTML element
this.addListeners(element);

this.foo = "Bar";

return this;
}

MyConstructor.prototype.addListeners = function(element)
{
element.addEventListener("keypress", this.doSomething, true);
}

MyConstructor.prototype.doSomething = function(e)
{

alert(this.foo.length); //Error: this.foo has no properties

alert(this);
//shows the HTML element that triggered the event
}

Is there any way to get "this" to refer to the object in the event listener?

Thanks,
Jeremy
Oct 16 '06 #1
6 6840
aka
No problem:
function MyConstructor(element)
{
//element is some HTML element
this.addListeners(element);

this.foo = "Bar";
// not necessary
//return this;
}

MyConstructor.prototype.addListeners = function(element)
{
// don't take native DOM function, it's not supported by IE5/6,
take a workaround, for example:
// http://aka-fotos.de/research/js/prototype_extended.js < take the
last both functions
//element.addEventListener("keypress", this.doSomething, true);
addEventListener(element, "keypress", this.doSomething, true);
// make a backlink
element._obj = this;
}
// it's not necessary any more to make a object function because
THIS is the html element
MyConstructor./*prototype.*/doSomething = function(e)
{

alert(this._obj.foo.length); // should work now

alert(this._obj);
//shows the MyConstructor object now
}
That should work, I do it very often in OO and UI programming.

Andi

Oct 16 '06 #2
Jeremy wrote:
I want each instance of an object to be able to listen for input events.
When the event occurs, a method of the object should be called, such
that "this" is in scope and refers to the object instance.
All function objects have a this value that is established when the
function is called, not when you declare it. You can modify the object
used for the this value using the function's apply() or call() methods.

The usual method is to use the this keyword to create a reference back
to the object when you attach the event, not try to get a reference to
the object at some later time. You might also consider simply adding
the property to a prototype object at an appropriate point in the scope
chain and avoid munging the this keyword altogether.

Is this possible?

Example:

function MyConstructor(element)
{
//element is some HTML element
this.addListeners(element);

this.foo = "Bar";

return this;
}

MyConstructor.prototype.addListeners = function(element)
{
element.addEventListener("keypress", this.doSomething, true);
Is this only for W3C browers? You seem to be ignoring IE's attachEvent.
}

MyConstructor.prototype.doSomething = function(e)
{

alert(this.foo.length); //Error: this.foo has no properties
Because the foo property belongs to the (possibly anonymous) object
that was used to attach the event, which is not what the this keyword
will refer to when the event calls the function.

In Gecko browsers, the this keyword will refer to the HTML element that
the event is attached to. In IE, it will refer to the window/global
object.
alert(this);
//shows the HTML element that triggered the event
Or the window object in IE. Try replacing the body of the addListeners
function with:

var obj = this;
element.onkeypress = function(){
obj.doSomething.apply(obj);
}

But the closure will likely cause a memory leak in IE. If you really
want to use attachEvent, try:

var obj = this;
if (element.addEventListener){
element.addEventListener(
'keyup',
function() {obj.doSomething.apply(obj)},
true
);
} else if (element.attachEvent){
element.attachEvent(
'onkeyup',
function() {obj.doSomething.apply(obj)}
);
}

Again you have a closure/memory leak issue in IE.

}

Is there any way to get "this" to refer to the object in the event listener?
Another strategy is to add a property to the element that points back
to the object and use the event to find the target/source element and
hence get the foo property:

MyConstructor.prototype.addListeners = function(element)
{
element.srcObj = this;
if (element.addEventListener){
element.addEventListener('keyup', this.doSomething, true);
} else if (element.attachEvent){
element.attachEvent('onkeyup', this.doSomething);
}
}

MyConstructor.prototype.doSomething = function(e)
{
var e = e || window.event;
var tgt = e.target || e.srcElement;
alert(tgt.srcObj.foo);
}

--
Rob

Oct 17 '06 #3
Jeremy wrote:
I want each instance of an object to be able to listen for input events.
When the event occurs, a method of the object should be called, such
that "this" is in scope and refers to the object instance.

Is this possible?
Yes ! With the help and magic of 'apply', this can be done. Here's how
it can be done (the bindAsEventListener function was taken from the
Prototype library at http://prototype.conio.net/) :
<div id="test1">Click me 1</div>
<div id="test2">Click me 2</div>

<script type="text/javascript">

Function.prototype.bindAsEventListener = function(object) {
var args = [];
for (var i = 0; i < arguments.length; i++)
args.push(arguments[i]);

var __method = this;
var object = args.shift();
return function(event) {
return __method.apply(object, [( event ||
window.event)].concat(args).concat(arguments));
}
};

var Events = {

clickHandler: function(event) {

alert( this.id + ' was clicked !' );

}

};

var test1 = document.getElementById('test1');
var test2 = document.getElementById('test2');

test1.onclick = Events.clickHandler.bindAsEventListener(test1);
test2.onclick = Events.clickHandler.bindAsEventListener(test2);
As you can see, the bindAsEventListener ensures that 'this' inside the
function always refers to the object passed as parameter.

Hope this helps.

-Yanick

Oct 17 '06 #4
BTW : you would normally only worry about memory leaks when repeatedly
manipulating the same object property with such code
(bindAsEventListener), so it's mostly a matter of choosing wisely how
to use what when.

Oct 17 '06 #5
RobG wrote:
Is this only for W3C browers? You seem to be ignoring IE's attachEvent.
Initially, I'm targeting standards, not I.E. If IE7 fails to support
the W3 model I might consider hacking in attachEvent support, but this
is a personal project so I can support whatever I wish and I prefer to
target standards.

Thanks for the thorough explanation. All that replied have been quite
helpful.

Jeremy
Oct 17 '06 #6
Yanick wrote:
Jeremy wrote:
I want each instance of an object to be able to listen for input events.
When the event occurs, a method of the object should be called, such
that "this" is in scope and refers to the object instance.

Is this possible?

Yes ! With the help and magic of 'apply', this can be done. Here's how
it can be done (the bindAsEventListener function was taken from the
Prototype library at http://prototype.conio.net/) :

<div id="test1">Click me 1</div>
<div id="test2">Click me 2</div>

<script type="text/javascript">

Function.prototype.bindAsEventListener = function(object) {
var args = [];
for (var i = 0; i < arguments.length; i++)
args.push(arguments[i]);

var __method = this;
var object = args.shift();
return function(event) {
return __method.apply(object, [( event ||
window.event)].concat(args).concat(arguments));
}
};

var Events = {
clickHandler: function(event) {
alert( this.id + ' was clicked !' );
The value of this.id isn't necessarily the id of the element that was
clicked on, it is the id of the element reference that was passed to
the function. It would be better example to have:

clickHandler: function(event) {
var tgt = event.target || event.srcElement;
if (tgt.nodeType != 1) tgt = tgt.parentNode;
alert(
'You clicked on ' + tgt.id + '\nbut '
+ 'this.id is: ' + this.id
);
}

and modify the attach code as suggested below.

}
};

var test1 = document.getElementById('test1');
var test2 = document.getElementById('test2');

test1.onclick = Events.clickHandler.bindAsEventListener(test1);
test2.onclick = Events.clickHandler.bindAsEventListener(test2);
It might have been a better example to use:

test1.onclick = ...(test2);
test2.onclick = ...(test1);

To show how the this value had been modified.

As you can see, the bindAsEventListener ensures that 'this' inside the
function always refers to the object passed as parameter.
But it seems a long way around, considering you passed the object to
the function in the first place and could just grabbed it from the
arguments object instead of messing with apply and the this value.

The following:

var Events ={};
Events.showID = function (){
alert(this.id);
}
var test3 = document.getElementById('test3')
test3.onclick = Events.showID;
achieves the same result in much less code - provided you have an
element with id='test3' of course. :-)
--
Rob

Oct 18 '06 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: Joe Kelsey | last post by:
When you use addEventListener (or addEvent in IE) to call an object method, does it call it with the correct this parameter? The ECMAScript reference has a lot to say about the caller using...
14
by: Michael Winter | last post by:
In an attempt to answer another question in this group, I've had to resort to calling the DOM method, Node.removeChild(), using a reference to it (long story...). That is, passing Node.removeChild....
6
by: b.dam | last post by:
I'm trying to do the following: function row() { this.selected = false; this._el = document.createElement("TR"); this._el.attachEvent("onClick", this.rowClick); } row.prototype.rowClick =...
1
by: Jim P. | last post by:
I'm having trouble returning an object from an AsyncCallback called inside a threaded infinite loop. I'm working on a Peer2Peer app that uses an AsyncCallback to rerieve the data from the remote...
4
by: Luke Matuszewski | last post by:
Here are some questions that i am interested about and wanted to here an explanation/discussion: 1. (general) Is the objectness in JavaScript was supported from the very first version of it (in...
16
by: anonymous.user0 | last post by:
The way I understand it, if I have an object Listener that has registered as a listener for some event Event that's produced by an object Emitter, as long as Emitter is still allocated Listener...
2
by: darthghandi | last post by:
I am creating a listener class that listens for incoming connections. When I get an incoming connection, I want to signal an event to happen and pass that connected socket to a different thread...
3
by: Greg A | last post by:
What I am unsuccessful at doing is adding the event listener to an object in memory which isn't referencing an HTML element: // DATA OBJECT CONSTRUCTOR function dataObject(id){ // ...
6
by: lochru45 | last post by:
I'm running the following javascript with no problem in FireFox: var multiGallery = { init: function(options){ this.overlay = new...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.