Array of Text Box; knowing which one caused event

M

mflll

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 writing the JavaScript doesn't have to
pass the index
in the "onChange" event name.


I thought that one might be able to use "this.value" or compare this as
indicated
in the second program, but it did not work.

Dr. Leff (e-mail address removed), ASsociate Professor of Computer Science,
Western Illinois
University, Macomb IL 61455 (309 367 0787 pager)


<HTML>
<HEAD>
<script Language="JavaScript">
document.write("array");
function Update(i) {
var aText = document.TF.Q.value;
document.TG.R.value = aText;
}


</script>
</HEAD>
<BODY>
<FORM name="TF">
<BR><INPUT TYPE=TEXT name="Q" onChange="Update(0)">
<BR><INPUT TYPE=TEXT name="Q" onChange="Update(1)">
<BR><INPUT TYPE=TEXT name="Q" onChange="Update(2)">
</FORM>
<FORM name="TG">
<INPUT TYPE=TEXT name="R" value="R1" >
<INPUT TYPE=TEXT name="R" value="R2">
<INPUT TYPE=TEXT name="R" value="R3">
</BODY></HTML>

This does NOT WORK:

<HTML>
<HEAD>
<script Language="JavaScript">
document.write("array 1");
function Update() {
var L = document.TF.Q.length;
alert("length is " + L+ " "+this.value);
for (i=0;i<L;i = i + 1) {

alert (" i is "+ i + "Q.value " + Q.value);
if (this.value == Q.value){
WhichOne=i;
}
}
alert ("whichONe is "+WhichOne);
var aText = document.TF.Q[WhichOne].value;
document.TG.R[WhichOne].value = aText;
}

The first alert shows an "undefined" for "this.value" I also tried
comparing
"this" to "Q" directly with no good result.

I teach GUI with Java and have taught it using Microsoft API and MFC as
well
as X-Windows. These GUI all provide several ways to have one event
handler
for an array of controls and that event handler finding the control
that fired it.
 
R

Randy Webb

(e-mail address removed) said the following on 1/17/2006 5:27 PM:
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 writing the JavaScript doesn't have to
pass the index
in the "onChange" event name.

pass 'this' to the function and it will point at the element that
triggered it.

From there, you can get is name (if it has one), its ID (if it has
one), its value etc..
 
R

Richard Cornford

I thought that one might be able to use "this.value" or
compare this as indicated in the second program, but it
did not work.
<FORM name="TF">
<BR><INPUT TYPE=TEXT name="Q" onChange="Update(0)">
<snip>

The function that you have given the name 'Update' is not an event
handler, it is a global function called from an event handler, and so
when it is executed the - this - keyword is a reference to the global
object, which does not have a - value - property.

The string value of an HTML event handling attribute is used as the
function body definition for a function object that the browser creates
internally and assigns to the - onchange - property of the corresponding
form control element. So the HTML attribute - onChange="Update(0)" - is
equivalent to doing:-

document.forms['TF'].elements['Q'].onchange = function(event){
Update(0)
};

- in most browsers, and:-

document.forms['TF'].elements['Q'].onchange = function(){
Update(0)
};

- in IE browsers and close imitators. (The difference being the event
handler function's formal parameter; 'event' in most browsers and
non-existent in IE browser. this reflects divergent handling of the
event objects, which IE makes available as a global variable and other
browsers pass as an argument to the event handler).

In javascript the value of the - this - keyword within a function is
determined (only) by how the function is called, and browsers
consistently call event handlers created from event handling HTML
attributes as methods of their associated elements, the equivalent of:-

document.forms['TF'].elements['Q'].onchange(); //or passing the event
//as an argument

This means that for the execution of the event handler the - this -
keyword is a reference to the form control element. However, the
internally generated event handling function calls your - Update -
function as a global function and so during its execution - this -
defaults to a reference to the global object.

If you want the function called from the event handler to have a
reference to the form control element on which the event was triggered
you may pass it that reference when the global function is called, and
you may get that reference with the - this - keyword in the event
handler. E.G:-

onChange="Update(this);"

- with the global function defined along the lines of:-

function Update(fieldRef){
...
}

- where the global function's - fieldRef - formal parameter receives the
reference to the form control element on which the event was triggered,
as a result of it having been passed as an argument in the -
Update(this); - function call.

Richard.
 
M

mflll

Thank you for the informative responses from Messrs. Cornford and Webb.
Using this information, I was able to use "Update(this)" and pick up
the
ID, etc. on the textbook in which the user entered text and left focus.
(See example below.)

However, is there any way we can avoid giving a unique ID or Value in
the HTML INPUT element.

We pass the textbox on which activity occurred to the procedure, by
writing onClick="Update(this)" And since all three text boxes are
named
Q, they form an array. Thus, I was hoping to compare the pointer
this with each successive element of TF.Q. I do this when I have
an array of buttons or other controls in Java.




<HTML>
<HEAD>
<script Language="JavaScript">
document.write("array");
function Update(i) {
alert (i.value+ "id " + i.id + "i.tabindex " + i.tabindex)
for (k=0;k<i.length;k++) {
if (i== TF.Q[k]){
alert ("equal ")
}
}
}


</script>
</HEAD>
<BODY>
<FORM name="TF">
<BR><INPUT TYPE=TEXT id="1" name="Q" onChange="Update(this)">
<BR><INPUT TYPE=TEXT id="2" name="Q" onChange="Update(this)">
<BR><INPUT TYPE=TEXT id="3" name="Q" onChange="Update(this)">
</FORM>
<FORM name="TG">
<INPUT TYPE=TEXT name="R" >
<INPUT TYPE=TEXT name="R" >
<INPUT TYPE=TEXT name="R" >
</BODY></HTML>
 
R

Richard Cornford

Thank you for the informative responses from Messrs.
Cornford and Webb. Using this information,

Having posted my last response I considered following it up with some
comment on the code you were actually using. A shortage of available
time prevented me from doing so, but I suspect that if I had you would
not be asking this question now.
I was able to use "Update(this)" and pick up the
ID, etc. on the textbook in which the user entered
text and left focus. (See example below.)

Yes, you pass a reference to the specific form control as an argument to
the function call.
However, is there any way we can avoid giving a unique
ID or Value in the HTML INPUT element.

In HTML an ID attribute must be unique on any document. Names do not
have that same restriction (and for radio buttons to work together they
must have the same name attribute). It is not necessary to give any
element an ID, but form controls will not be 'successful' (their values
sent to the server upon submission) unless they have a name attribute.
We pass the textbox on which activity occurred to the
procedure, by writing onClick="Update(this)" And since
all three text boxes are named Q, they form an array.

Strictly it is a collection (so not a javascript Array, though a
collection is an array in the broadest sense of the word).
Thus, I was hoping to compare the pointer

Javascript doesn't have pointers as such. What we have is values that
refer to objects, how they refer is not specified (or important) and it
is normal (and sufficient) to refer to such values as references.
this with each successive element of TF.Q.

There you have a problem as - TF.Q - is not an ideal method of accessing
the collection. It assumes that TF is effectively a global variable that
has a value that refers to the form element. This is an IE proprietary
feature that is (more or less) reproduced in some other browsers, but
not all. The W3C HTML DOM specification defines a document.forms
collection as a convenience method for acquiring references to forms (by
name, ID and integer index) as a formalisation of a pre-existing feature
of browser object models. As a result it is standard and back-compatible
with all scriptable web browsers that expose forms for scripting.

Accessing a form with the name 'TF' would require:-

document.forms['TF']
-or:-
document.forms.TF

Each form element has (by W3C specification and again as a formalisation
of a pre-existing common feature) a property called - elements -, which
is a collection of all controls within the form. Form controls may be
accessed through the - elements - collection by integer index or by
name/ID, and when numerous controls share the same name the elements
collection returns a reference to another collection that is a
collection of all the like-named controls.

Your 'Q' control collection would be accessed as:-

document.forms['TF'].elemtns['Q']

(and/or dot notation variants)

However, all form controls have a property called 'form' that is a
reference to the form element that contains them, so as you are passing
a control to the function the easiest method of getting a reference to
the containing form (and so its - elements - collection) is through that
property:-

formControl.form.elements['Q']

This reference to the form element is anonymous, the form itself does
not need to have a name or an ID.

Also, as you want to acquire the collection of like-named controls to
which the control for which you have passed a reference belongs you can
anonymously look-up that collection as:-

formControl.form.elements[formControl.name]

(As the existence of a name attribute is required to create a like-named
collection of elements in the first place)
I do this when I have
an array of buttons or other controls in Java.

And you can do it successfully in javascript as well, as objects all
have unique identity that can be verified with either type-converting
comparison (==) or strict comparison (===) operations.
<HTML>
<HEAD>

Formally valid HTML is require to contain a TITLE element (it is
non-optional in an HTML document). It is important when (non-trivially)
scripting HTML DOMs that the DOM structure that the browser creates is
consistent (else the effort to produce cross-browser code is multiplied
many fold) and different browsers only create structurally consistent
DOMs when they are presented with structurally correct HTML mark-up. And
the best way of confirming that mark-up is structurally correct is to
validate it, for which it need to be formally valid in order to pass.
(Formal validity is a stricter requirement than structural correctness,
only structural correctness is necessary in order to avoid browsers
creating structurally inconstant DOMs.)
<script Language="JavaScript">

In formally valid HTML the SCRIPT element is required to have a TYPE
attribute, and if the TYPE attribute is present the (deprecated and
optional) LANGUAGE attribute is superfluous.
document.write("array");
function Update(i) {

Javascript has no notion of classes, but the concept of classes is often
used in script architecture. Because there is no syntax to define a
'class' the structures that are used to implement the concept of a
'class' are not inherently distinct. To mitigate this various naming
conventions are commonly used, and one (probably the most common) is to
give the primary Identifiers of structures implementing the 'class'
concept (usually a function that is to act as the class constructor) an
initial capital character, while using initial lower case characters for
all non-constructor and method functions. As this naming convention is
widely used (commercially), it is a reasonable idea to adopt it from the
outset.

The formal parameter of this function has been given the identifier -
i -, which says nothing about what the argument is. The argument is a
form control, possibly a specific type of form control, and it should be
identified as such by its identifier. This avoids confusion as to the
nature of the object referred to by that identifier.
alert (i.value+ "id " + i.id + "i.tabindex " + i.tabindex)

The 'i' in the property name 'tabIndex' is uppercase.
for (k=0;k<i.length;k++) {

The Identifier - k - has not been declared in this script (globally or
locally). It is good practice to explicitly declare all variables, local
or global, and doing so can avoid undesirable consequences of naming
collisions with the IE feature that makes IDed and named elements
accessible as global variables.

However, the general programming axiom that a variable should never be
given more scope that it absolute needs is as true in javascript as any
other language, and - K - does not need to exist in any scope outside of
this function. So it should be declared as a function local variable,
with the use of the - var - keyword. (it is particularly ill-advised to
use global variables as loop counters)

In using - i.length - you have suffered form using - i - as the
identifier for the form control. If you had used, for example, -
formControl - it may have been more obvious that the object referred to
by the identifier was singular and would not necessarily have a -
length - property, or that any - length - property it may have would be
unrelated to any colleciton of like named elements.

This is your primary problem this the code as it guarantees that the
code in the loop will never execute; - i.length - evaluates as the
value - undefined - and the comparison operator type-converts undefined
to the number NaN, and NaN always returns false from any comparison
operation for which it is an operand, the loop body is never executed.
if (i== TF.Q[k]){

Apart from the IE style reference to the form element, that comparison
would work, if the body of the loop was ever executed.
alert ("equal ")
}
}
}

</script>
</HEAD>
<BODY>
<FORM name="TF">

In valid HTML the opening FORM tag is required to include an ACTION
attribute.

Your modified function may resemble:-

function update(formControl){
var k, controlCollection;
alert(formControl.value +
"id " + formControl.id +
"formControl.tabIndex " + formControl.tabIndex
);
if(
(controlCollection = formControl.form.elements[formControl.name])&&
(controlCollection != formControl) // In case there is only one
// control with this name.
){
for(k = 0;k < controlCollection.length;++k){
if(formControl == controlCollection[k]){
alert("equal ");
}
}
}
}

Richard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,818
Latest member
SapanaCarpetStudio

Latest Threads

Top