How to Build a Character Countdown Function in JavaScript
This is going to be a really fun exercise. I got the idea for what we're going to walk through in this guide and also in the next one from a student who's looking to add this to their application.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
  • Complete the Exercise
Video locked
This video is viewable to users with a Bottega Bootcamp license

What we're going to build out is a character countdown. So if you've ever seen tools such as Twitter and you saw how you were typing something into a form input and it was counting down on the screen. That is what we're going to build out and in the next guide, we're going to extend it and see how we can build out the ability to have multiple character countdowns on the same page and to follow best practices while doing it.

So all of the HTML we're going to need is already on the page here.

large

I have an input that has a class of message input and then we have this div that has our countdown and I wrapped up our message calendar inside of a span. So with all of that in place, we can start writing our code. The first thing I do is I'm going to set our variables so I'm gonna say const message input and so I'm going to use a query selector so I'll say document.querySelector and we just want a regular query selector, not all here, and then we'll pass in the value that we're looking for which in this case is the message input. And with that in place, we can also come in grab our message counter because we're going to need this in order to set its values so I'll go message counter and then counter just like this.

Now the last variable we're going to need is a max value. So this is going to be the max character count so for this case it's going to be 144. So say const max and by default, we'll just have it set to 144 at the beginning.

const msgInput = document.querySelector('.msgInput');
const msgCounter = document.querySelector('.msgCounter');
const max = 144;

So everything there is all the data that we're going to need.

Now when we have something that is a little bit more on the non-trivial side so we're not just trying to add a class or do anything like that we actually have to maintain a level of state here because we're not just looking to toggle something we're looking to keep track of something. So whenever we're going to do that I think it definitely pays off to think our way through the process that we're going to want to follow.

A couple of things that I think we are going to need is you need a way of ensuring that we have not hit the max value. So ensure character count. So we're going to create a function just for that and then I'm going to need an event listener that will watch for the keyUp. So if you think about events in Javascript what happens when the user is typing you have a few different events whenever you're working with an input field. So one of them is keyDown that's where someone presses the key and it goes down. But what we actually want is we want to know when the key is up because that's when they have finished typing. So if I type s here the key up event will trigger as soon as my finger comes off of the keyboard.

So that is all we're looking for here and we need to keep track of it. And so we're going to have an event listener that's going to do that and then we're going to as the user is typing. We're going to ensure that they have not gone past the character count. So let's create this character count function first. So I'm going to say const and then let's call this ensureContentLength you can call it where ever you like I think that is a good name for that.

And this is going to take in two arguments it's going to take in the content and it's going to take in whatever the max value is and I'll use an arrow function here. And so inside of this, I'm just going to have a basic conditional so I'll say if content.length is greater than our max. Then I want to return False and else I want to return true.

const ensureContentLength = (content, max) => {
    if (content.lenth > max) {
        return false;
    } else {
        return true;
    }
}

So that part is pretty basic all we're doing is we're saying I need a content and a max. And if the content length is greater than max then I want you to return false which means that we're no longer ensuring this character count so that is our validator.

So now with that in place let's come down here and add out event listener. Now we're going to take a different approach than we've taken before and it's because we need to actually keep track of the length. If I were to do something like this where it's a msgInput.addEventListener and then I listened for on key up this actually would not work in this example.

Because we need to keep track of the total length we don't just want to know about the event that's where this is very different than say a click. We actually want to know and keep track of everything. So I'm gonna say message input dot on keyUp and then set this equal to a function declaration. Now do not use an arrow function here and I'll show you why in a minute so just use a regular function declaration.

And now what we're going to do is we're going to grab that counter which is actually message counter, so message counter and then we'll say msgCounter.innerHTML because we're wanting to set this value right here. So say innerHTML equals our max and then minus this dot value dot link.

msgCounter.innerHTML = max -this.value.length

So let's save this and see if this is working, so come over here hit refresh. And now if I type in one character it counted down perfect type another one and this is working perfectly.

large

Now if you're wondering why you needed to use function right here. Let's take a look at that before we finish this exercise. So if I tried to go with an arrow expression and said something like that. Watch what happens if I try to console.log this so if I say console.log and this dot value and hit save if I come back and hit refresh let's see what this actually is. So it is undefined and we have an error it says uncaught type error cannot read property length of undefined.

large

So what this means is because we used an arrow function this changed the scoping of what this represents and so remember that the arrow function is not just a shorter way of writing a function it specifically is going to change how this is scoped and so, in this case, we actually want to go with the traditional just function declaration in order to have the proper scope of this defined.

And so if I change that back hit refresh now this is working perfectly and you can see it keeps track of the value it doesn't just give us the event.

large

And so this is why I didn't just want the event value I wanted to use this full expression just because I think it makes sense to structure it this way and you have access to all the data that you are looking for.

Now let me clear this console log out because we have one more feature that I think would be pretty cool to add in and that is I want to keep track because you may notice we haven't actually even used this function yet and it's because I want to keep track of the character counts and then I want to disable this if it goes over the limit. So if this goes up to 144 I want to disable the field so the user can even type anything else in. So I'm going to come back into our event listener here and say if and then I'm gonna start off with the with a bang and then say ensure content length and remember this takes in two arguments so this is going to take in this.value and then it's going to take in the max minus one.

if (!ensureContentLenth(this.value, (max -1)))

The reason for that is because if you don't do Max minus 1 remember that the values and everything you're going to end up in a situation where you have an off by one error. So I'm going to subtract one from it and then inside of here say msgInput.disabled equals true

msgInput.disabled = true;

So if all of this works let's change our max down here to something like 20. And so if all this is working as soon as I hit 20 characters then it should go and it should disable this field. So I hit refresh and don't let the 144 scary away that's simply hardcoded and it'll change as soon as I start typing so I type in one character. It's 19 and now 18 and so if I keep going as soon as we hit 0, look at that the field is now disabled and I can't type anything else in.

large

large

That is because this cool little ensure content length function here was checking for the value it was watching it at each key up event. And then as soon as we hit that max it simply said Okay it's time to disable this entire field.

So this is a really fun introduction to learning how to work with text input, how to have some more insight into the differences between arrow functions and traditional function declarations, and how we could build a cool character countdown feature.

Starter Code

<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset='UTF-8'>
  <title></title>
</head>
<body>
  <input type="text" class="msgInput">

  <div>
    Characters left <span class="msgCounter">144</span>
  </div>
</body>

<script>

</script>
</html>

Code

<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset='UTF-8'>
  <title></title>
</head>
<body>
  <input type="text" class="msgInput">

  <div>
    Characters left <span class="msgCounter"></span>
  </div>
</body>

<script>
  const getCurrentContentLength = (content, max) => {
    const maxLength = max;
    if (content.length > maxLength) {
      return false;
    } else {
      return true;
    }
  }
  const msgInput = document.querySelector('.msgInput');
  const counter = document.querySelector('.msgCounter');
  const max = 20;
  // Nope
  // msgInput.addEventListener('keyup', (e) => {
  //   console.log(e);
  // });
  msgInput.onkeyup = function() {
    counter.innerHTML = max - this.value.length;
    if(!getCurrentContentLength(this.value, (max - 1))) {
      msgInput.disabled = true;
    }
  }
</script>
</html>