Steven Matthews
Steven Matthews

Reputation: 11285

Elegant way to count the number of characters in a textarea/input box

So I want to tell a user how many characters they have typed, dynamically. I'm seeing several examples online of using "Onchange" or "OnKeyUp", but they're both old examples and they seem sorta kludgy.

Is there a better way? Perhaps some method of binding/assigning (may not be using this term correctly) the value to a variable, and then checking every time a change has been made?

Upvotes: 3

Views: 17719

Answers (5)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76405

When you're talking about dynamically showing how many characters have been typed, you'll need events, like onchange or onkeyup... there's really no other way.

What's more: "assigning the value to a variable and checking each time a change was made" how else but through onchange will you know what you need to check and when?

a quick, easy way of doing this:

document.getElementById('aninputId').onchange= function(e)
{
    console.log(this.value.length);//or alert(); do with this value what you want
};

An important thing to keep in mind is that, if you're dealing with a lot of elements, you might want to consider delegating the event. here you can find a similar (not duplicate) question with 2 answers that deal with event delegation. Google it to find out more about this powerful feature!


UPDATE (with fiddle)

Ok, I set up this quick fiddle as an example of using event delegation. I used onchange, so the length won't show up until the focus is lost, there's a bit of comment to tell you what the code does, but it's written rather hastily. If you have any questions regarding this example, let me know.

PS: if you want the length to change in real time, just replace

mainDiv.addEventListener('onchange',showLength,false);

with

mainDiv.addEventListener('keyup',showLength,false);

Nothing more to it!


Update2

In response to your last comment: No, that code won't work. The alert(e); line will attempt to alert the event object, so you'll only see '[object object]', if not undefined on IE.

To help you understand what the code from my jsfiddle does, I'll break it down and go through it, step by step:

In my example, the input fields (there's only 1 but there could have been 100's) are all children of a main div element #allInputs. So first, we get a reference to that element:

var mainDiv = document.getElementById('allInputs');//Here's where we'll listen for events

Then, we want an event listener attached to that div. Due to event bubbling in IE, and event capturing in all other browsers, all events that are triggered within this element will pass by the div. Here are some diagrams that might clarify what I mean by that. This is the crucial concept of event delegation. Once you get this, the rest is easy.

Now, back to listening in on events: addEventListener does what it says on the tin: it's attached to an element and listens to the events that are fired/dispatched within that element. If any of those events is of the specified type, it calls a function. This function is what is referred to as a callback, or -in this case- the event handler. As always IE doesn't work like all other browsers, so first we check to see if the addEventHandler method is available to us.

if (!(mainDiv.addEventListener))
{//if not: this is the IE way
    mainDiv.attachEvent('onfocusout',showLength);
}
else
{
    mainDiv.addEventListener('change',showLength,false);
}

In both cases, the first argument is a string, stating the event type we want to listen to, In most cases addEventListener drops the on from the event type's name. IE, again being stubborn doesn't support the onchange event to be delegated (it doesn't bubble), we get around this issue by listening to another event: IE's own onfocusout event (if an input element looses focus, it has most likely been used for input, and has a new value. That makes it the right moment to check the value length.
addEventListener has a third parameter, to capture the event, this is irrelevant in this case, but on quirksmode (the link above), you can read more on this topic, and I recommend you do.

function showLength(e)
{
    e = e || window.event;
    var target = e.target || e.srcElement;//ie== srcElement, good browsers: target
    if (target.tagName.toLowerCase() === 'input' && target.type === 'text')
    {
        document.getElementById(target.id + 'Span').innerHTML = target.value.length;
    }
}

Now for the actual callback. Declare this function, like you would any other function, and specify 1 argument, in this case e, or event - doesn't matter what you call it. (multiple arguments are possible, but then we have to get into closures, I'll try and stick to the basics here).
The event object will be passed to the handler function when it is called by most browsers, guess which browser doesn't pass this object? Yes, our old pal IE, but that's an easy fix: first line of the function e = e || window.event; can be written as:

if (e === undefined)
{
    e = window.event;
}

Why? because our dear friend Internet Explorer doesn't pass the event object to the handler, but rather keeps it in the global object, which we can access via window.event.

Next thing we need, is to figure out on which element the event was triggered. Suppose you have a form, with 20 input fields, 10 radio buttons and 16 select elements, we're detecting the event at the level of the div#allInputs, we only know that the event was set off within this element. Luckily the event object has a property that holds a reference to the element we need:

var target = e.target || e.srcElement;

Again, IE is different. If you've read (and remembered) what was on the quirksmode page: IE events are triggered first at element level, and then bubble their way upward in the DOM, while all W3C browsers dispatch the event at the top level, and passes it down to the actual element. This explains the different names of what is, essentially, the same property: W3C events are dispatched, and then seek out their target element, IE events are dispatched at the element, and bubble up from their source. --I'm drifting off.

What do we know at this stage? Everything we need to know, really. We know what event was fired (e.type will tell us that - in this case either change or focusout). We what element has changed/lost focus, and have a reference to that element in a variable target, so we can get at it's value (target.value), it's id (target.id) and and, more importantly: we can check if we want to do anything further:

if (target.tagName.toLowerCase() === 'input' && target.type === 'text')

Remember that I talked about a situation where you're dealing with a form of 30+ elements, not all text inputs? If a new option is selected in a select element, we don't want to show the length of the select's value property, do we? So we check to see if the target is an input tag.
This can still be any input element imaginable, so let's also check if its type, only if target is a text input will we display the length:

document.getElementById(target.id + 'Span').innerHTML = target.value.length;
//or, if you want:
alert(target.value.length);

What are the advantages of this over direct binding of events? Again: if you're binding change events to several elements individually, your users WILL notice. As an added bonus: the change events of selects, text inputs, radio's,... all can be handled by 1 function. All it takes is a switch statement. Instead of:

if (target.tagName.toLowerCase() === 'input' && target.type === 'text')
{
    document.getElementById(target.id + 'Span').innerHTML = target.value.length;
}

Try this:

if (target.tagName.toLowerCase() === 'select')
{
    alert('You\'ve selected option number: ' + (target.selectedIndex + 1));
}
if (target.tagName.toLowerCase() !== 'input')
{//suppose I only want to deal with input and select elements
    return;
}
switch(target.type)
{
    case 'text':
        alert(target.value.length);//alerts length of input value
        return;//this event is done, return
    case 'radio':
        alert('The radio button is ' + (target.checked ? '' : 'un') + 'checked');
        return;
    case 'password': 
        if (target.value.length < 8)
        {
            alert('The password is too short, add '+(8 - target.value.length)+'chars');
            return;
        }
        var verifyPass = 'passIn_' + (target.id.match(/_1$/) ? '2' : '1');
        verifyPass = document.getElementById(verifyPass);
        if (verifyPass.value.length > 0 && target.value.length > 0 && verifyPass.value !== target.value)
        {
            alert('password confirmation failed');
        }
}
return;

As you can see, a ton of events can be dealt with in a single function, binding 1 event listener. If you bind directly you'd need to bind a ton of handlers, probably need to write several functions, which can be harder to maintain and even worse: makes your site sluggish for the user.


Ok, I'm done now and ready for bed. I hope this clears things out a little for you. Read the page on quirksmode, google a tutorial on event delegation if this dribble wasn't very clear to you, but I hope I did provide you with adequate explanation of the code I provided, and gave you some insight as to why event delegation is the right way of doing things, and why -behind the screens- most libraries work this way, too.
If you're really hesitant to include code that you can't understand, you might find it useful to know that jQuery delegates nearly all events, rather the binding them directly.

Upvotes: 6

Nishu Tayal
Nishu Tayal

Reputation: 20830

There are various jquery plugins which provides this feature and various configurations
You can have a look on :

http://youhack.me/2010/04/22/live-character-count-with-progress-bar-using-jquery/

http://qwertypants.me/counter/

Hope, it'll help you.

Upvotes: 0

Xenione
Xenione

Reputation: 2213

with jquery

$('input[name='the name of the input']').bind('keyup',function(){
 $('#show').val($(this).length)
});

Upvotes: 0

Justin Schier
Justin Schier

Reputation: 524

If you're using jQuery, there is a plugin that does this. I have used it in production and it works great.

http://cssglobe.com/post/7161/jquery-plugin-simplest-twitterlike-dynamic-character-count-for-textareas

Upvotes: 0

Mohamed Khamis
Mohamed Khamis

Reputation: 8029

You need a listener to detect added/removed characters, and accordingly change the counter. So you have to either use a key listener, or use onchange

here's an example: Twitter-like Textbox Character Count with inline alert

Upvotes: 1

Related Questions