First things first lets include Alpine to our project by including it from script
tag.
<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.
...
<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.
<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.
<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.
<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
.
<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.
<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
<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>
Top comments (2)
It would be nice if you first show how to add alpine.js to the document:
How would you enforce this limit?
Thanks for pointing that out.
One way to inforce the limit is by using HTML's maxLength attribute. I will update my post to cover that.