Alpine.js Build a Remaining Character Count Component 
Tags: javascript, alpinejs, alpine
Published: 2021-08-14 08:12:17

First things first lets include Alpine to our project by including it from `script` tag.
```javascript
<html>
  <head>
<script defer src="https://unpkg.com/alpinejs@3.2.3/dist/cdn.min.js"></script>
</head>
<body>
...
</body>
</html>
```
The script tag fetches alpinejs (in this case Version 3.2.3) from CDN. The `defer` attribute is important here because it tells the browser not to wait for the script, instead browser continue to process the HTML and build the DOM.

Next lets layout some basic markup for our character counter. We have a parent `div`, a `textarea` input with `maxlength` attribute set to 120 which will help us to enforce the limit (shout out to @marzelin for pointing that out in the comments) and a `p` tag that we will use to display remaining characters while user is typing.
```javascript
...
<div>
    <textarea maxlength="120"></textarea>
    <p></p>
</div>
```

To continue with Alpine we need to ensure Alpine can initialize a new component. We do this by using `x-data` directive. Inside `x-data` you can declare object of data that Alpine will track. Lets just leave it empty for now.
```javascript
<div x-data="">
    <textarea maxlength="120"></textarea>
    <p></p>
</div>
```

Now we can add property that will hold contents of `textarea` to `x-data` directive.
```javascript
<div x-data="{content: ''}">
    <textarea maxlength="120"></textarea>
    <p></p>
</div>
```

We need to hook the `content` property to `textarea`. We can achieve this by using `x-model` directive, which allows you to bind the `value` of an input element to Alpine data. `x-model` is two-way bound, meaning it both "sets" and "gets". In addition to changing data, if the data itself changes, the element will reflect the change.
I will also add `x-ref` directive which we'll letter use to get reference of `textarea` element.
```javascript
<div x-data="{content: ''}">
    <textarea x-model="content" x-ref="message"></textarea>
    <p></p>
</div>
```

All that is left now is to display the number of remaining characters, but first we need to add new property on our component to hold the limit which is the value of `maxlength`. To do that we can use `$refs` property which is used to retrieve DOM elements marked with `x-ref`. Think of `x-ref` and `$refs` as a replacement for APIs like `getElementById` and `querySelector`.
```javascript
<div x-data="{content: '', limit: $refs.message.maxLength}">
    <textarea maxlength="120" x-ref="message" x-model="content"></textarea>
    <p></p>
</div>
```

Now we can count how many characters the content has, and substract from our limit to get the remaining characters. Alpine has `x-text` directive that set text of an element to the result of given expression.
```javascript
<div x-data="{content: '', limit: $refs.message.maxLength}">
    <textarea maxlength="120" x-ref="message" x-model="content"></textarea>
    <p x-text="limit - content.length"></p>
</div>
``` 

Lets improve it a little bit with more information to our user
```javascript
<div x-data="{content: '', limit: $refs.message.maxLength}">
    <textarea maxlength="120" x-ref="message" x-model="content"></textarea>
    <p> You have 
       <span x-text="limit - content.length"></span>
      characters remaining
</p>
</div>
```