Playing
01. Autosizing Textarea with Vue

Episodes

0%
Your progress
  • Total: 13m
  • Played: 0m
  • Remaining: 13m
Join or sign in to track your progress

Transcript

00:00
In this snippet, I'm going to show you how to create out a automatically resizing text area inside of Vue.
00:07
This might seem pretty simple. And it actually is to get this hooked up. But what we're going to do is take this step by step. So we're first of all going to look at the unideal solution
00:16
to this, and then finally end up with what you see here. So it's pretty straightforward. But let's dive straight into the code and start to figure out how we would get this working.
00:26
OK, so we're starting out here with a fresh Vue CLI built project. I've just cleared a couple of things out. But if you're integrating this into an existing project,
00:35
it's going to be super simple to do. OK, so let's just start out, of course, with a few text areas. So I'm just going to bring all of these attributes down, because we're going to be adding some event
00:45
hooks onto these in just a moment. So let's just pull all of this down here. And let's just give this a name. Let's just say body.
00:52
And let's give this an ID as well. It doesn't really matter too much what we're doing with this now. And now we need to figure out, well,
00:58
for all of the text areas we have on a page, so we could have two of these, we want to hook this in somehow. Now, I'm just going to do it by its tag name.
01:09
But of course, what you can do is do this by class. So for example, if you wanted a specific expanding text area, you could do something like text area expand. It's entirely up to you.
01:19
But this solution isn't going to be great. And I'll talk to you about why that is in just a minute. Now, what we want to do is somewhere in our application in this not ideal implementation
01:30
is have some kind of mounted hook. So when a component renders, we can start listening, or at least setting event listeners to our elements.
01:39
So we're going to start out by just using query selector all. And we're going to pass in, like I said, the tag name of these elements. Now, this will return to us a list of elements
01:49
that match that tag name. So if we just do a console log on this, we'll be able to iterate over this. So let's just pop over to the browser.
01:58
And we should see, if we just inspect this, bring up the console, we should see we've got a node list of two text areas. So we can iterate over each of them and add this event
02:08
listener. Remember, this isn't the ideal solution. Eventually, we're going to extract this out to a component.
02:13
So let's go ahead and just do a really simple for each loop on this. And for each of these elements that we get in, we're now going to set up some kind of event listener.
02:22
So when we hit a key, we're going to automatically resize this based on the scroll height of the element. Now, just before we do this, I'm going
02:30
to bring over some styles that I pre-created, just so we've got something that looks a little bit better. All we're doing is setting the width of each text area, adding a border, border radius.
02:40
And really importantly, we're setting box sizing to border box. And I'm going to comment this out just for now, because we're going to examine what
02:47
happens if we don't have box sizing set to border box and we have a padding set to our text areas. Most of the time, you will have a padding set to your text areas.
02:56
So this is really important. So let's go ahead and give these a class of text area really quickly. So we'll do that for each of these.
03:04
And let's give that a refresh. Great. So we've got this just in here now. So what we want to do then, let me just
03:10
go and get rid of the columns and rows, because they look a little bit too high. And if we come over, that's perfect. So now we can type.
03:18
And obviously, the natural way that this works is you end up with a scroll bar. So this is determined by the scroll height of the content inside of this.
03:27
And that's not what we want. It might be, but in this case, it's not. So now what we want to do is listen for a key down so we can constantly resize this based on that scroll height
03:38
that we've just spoken about. So I'm going to go ahead and with the element that we get in here, I'm going to add an event listener. So for every single element that we've picked out
03:46
using that querySelectAll method, we're now going to listen for a key down. And inside of here, we'll get a closure with the event details. So the event details contain lots of different things.
03:59
So just as an example, let's log out e.target, which is the target element that we're looking at. If we go ahead and keep our console open and type something in, we get the target just inside of here.
04:10
So we can pluck out attributes from this. We can set styles. We can do pretty much anything that we want to do. Now what we want to do is, first of all,
04:20
set the height of this to auto. Then what we want to do is resize it. And I'll speak about why that is in just a minute. So I'm going to say e.target.style.height.
04:30
And first of all, let's set this to the scroll height of the element. So the scroll height of the element, let's just pop this in here, is e.target,
04:37
so that element that we just saw, .scrollHeight, like so. Now when we are creating an event listener like this, what we actually need to do, it's a little bit hacky, and that's why I don't like this solution,
04:49
is we have to put a setInterval in here with a 0, just so it has time to catch up. So I'm going to go ahead and pop that in there. And of course, this isn't the ideal solution,
04:57
so we'll be getting rid of that in just a minute. So if I type a character in here now and I come down, notice that actually nothing happens. So let's go and check why that might be.
05:07
Of course, we need to add the unit just inside of there. So now when I go ahead and type here, notice that what happens is for some reason it creates this really, really long text area.
05:18
That's what I was talking about a moment ago when I said we need to set this to auto to start with. So let's go ahead and say e.target.style.height, and let's set this to auto to start with before we
05:31
go ahead and resize this. So let's just assign that properly and come over and give that a refresh. So now when I type, we shouldn't see that issue,
05:38
but you'll notice that we get this little resize when we type a single character. We don't really want that to happen, and we'll talk about why that is in just a minute.
05:46
So now as I come down, you'll notice that this does actually resize. It's not great at the moment, and the reason that this isn't working as we would expect at the moment
05:56
is purely because of that box sizing. If you're not aware of the box sizing property in CSS and the border box value for the box sizing property, essentially what this does is when you add padding
06:06
to an element, it will increase the height or width of this element based on the padding. If we set box sizing to border box, what that will do is it will not ignore the padding,
06:16
but it will use the actual height of it rather than taking into account the padding. You've probably used this before. So now when I've done that, notice
06:24
that just adding in that property makes these boxes actually a little bit smaller because we've already set a minimum height to these, so it's not interfering with that padding.
06:34
Now when I initially type, we don't see that flicker of it trying to expand, and that's just because it's not adding in the padding, which is, of course, 20 pixels.
06:43
But now when we come down, notice that this is working as we would expect, and it works for all of the text areas on the page as well. So this is all well and good, but there's a much better
06:53
solution, and that is to singly target a text area and take advantage of the input event hook directly on the element. And again, this isn't the perfect solution,
07:06
but in the next couple of minutes, we'll arrive at that perfect solution that we were talking about. So what we're going to do instead
07:13
is get rid of all of this. We can take these because they're going to be useful, but let's get rid of all of this inside of here, and we don't even need our mounted hook in here now.
07:21
What we can do is we can say for any text area, and of course, you'd have to apply this to each text area that you wanted this to work on, add your input event hook using view,
07:31
and then call a method passing in the event, which will, of course, contain the target and all of the other event information. So now what I can do is instead, I
07:40
can define out a method in here called resize, take in that event, and then I can literally just put in what we did before. So rather than iterate over all of the text
07:50
areas on the page, which isn't really efficient, we're specifically targeting this element here, and we're still getting through all of the event information. So now if we come over and give this a refresh,
08:01
you can see it works in exactly the way that we would expect. So it's still automatically resizing itself. Now with this, we just have a really, really simple app here with nothing much else going on.
08:14
But let's say that you did have a text area on a page. You wouldn't want to take this method and add it to another page within your view project. It's not going to be great.
08:24
So the ideal solution that we're now going to arrive at is extracting out our text area to a completely separate component of its own. Now this does have implications with things
08:34
like attributes that you want to set, certain events that you want to fire from your text area, but I'm going to do my best to cover all of them now. Now inside of the components directory,
08:44
let's create out a form folder. And inside of here, I'm just going to follow view standards. I'm going to say app form text area dot view.
08:52
So that's going to be a completely separate component that we can use to basically render out one of these text areas. So I'm actually going to copy all of this into here,
09:02
and I'm actually going to take all of this as well and pop that into the script section of this component. So let's just pop that in there and just re-indent that.
09:11
Great. So now we've got a completely separate text area that we can import and include wherever we need a resizable text area.
09:19
And if you already had a text area field somewhere and you wanted to include that in here, you just import that component from within here. We'll try and keep things simple for now.
09:28
So let's go ahead and define out our app form text area. Remember, we don't need this method in here now. But what we do need to do is import that component that we've just created.
09:40
So let's import this from the root directory under components, under form, and in app form text area. Great. So now all we need to do is just to find out
09:50
our list of components in here. And that is just app form text area, so pretty straightforward stuff. We can actually move these styles over as well now,
09:58
since we have a separate component. So I'm going to create a style tag in here. I'm going to make this scoped, because it only applies to this particular component.
10:06
And we should be good to go. So let's go and just make sure we've included this within here, come over to the browser, and you can see that this is working as we would expect.
10:17
But let's look at the ramifications of moving something like a form here over to a new component. Let's just say that on this page that you were building, you had some data that you wanted
10:26
to use to keep track of what is in that text area. That's pretty normal, and I usually include a form property in here with a body, like so. Well, we'd want to use vmodel on this to hook this up,
10:40
if I can actually type. So let's go ahead and hook this up to form.body. And just down here, let's output the contents of that object. So heading over to the browser, you
10:49
can see that when I actually type something in here, despite the fact we've hooked it up with vmodel, it is not working. So although we've got this working,
10:57
we really have to make sure that we can actually keep track of what's being typed inside of here. And that's actually pretty straightforward. If there are any other props that you want to pass down,
11:07
you can follow this same pattern. So let's just say that you wanted to set the name and the ID to the same thing. We would pass some props down to this component,
11:15
because of course, not every text area on your site is going to have the same name and ID. So for example, you could define out a name prop in here. You could say that this is required,
11:25
because we're going to also set this to the ID, which is required. And it's recommended that you set a type hint in here as well.
11:32
So we're going to say that we want to make sure that that's a string. So now what we can do is we can replace out the name and the ID with the name prop,
11:39
making sure that we bind that in using a colon in there. And if we come over to here, you can see we get missing required prop name. So we're going to have to pass that in to here.
11:49
So we're just going to call that body for now. And you can see, if we just inspect this element, that we get the name and the ID both set to body. Great.
11:56
So now that we've done that, we need to work out this vModel stuff. So over in our app form text area, a little lesson on how vModel actually works.
12:06
vModel will take in a prop called value to set the initial value of the field. So what we can actually do is we can say required false, because that might not be passed in.
12:16
And if we initially set the body to something in here, and we make sure that we bind the value in that we get through as a prop, like so, so let's bind that to the value, we'll actually see that output in there.
12:28
So vModel is essentially just syntactic sugar for passing in a value that exists within what you're binding into here. So we can just use that prop that's
12:38
being passed in by default to bind the value to our text area. But now what happens when we actually want to type and see this value updated?
12:46
Well, in that case, we're going to have to do something within the input event. So what we want to do is, as well as resize this, we also want to emit an event within here.
12:57
And again, this is a little lesson on how vModel works. vModel will listen for any input event, like so, and it will take the value and set that to what you've bound it to within the outer component.
13:10
So all we want to do is use event, target, and value, and emit that from this component under input. So if we just go over here now, and I carry on typing, you can see that that is taking that into account.
13:24
So all of that was completely unrelated to the auto-resizing text area that we've built. But it naturally follows that you'd want to extract this out to a separate component
13:34
to keep this resize method nice and reusable. So there we go. We have an auto-resizing text area with everything hooked up. So we have everything that we need to reuse this component.
1 episode 13 mins

Overview

In some cases, autosizing a textarea height as a user types makes sense. Here's how to do it with Vue.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!