Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > Set event handler to global object's method...?

Reply
Thread Tools

Set event handler to global object's method...?

 
 
Joakim Braun
Guest
Posts: n/a
 
      12-22-2004
Why doesn't the below code work?

I'm trying to create a global object and set an event handler to one of its
methods. The function is called, but the object's mTest property is
undefined.

(What I'm trying to do is make a general-purpose solution for the situation
where you have a list box with several associated form elements. When the
element values are changed, you want to update the value of the selected
list option, and when the list selection changes, you want to update the
form elements. So here you could have a "watcher" object that is constructed
with a bunch of element names and handles that stuff without any custom
code.)

Joakim Braun

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

<script type="text/javascript">

function cWatcher(){

this.mTest = "Testing";
this.wire = cWatcher_Wire;
this.changeFunc = cWatcher_Changed;
}


function cWatcher_Wire( inFormName,
inElementName){

document.forms[inFormName].elements[inElementName].onchange =
this.changeFunc;

}

function cWatcher_Changed(){

alert("Changed, test=" + this.mTest);

}
</script>
</head>

<body>
<form id="form1" action="">
<select id="obj1">
<option value="1">Data</option>
<option value="2">More data</option>
</select>
</form>

<script type="text/javascript">

window.gWatcher = new cWatcher();
window.gWatcher.wire("form1","obj1");
</script>
</body>
</html>


 
Reply With Quote
 
 
 
 
Michael Winter
Guest
Posts: n/a
 
      12-23-2004
On Wed, 22 Dec 2004 22:51:27 +0100, Joakim Braun
<> wrote:

> Why doesn't the below code work?
>
> I'm trying to create a global object and set an event handler to one of
> its methods. The function is called, but the object's mTest property is
> undefined.


Read the following slowly. I always find it difficult to word properly.

In the case of using the this operator in a function, it is set by the
caller when the function is called. That is, the caller determines what
this refers to when the call is made.

When you assign a function reference to the property of an object and then
call that function as a method, the this operator will refer to said
object:

var myObject = new Object();
function myFunction() {}

/* myFunction is a property of the global object so when the this
* operator is used, it will refer to the global object.
*/
myFunction();

/* myFunction is now called as a method of the object, myObject.
* Here the this operator will refer to myObject.
*/
myObject.myMethod = myFunction;
myObject.myMethod()

So, when you assign a method from one object to another, the this operator
won't point to the original object, it will point to the one it was called
from.

One way around this is to use a closure and reserve the this operator for
getting the form element.

function Watcher() {
/* Define data here as local variables. */
var test = 'Testing...';

function change() {
/* When this function is called as a result of the change
* event, the this operator will refer to the form control
* that triggered the event.
*/
alert('Changed (test=' + test + ')');
}

this.wire = function(form, element) {
document.forms[form].elements[element].onchange = change;
};
}

var obj = new Watcher();
obj.wire('form1', 'obj1');

[snip]

> function cWatcher(){
>
> this.mTest = "Testing";
> this.wire = cWatcher_Wire;
> this.changeFunc = cWatcher_Changed;
> }
>
>
> function cWatcher_Wire( inFormName,
> inElementName){
>
> document.forms[inFormName].elements[inElementName].onchange =
> this.changeFunc;
>
> }
>
> function cWatcher_Changed(){
>
> alert("Changed, test=" + this.mTest);
>
> }


Those two functions should be added via the prototype:

function cWatcher() {
this.mTest = 'Testing';
}
cWatcher.prototype.wire = function(form, element) {
document.forms[form].elements[element].onchange = this.changeFunc;
};
cWatcher.prototype.changeFunc = function() {
alert("Changed, test=" + this.mTest);
};

Obviously, that will still suffer from the original problems. It was just
a technical suggestion.

[snip]

> window.gWatcher = new cWatcher();
> window.gWatcher.wire("form1","obj1");


The 'window' isn't necessary.

var gWatcher = new cWatcher();
gWatcher.wire('form1', 'obj1');

The var keyword in this instance isn't either; I just think it's good form.

[snip]

Hope that helps,
Mike

--
Michael Winter
Replace ".invalid" with ".uk" to reply by e-mail.
 
Reply With Quote
 
 
 
 
Joakim Braun
Guest
Posts: n/a
 
      12-23-2004
"Michael Winter" <> skrev i meddelandet
newspsjfpb8fxx13kvk@atlantis...

<snip>

> Read the following slowly. I always find it difficult to word properly.


You're doing fine.

<snip>

> One way around this is to use a closure and reserve the this operator for
> getting the form element.
>
> function Watcher() {
> /* Define data here as local variables. */
> var test = 'Testing...';
>
> function change() {
> /* When this function is called as a result of the change
> * event, the this operator will refer to the form control
> * that triggered the event.
> */
> alert('Changed (test=' + test + ')');
> }
>
> this.wire = function(form, element) {
> document.forms[form].elements[element].onchange = change;
> };
> }
>
> var obj = new Watcher();
> obj.wire('form1', 'obj1');


OK, I think I get that, a bit vaguely. Couldn't figure out how to establish
the right "this" context. And this allows for having several of these things
around, each with its own "member variables", right?

<snip>

> Those two functions should be added via the prototype:
>
> function cWatcher() {
> this.mTest = 'Testing';
> }
> cWatcher.prototype.wire = function(form, element) {
> document.forms[form].elements[element].onchange = this.changeFunc;
> };
> cWatcher.prototype.changeFunc = function() {
> alert("Changed, test=" + this.mTest);
> };


<snip>

Why? (I mean, they got called anyway. Or didn't they? Is there any reason
why functions should be treated differently than variables?)

(I'll find that out, so don't answer if it's laborious to explain. I'm just
longing for C++...)

Joakim Braun



 
Reply With Quote
 
Michael Winter
Guest
Posts: n/a
 
      12-23-2004
On Thu, 23 Dec 2004 10:43:31 +0100, Joakim Braun
<> wrote:

> "Michael Winter" <> skrev i meddelandet
> newspsjfpb8fxx13kvk@atlantis...


[snip]

>> function Watcher() {
>> /* Define data here as local variables. */
>> var test = 'Testing...';
>>
>> function change() {
>> /* When this function is called as a result of the change
>> * event, the this operator will refer to the form control
>> * that triggered the event.
>> */
>> alert('Changed (test=' + test + ')');
>> }
>>
>> this.wire = function(form, element) {
>> document.forms[form].elements[element].onchange = change;
>> };
>> }
>>
>> var obj = new Watcher();
>> obj.wire('form1', 'obj1');

>
> OK, I think I get that, a bit vaguely.


It works on the principle that inner functions like change, and the
anonymous function expression assigned to this.wire, can access variables
in surrounding scopes. It avoids the problem where the this operator won't
refer to the Watcher object by providing direct access to the data.

A technical discussion of closures can be found in the FAQ notes
(<URL:http://www.jibbering.com/faq/faq_notes/closures.html>). Another
related text can be found on Douglas Crockford's website
(<URL:http://www.crockford.com/javascript/private.html>).

> Couldn't figure out how to establish the right "this" context.


Could you elaborate? Perhaps show what you've tried and explain what you
expected.

> And this allows for having several of these things around, each with its
> own "member variables", right?


Absolutely. Each time a Watcher object is created, any "private" data
(like test) will be unique to that object.

>> Those two functions should be added via the prototype:
>>
>> function cWatcher() {
>> this.mTest = 'Testing';
>> }
>> cWatcher.prototype.wire = function(form, element) {
>> document.forms[form].elements[element].onchange =
>> this.changeFunc;
>> };
>> cWatcher.prototype.changeFunc = function() {
>> alert("Changed, test=" + this.mTest);
>> };

>
> Why?


Why via the prototype? As I said, it's a technical correction; using the
prototype is the "proper" way to add methods to achieve the result you
were aiming form. Also, it means you aren't adding addition identifiers to
the global namespace.

> Is there any reason why functions should be treated differently than
> variables?)


I'm afraid I'm not sure I follow[1]. What special treatment do you see?

[snip]

Mike


[1] I've probably forgotten what I thought when I first read about this
stuff, so please forgive my current inability to relate.

--
Michael Winter
Replace ".invalid" with ".uk" to reply by e-mail.
 
Reply With Quote
 
Martin Honnen
Guest
Posts: n/a
 
      12-23-2004


Joakim Braun wrote:

> "Michael Winter" <> skrev i meddelandet
> newspsjfpb8fxx13kvk@atlantis...


>>Those two functions should be added via the prototype:
>>
>> function cWatcher() {
>> this.mTest = 'Testing';
>> }
>> cWatcher.prototype.wire = function(form, element) {
>> document.forms[form].elements[element].onchange = this.changeFunc;
>> };
>> cWatcher.prototype.changeFunc = function() {
>> alert("Changed, test=" + this.mTest);
>> };

>


> Why? (I mean, they got called anyway. Or didn't they? Is there any reason
> why functions should be treated differently than variables?)


It is a matter of style and efficiency, in a language like Java or C++
all instances created with
new Watcher()
would share the same methods but in JavaScript if you do
function Watcher () {
this.changeFunc = function () { ... }
}
then each time you create a
new Watcher()
a new function is created and assigned to the changeFunc property.
If you use
Watcher.prototype.changeFunc = function () { ... }
then all instances created with
new Watchwer()
share that single function object.


--

Martin Honnen
http://JavaScript.FAQTs.com/
 
Reply With Quote
 
Joakim Braun
Guest
Posts: n/a
 
      12-26-2004
"Martin Honnen" <> skrev i meddelandet
news:41cafa6c$0$29409$...
> Joakim Braun wrote:
>
> > "Michael Winter" <> skrev i meddelandet
> > newspsjfpb8fxx13kvk@atlantis...

>
> >>Those two functions should be added via the prototype:
> >>
> >> function cWatcher() {
> >> this.mTest = 'Testing';
> >> }
> >> cWatcher.prototype.wire = function(form, element) {
> >> document.forms[form].elements[element].onchange = this.changeFunc;
> >> };
> >> cWatcher.prototype.changeFunc = function() {
> >> alert("Changed, test=" + this.mTest);
> >> };

> >

>
> > Why? (I mean, they got called anyway. Or didn't they? Is there any

reason
> > why functions should be treated differently than variables?)

>
> It is a matter of style and efficiency, in a language like Java or C++
> all instances created with
> new Watcher()
> would share the same methods but in JavaScript if you do
> function Watcher () {
> this.changeFunc = function () { ... }
> }
> then each time you create a
> new Watcher()
> a new function is created and assigned to the changeFunc property.
> If you use
> Watcher.prototype.changeFunc = function () { ... }
> then all instances created with
> new Watchwer()
> share that single function object.


I see, thanks.

Joakim Braun



 
Reply With Quote
 
Joakim Braun
Guest
Posts: n/a
 
      12-26-2004
"Michael Winter" <> skrev i meddelandet
newspsjgtolgsx13kvk@atlantis...
<snip useful discussion>
> > Couldn't figure out how to establish the right "this" context.

>
> Could you elaborate? Perhaps show what you've tried and explain what you
> expected.


I was thinking in terms of implicit (and "invisible", to the programmer)
"this" pointers passed to C++ class member functions. (for instance, would
someElement.onchange=someFunction pass an implicit "this" to someFunction,
and how to change the "this" into some other object than the someElement)

<snip>

> >> Those two functions should be added via the prototype:

<snip>
> >> cWatcher.prototype.changeFunc = function() {
> >> alert("Changed, test=" + this.mTest);
> >> };

> >
> > Why?

>
> Why via the prototype? As I said, it's a technical correction; using the
> prototype is the "proper" way to add methods to achieve the result you
> were aiming form. Also, it means you aren't adding addition identifiers to
> the global namespace.
>
> > Is there any reason why functions should be treated differently than
> > variables?)

>
> I'm afraid I'm not sure I follow[1]. What special treatment do you see?


this.mSomething = "variable value" (or var something = "value", plus "inner
functions" that can access the something)
vs
cSomeObject.prototype.mSomething = function(){...}

But Martin's reply explained that.

Thanks again.

Joakim Braun



 
Reply With Quote
 
Michael Winter
Guest
Posts: n/a
 
      12-26-2004
On Sun, 26 Dec 2004 10:25:41 +0100, Joakim Braun
<> wrote:

[snip]

> I was thinking in terms of implicit (and "invisible", to the programmer)
> "this" pointers passed to C++ class member functions. (for instance,
> would someElement.onchange=someFunction pass an implicit "this" to
> someFunction, and how to change the "this" into some other object than
> the someElement)


Every function has an associated this value, but that value changes based
on how the function is called. When an event listener assigned like this:

someElement.onchange = someFunction

is called, the this value for someFunction would be someElement.

You can change what is used for the this value through the call method:

func.call(obj, arg, ...);

The function, func, would be called as if it were a method of the object,
obj, and is passed the remaining arguments. Unfortunately, the call method
was added as late as JScript 5.5 (though earlier to JavaScript), so you'll
need to emulate it for the majority of users with IE 5.5 or earlier:

if(Function.prototype && ('function' != Function.prototype.call)) {
Function.prototype.call = function(obj, arg) {
var prop = '__call', ret;
while('undefined' != typeof obj[prop]) {prop += prop;}
obj[prop] = this;
ret = obj[prop](arg);
delete obj[prop];
return ret;
};
}

Clearly, this only lets you pass one argument to the function, but that's
easily changed. There is one other complication, though. If obj is an
element reference, the use of the delete operator will cause an error in
IE. The only option here is to use a fixed temporary property name and
hope that there are no conflicts:

if(Function.prototype && ('function' != Function.prototype.call)) {
Function.prototype.call = function(obj, arg) {
var prop = '__call';
obj[prop] = this;
return obj[prop](arg);
};
}

[snip]

> this.mSomething = "variable value" (or var something = "value", plus
> "inner functions" that can access the something)
> vs
> cSomeObject.prototype.mSomething = function(){...}


Oh, I see. In my mind, that had nothing to do with variables which is
where to confusion arose.

[snip]

Mike


Seasons Greetings

--
Michael Winter
Replace ".invalid" with ".uk" to reply by e-mail.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
global event handler Rainer Hahnekamp Javascript 1 05-31-2007 03:39 PM
Event Handler that creates adds another event handler kaczmar2@gmail.com ASP .Net 1 02-22-2007 07:37 AM
FWSM/PIX and Dynamic PAT using global IP range vs. global interface vs. global IP Hoffa Cisco 1 10-25-2006 06:50 PM
FWSM/PIX and Dynamic PAT using global IP range vs. global interface vs. global IP Hoffa Cisco 0 10-25-2006 01:04 PM
Global event handler of new window being lost on location change Fred Basset Javascript 8 09-09-2003 08:42 AM



Advertisments