Textarea Resizer

Angular 1 directive which dynamically regulates TEXTAREA's height to fit the content that's being typed in

by Andrey Gruniov

HTML

<div ng-app="myAwesomeTextarea">
<div ng-controller="Controller" style="padding:2em;"> 
  
  <h1>Textarea resizer</h1>  
  <label class="textfield" style="max-width: 75%;">
      <textarea id="resizer-test" rows="1" textarea-resizer></textarea>
      <span class="textfield__label">Type or paste text here</span>
    </label>
  
</div>
</div>

CSS

// Shamelessly stolen from https://www.cutestrap.com/

* {
  border: 0;
  font: inherit;
  margin: 0;
  padding: 0;
  text-rendering: geometricPrecision; }

html {
  box-sizing: border-box;
  font-size: 62.5%;
  height: 100%; }
  @media screen and (min-width: 960px) {
    html {
      font-size: 78.125%; } }

body {
  box-sizing: border-box;
  color: #3c3c3d;
  font-family: "Avenir", "Avenir Next", "Helvetica Neue", "Segoe UI", "Verdana", sans-serif;
  font-size: 1.6rem;
  line-height: 2.4rem;
  min-height: 100%;
  position: relative; }

h1, h2, h3, h4, h5, h6 {
  font-family: "Avenir", "Avenir Next", "Helvetica Neue", "Segoe UI", "Verdana", sans-serif;
  font-weight: 500; }

h1, h2, h3 {
  line-height: 4.8rem;
  padding: .8rem 0 1.6rem; }

h4, h5, h6 {
  padding: 2.4rem 0; }

h1 {
  font-size: 3.6rem; }

h2 {
  font-size: 3.2rem; }

h3 {
  font-size: 2.8rem; }

h4 {
  font-size: 2.0rem; }

h5 {
  font-size: 1.8rem; }

h6 {
  font-size: 1.6rem; }

p {
  padding-bottom: 2.4rem; }

form {
  padding: 2.4rem 0; }

pre,
code,
samp,
kbd {
  background-color: #efeff0;
  font-size: 1.2rem;
  color: #e528b0;
  font-family: "Consolas", "Monaco", "Courier New", monospace;
  line-height: 1; }

code,
samp,
kbd {
  border-radius: 2px;
  overflow-y: hidden;
  overflow-x: auto;
  padding: .4rem .4rem .2rem; }

pre {
  border-radius: 2px;
  line-height: 2.4rem;
  overflow-y: hidden;
  overflow-x: auto;
  padding: 1.2rem 1rem;
  margin: 0 0 2.4rem; }
  pre code,
  pre samp,
  pre kbd {
    background-color: transparent;
    border: none;
    border-radius: 0;
    padding: 0; }

a:link {
  color: #4076D4; }

a:visited {
  color: #9140D4; }

a:hover,
a:focus {
  color: #2e68cc; }

a:active {
  color: #D44076; }

img,
video {
  max-width: 100%;
  display: table; }

hr {
  height: 4.8rem;
  margin-bottom: 2.4rem;
  position: relative; }
  hr:before {
    background-color: #cdcdce;
    content: '';
    height: 1px;
    position: absolute;
    top: 50%;
    width: 100%; }

ol,
ul {
 ...

JavaScript

angular.module('myAwesomeTextarea', [])

.controller('Controller', ['$scope', function($scope) {}])

.directive('textareaResizer', function() {
return {
        restrict: 'A',
        link: function(scope, element) {
            // Check if the directive is used on a textarea and to do go forward otherwise
            if (element[0].tagName != 'TEXTAREA') {
                return;
            }
            // Initial height of an empty element
            const baseHeight = element[0].clientHeight;
            // Maximum height to be enlarged to. I set it x5 but it's up to you.
            const maxHeight = baseHeight * 5;
            // Indicates the textarea content's length after the last change. Used to determine if the content was added or removed.
            var lastValueLength = element[0].value.length;
            function changeHeight() {
                var elHeight = element[0].clientHeight,
                    scrollHeight =  element[0].scrollHeight,
                    valueLength = element[0].value.length;
                if (scrollHeight > elHeight) {
                    // Do not make the textarea higher than the chosen max height, we don't want it to be too high.
                    if (scrollHeight >= maxHeight) {
                        element[0].style.height = maxHeight + 'px';
                    }
                    // Set the exactly the height that we need.
                    else {
                        element[0].style.height = scrollHeight + 'px';
                    }
                }
                // The content was not increased - either removed or remains the same. 
                // This is where current and last content length comparison comes in handy.
                else if (valueLength < lastValueLength) {
                    // The content was removed. We reset the element set to basic and run this function recursively, so that it starts over and sets right new height. 
                    element[0].style.height =...