473,406 Members | 2,343 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,406 software developers and data experts.

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

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>
Jul 23 '05 #1
7 2253
On Wed, 22 Dec 2004 22:51:27 +0100, Joakim Braun
<jo**********@jfbraun.removethis.com> 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.
Jul 23 '05 #2
"Michael Winter" <M.******@blueyonder.co.invalid> skrev i meddelandet
news:opsjfpb8fxx13kvk@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

Jul 23 '05 #3
On Thu, 23 Dec 2004 10:43:31 +0100, Joakim Braun
<jo**********@jfbraun.removethis.com> wrote:
"Michael Winter" <M.******@blueyonder.co.invalid> skrev i meddelandet
news:opsjfpb8fxx13kvk@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.
Jul 23 '05 #4


Joakim Braun wrote:
"Michael Winter" <M.******@blueyonder.co.invalid> skrev i meddelandet
news:opsjfpb8fxx13kvk@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/
Jul 23 '05 #5
"Martin Honnen" <ma*******@yahoo.de> skrev i meddelandet
news:41***********************@newsread4.arcor-online.net...
Joakim Braun wrote:
"Michael Winter" <M.******@blueyonder.co.invalid> skrev i meddelandet
news:opsjfpb8fxx13kvk@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

Jul 23 '05 #6
"Michael Winter" <M.******@blueyonder.co.invalid> skrev i meddelandet
news:opsjgtolgsx13kvk@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

Jul 23 '05 #7
On Sun, 26 Dec 2004 10:25:41 +0100, Joakim Braun
<jo**********@jfbraun.removethis.com> 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.
Jul 23 '05 #8

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

Similar topics

7
by: Pavils Jurjans | last post by:
Hallo, I have been programming for restricted environments where Internet Explorer is a standard, so I haven't stumbled upon this problem until now, when I need to write a DOM-compatible code. ...
10
by: tony kulik | last post by:
This code works fine in ie and opera but not at all in Mozilla. Anybody got a clue as to how to get it right? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <script...
6
by: Amir Hardon | last post by:
I am dynamically adding rows to a table, and each row have a button which removes it. I have successfully implemented this for mozilla but I'm having troubles with IE, here is how I did it: ...
2
by: Breeto | last post by:
Can anyone please tell me why the following doesn't work... using System; using System.Web; namespace AspTests {
10
by: Sean Dockery | last post by:
I have the following HTML file that I've been using for testing... <html> <head> <script type="text/javascript"> <!-- function handleWindowLoad() { var items = ; for (var i = 0; i < 11; i++)...
4
by: mflll | last post by:
I am looking into the different techniques of handling arrays of edit boxes in Java Script. The first program below works fine. However, are there better ways of doing this, where the person...
5
by: james | last post by:
Hello, I am having a little trouble creating an event handler for a context menu toolstripmenuitem. I've seen various tutorials and so on, but I keep getting a bit stuck! So far I have a second...
2
by: Robert | last post by:
Hello javascript group readers, I have a question regarding how to prevent memory leaks in Internet Explorer when using closures. I already knew about the circular reference problem, and until...
2
by: Ralph | last post by:
Hi I don't understand why it's not working: function schedule(imTop){ this.tdImagesTop = imTop; } schedule.prototype.selectEl = function() { alert(this.tdImagesTop);
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: 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
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
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
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
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...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

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.