Hassan Baig
Hassan Baig

Reputation: 15834

Creating a textarea that resizes with volume of text (vanilla JS, no JQuery)

Server-side dev here, looking to dabble in a bit of vanilla JS to learn the ropes.

This question is about creating a form where textareas are initially hidden behind button clicks, and which also resize as the text entered into them grows larger. It's a challenging problem for someone at my level, and I'm trying to code it myself for learning purposes.


Here's what I've done so far.

Imagine a forum where users submit textual content. Each submission has a "reply" button under it. Pressing that opens a simple textarea that one can type their response into. I wrote simple JS to accomplish this:

var replyBtns = document.querySelectorAll('[id=rep]');

for(var i = 0; i < replyBtns.length; i++) {
 replyBtns[i].addEventListener('click', function(e) {
   e.preventDefault();
   var textarea = document.getElementById("reply-message");
   if (textarea) { textarea.parentNode.removeChild(textarea); }
   var replyBox = document.createElement('textarea');
    replyBox.setAttribute('id', 'reply-message');
    replyBox.setAttribute('placeholder', 'Reply');
   this.parentNode.insertAdjacentElement('beforeend', replyBox);
 }, false);
}

As you can see, this JS creates a text area and adds it in the HTML. The CSS to go with it is:

textarea#reply-message {
  display: block;
  border: none;
  color:#306654;
  font-size:120%;
  padding: 5px 10px;
  line-height: 20px;
  width:550px;
  height: 20px;
  border-radius: 6px;
  overflow: hidden;
  resize: none;
}

This works well.


My next endeavour was to make this textarea resize as the text starts overflowing. For that, I'm taking this route:

This strategy uses the property that any div element naturally stretches to fit the height of its content (assuming no floats or absolutely positioned elements). I owe this technique to this blog post.

Here's the JS for implementing the aforementioned technique:

let txt = document.getElementById('reply-message'),
    hiddenDiv = document.createElement('div'),
    content = null;

hiddenDiv.classList.add('hiddendiv');

document.body.appendChild(hiddenDiv);

txt.addEventListener('keyup', function () {

  content = this.value;
  hiddenDiv.innerHTML = content + '\n\n';
  this.style.height = hiddenDiv.getBoundingClientRect().height + 'px';

}, false);

Where hiddendiv is:

.hiddendiv {
  position: absolute;
  left: -9999px;
  visibility: hidden;
  white-space: pre-wrap;
  width: 550px;
  height: 20px;
  font-size: 120%;
  padding: 5px 10px;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

But thing is, I need this JS snippet to run only once the textarea actually exists.

Currently, I have it running on page load, where it's unable to have any effect since textareas don't yet exist. How do I ensure it only runs once they've been created? Being a newbie in JS, this is totally eluding me. Please advise.

Upvotes: 0

Views: 238

Answers (4)

kiranvj
kiranvj

Reputation: 34137

There is plugin for that autosize

Check the src at github, you will get idea on how to do it.

If your code is working in page load and not in text update, you need to call your resize function in every keyup, resize and input events.

window.addEventListener('resize', yourFunc);
textarea.addEventListener('paste', yourFunc);
textarea.addEventListener('keyup', yourFunc);

There is simpler method here

function auto_grow(element) {
    element.style.height = "5px";
    element.style.height = (element.scrollHeight)+"px";
}
textarea {
    resize: none;
    overflow: hidden;
    min-height: 50px;
}
<textarea onkeyup="auto_grow(this)"></textarea>

Upvotes: 0

2pha
2pha

Reputation: 10165

Maybe using a div with contenteditable instead of a textarea would make your life a bit easier.

#textarea {
    -moz-appearance: textfield-multiline;
    -webkit-appearance: textarea;
    border: 1px solid gray;
    padding: 2px;
    width: 400px;
}
<div id="textarea" contenteditable>dummy textarea</div>

Upvotes: 0

Jason Warta
Jason Warta

Reputation: 450

This isn't exactly answering your question, but restructuring your code a bit to achieve the same result.

You should add the event listener to your textarea when you create it, before you add it to the page.

Insert code like this in your first glob of javascript:

replyBox.setAttribute('placeholder', 'Reply');
replyBox.addEventListener('keyup', function () {
    /* do event handling here */
}, false);

A cleaner way to handle all of this would be to move all of the code pertaining to setting up your textareas into a separate function.

function setUpReplyBox(replyBox) {
    replyBox.setAttribute('id', 'reply-message');
    replyBox.setAttribute('placeholder', 'Reply');

    hiddendiv.createElement('div');
    content = null;

    hiddendiv.classList.add('hiddendiv');
    document.body.appendChild(hiddenDiv);

    replyBox.addEventListener('keyup', function () {
        content = this.value;
        hiddenDiv.innerHTML = content + '\n\n';
        this.style.height = hiddenDiv.getBoundingClientRect().height + 'px';
    }, false);
}

Call this right after you create the item, at

var replyBox = document.createElement('textarea');
setUpReplyBox(replyBox);
this.parentNode.insertAdjacentElement('beforeend', replyBox);

Upvotes: 1

Four_lo
Four_lo

Reputation: 1150

Listen for the domContentLoaded Event

document.addEventListener("DOMContentLoaded", function(event) {
    console.log("DOM fully loaded and parsed");
});

Reference Link https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded

Upvotes: 0

Related Questions