Referencing function parameters

I have this JS function:

var maxAmount = 250;
function textCounter(textField, showCountField) {
    if (textField.value.length > maxAmount) {
        textField.value = textField.value.substring(0, maxAmount);
} else { 
        showCountField.value = maxAmount - textField.value.length;
}
}

I also have a form in which I need ti use the fuction:

<body>
<form>
<textarea name="ta" rows="6" style="width:340px;" onKeyDown="textCounter(???????,???????);" onKeyUp="textCounter(???????,???????);"></textarea>
<br>
<input readonly id="ta2"type="text" name="countDisplay" size="3" maxlength="3" value="250"> Characters Remaining
</form>
</body>

How do I reference my the parameters of my function from the form?

The two values of the function are textField and showCountField

For the first parameter, the this keyword is going to be the textarea field.
For the second parameter, you could use this.form.elements.countDisplay to refer to the display field.

or…

<form>
<textarea name="ta" rows="6" style="width:340px;" onKeyDown="textCounter(this,countDisplay);" onKeyUp="textCounter(this,countDisplay);"></textarea>
<br />
<input id="ta2" type="text" name="countDisplay" size="3" maxlength="3" value="250" readonly /> Characters Remaining
</form> 

(but Paul is way better at this voodoo than me!)

onKeyDown="textCounter(this,this.form.elements.countDisplay);" onKeyUp="textCounter(this,this.form.elements.countDisplay);"></textarea>

was unsuccessful. Did i misunderstand?

It works for me. Is there something else that’s causing the problem?
If you link us through to a test page, we can find out.

http://www.lincolnsrealdeals.com/input_length.html

Now the problem is clear as day. The code you claim to have used in post #4 is different from what you actually used.
Can you see the problem?

I see the difference. Sorry about that. I’ve been played with it too much. I made the change and it works.

I found that this also works:

onKeyDown="textCounter(this,document.getElementById('ta2'));" onKeyUp="textCounter(this,document.getElementById('ta2'));"

It makes sense to me though I had to give <input> an id.

I’m real fuzzy in the use of “this” and I’m unsure how you came-up with “this.form.elements”.

  1. I’ve already spoken to my fuzziness with “this”

  2. I believe the “form” comes from the HTML DOM Form Object. If so, that makes sense, but please confirm.

  3. I’m unclear where the “elements” came form the HTML DOM Form Object talks about “elements” if they’re the same why drop the “”?

My uncertainty about how to use “this” really bugs me because it seems important based on what I’ve read, but I haven’t found clear general ref for how to use it. any suggestions (other than asking too many questions)?

The this keyword is a reference to the context within which the function was executed.

All form fields contain a property called form, that refers to the form element that the field is contain within.

Using the array index notation could be used as well, but typically it’s not used in preference to object notation.
this.form.elements[‘username’] is the same as this.form.elements.username

But the same goes with any property reference:
this[‘form’][‘elements’][‘username’] is the same too, though less readable.

Typically the only reason to use the array notation is when you want to make it clear that actual indexed arrays are used, or when characters occur in the name (a dash for example) that aren’t usable as a property notation.

What do you think of the information at the page about the this keyword

regarding who owns the textCounter function, in my script I think its a method of the window object, because it hasn’t been copied to a different object, therefore a simple “this” is enough. Please confirm.

The textCounter function is not copied to the event handler, so the function will not have an appropriate this keyword reference to the element that triggered the event.

There are three different techniques that can be used to deal with that problem.

  1. Use traditional event assignment instead, so that the this keyword is available from within the function.
  2. Pass the this keyword separately to the function as an additional function parameter.
  3. Call the function using the this keyword to assign the context from which the function will be called.

Traditional Event Assignment

When using this first technique, the HTML code for the form field is much simpler, where a unique identifier is placed on the form to allow scripting to access the form elements:


<form [color="green"]id="countedText"[/color]>
    <textarea name="ta" rows="6" style="width:340px;"
        [s][color="red"]onKeyDown="textCounter(this, this.form.elements.countDisplay);"
        onKeyUp="textCounter(this, this.form.elements.countDisplay);"[/color][/s]>
</textarea>
</form>

so that scripting is used to perform the event assignment instead.
There are a couple of ways to handle things here.

One way, the way that I typically prefer, is to store a reference to the display area as a property directly on the textarea field itself:


function countDisplay() {
    var textField = this,
    showCountField = textField.countDisplay;
    ...
}
var form = document.getElementById('countedText'),
    textarea = form.elements.ta;

textarea.countDisplay = form.elements.countDisplay;
textarea.onkeydown = textCounter;
textarea.onkeyup = textCounter;

Another way is to pass the count display as a property to the function, but then you cannot use a direct assignment of the function to the event, you have you use an anonymous function so that you can invoke the countDisplay function with the appropriate argument, and pass the this context along to the function as well:


function countDisplay(countDisplay) {
    var textField = this;
    ...
}
var form = document.getElementById('countedText'),
    textarea = form.elements.ta,
    countDisplay = form.elements.countDisplay;
textarea.onkeydown = function () {
    textCounter.call(this, countDisplay);
};
textarea.onkeyup = function () {
    textCounter.call(this, countDisplay);
};

It can be preferred though to combine those identical functions in to a single event handler function instead:


function countDisplay(countDisplay) {
    var textField = this;
    ...
}
function textareaHandler(countDisplay) {
    return function () {
        textCounter.call(this, countDisplay);
    };
}
var form = document.getElementById('countedText'),
    textarea = form.elements.ta,
    countDisplay = form.elements.countDisplay;
textarea.onkeydown = textareaHandler(countDisplay);
textarea.onkeyup = textareaHandler(countDisplay);

That gets a bit complicated though, and it why I prefer to use the first example where the field property to store the reference. It’s a good technique that only needs to be moved away from if you’re concerned about other code over-writing the property.

Inline Event Attribute

You are currently using the second technique, which forces you to use an extra parameter in your function to receive a reference to the element. Examples of which are in the previous posts above.

Pass the Inline Context to the Function

This third technique is where the call method is used to call the function with an appropriate context, simulating that which scripting automatically does in the first technique:


<textarea name="ta" rows="6" style="width:340px;"
    onKeyDown="textCounter[color="blue"].call[/color](this, this.form.elements.countDisplay);"
    onKeyUp="textCounter[color="blue"].call[/color](this, this.form.elements.countDisplay);"[/color][/s]>
</textarea>

where the function still has only the one parameter:


function countDisplay(countDisplay) {
    var textField = this;
    ...
}

This technique is disliked though, due to it being another inline event attribute technique, along with .call() to help confuse things.

On second thought, “this” must be referring to the <textarea> because that’s where the input is coming from. without the <textarea> input there’s nothing for the function to do. Right now for the right reason?

Thanks for all the work you put into your last post.

At this point, I need to confirm an understanding that I’ve formed based on this topic. Please confirm that the this keyword refers to the owner of of the input which in this case is HTML DOM Textarea Object.

Also, would it be safe to say, that to establish ownership, I need to establish the source of an input as in source = owner?

The this keyword refers to the execution context. With your current code, the this keyword refers to the element on which the event is triggered.

I don’t follow what you mean there. Can you provide a small example of what you mean there?

“the this keyword refers to the element on which the event is triggered.”

which is the HTML DOM Textarea Object, right?

Javascript - The this keyword talks the owner of the function. Since th onkeyup/down is being called from <textarea> the Textarea Object owns that function therefore this refers to the <textarea> input. Correct?

When using an inline event attribute, here is what happens.


<textarea onkeyup="doSomething();">

The following anonymous function gets assigned to the onkeyup event:


function () {
    doSomething();
}

So it is only inside of that function that the this keyword refers to the textarea object. When the doSomething function is called, inside of that doSomething function the this keyword refers to the default global context, that being the window object.

It doesn’t own the doSomething() function from the above example. It only owns the outer function that is assigned to the event, that being the anonymous function.

That’s why you can pass the this keyword as a function parameter.

Once you move on from using horrible inline event attributes to using traditional event associations instead, the problem of dealing with the this keyword is largely reduced.

How would you change this script to Traditional Event Assignment. I think you were hinting around in post #11, but I wasn’t catching on (obviously)

<html>
<head>
<script language="javascript" type="text/javascript"> 
var maxAmount = 250;
function textCounter(textField, showCountField) {
    if (textField.value.length > maxAmount) {
        textField.value = textField.value.substring(0, maxAmount);
} else { 
        showCountField.value = maxAmount - textField.value.length;
}
}
</script>
</head>
<body>
<form>
<textarea name="ta" rows="6" style="width:340px;" onKeyDown="textCounter(this,this.form.elements.countDisplay);" onKeyUp="textCounter(this,this.form.elements.countDisplay);"></textarea>
<br>
<input readonly id="ta2" type="text" name="countDisplay" size="3" maxlength="3" value="250"> Characters Remaining
</form>
</body>
</html>

.

When using traditional event assignment, scripting is used to associate a function to the event of an element.

With the scripting currently in the head, at the time that the script runs, none of the web page yet exists, so the head script cannot work with elements on the page until later on once they exist. It’s possible to keep the script in the head and use some kind of onload event to delay the running of your code until the page exists, but that’s introducing more complexity that you can do without.

The solution to all of this is to not put your script in the head. A technique to speed up the loading of your web page is to put your script at the bottom, just before the </body> tag.

You can move your script from the head to the end of the body \without affecting how your code works, and it opens up the benefit of being able to easily work with elements on the page directly from your script without having to wait until some later point in time.

Once you’ve done that, you can gain a reference to the form, and from there to the textarea element, so that you can assign a function to certain events.

Please be more specific . I’ve moved the <script> tag to the end and need to be clearer about what’s intended.

To be more specific, here is a copy/paste from post #11