Wednesday, December 13, 2023

Auto Grow a Textarea with Javascript | How to create auto-resize textarea using JavaScript

The idea was to make a <textarea> more like a <div> so it expands in height as much as it needs to in order to contain the current value. It’s almost weird there isn’t a simple native solution for this
The trick is that you exactly replicate the content of the <textarea> in an element that can auto expand height, and match its sizing.

Instead, you exactly replicate the look, content, and position of the element in another element. You hide the replica visually (might as well leave the one that’s technically-functional visible).
You need to make sure the replicated element is exactly the same

Same font, same padding, same margin, same border… everything. It’s an identical copy, just visually hidden with visibility: hidden;. If it’s not exactly the same, everything won’t grow together exactly right.

We also need white-space: pre-wrap; on the replicated text because that is how textareas behave.
Example is as below:

HTML Part

<div class="grow-wrap">
    <textarea name="text" id="text"></textarea>
</div>

CSS Part

/* For grow textarea */
.grow-wrap {
    /* easy way to plop the elements on top of each other and have them both sized based on the tallest one's height */
    display: grid;
    width: 100%;
}

.grow-wrap::after {
    /* Note the weird space! Needed to preventy jumpy behavior */
    content: attr(data-replicated-value) " ";
    /* This is how textarea text behaves */
    white-space: pre-wrap;
    /* Hidden from view, clicks, and screen readers */
    visibility: hidden;
}

.grow-wrap>textarea {
    /* You could leave this, but after a user resizes, then it ruins the auto sizing */
    resize: none;
    /* Firefox shows scrollbar on growth, you can hide like this. */
    overflow: hidden;
}

.grow-wrap>textarea,
.grow-wrap::after {
    font: inherit;
    grid-area: 1 / 1 / 2 / 2;
    margin: 0 !important;
    padding-top: 10px;
    padding-bottom: 10px;
}

JavaScript Part

const growers:any = document.querySelectorAll(".grow-wrap");
growers.forEach((grower:any) => {
    const textarea = grower.querySelector("textarea");
    textarea.addEventListener("input", () => {
        grower.dataset.replicatedValue = textarea.value;
    });
});

Live example is as below:


No comments:

Post a Comment