Skip to content

Instantly share code, notes, and snippets.

@enapupe
Last active February 8, 2021 11:26
Show Gist options
  • Select an option

  • Save enapupe/2a59589168f33ca405d0 to your computer and use it in GitHub Desktop.

Select an option

Save enapupe/2a59589168f33ca405d0 to your computer and use it in GitHub Desktop.
Auto Grow/Shrink textarea directive for AngularJS (credits to this gist https://gist.github.com/thomseddon/4703968, specially @huyttq)
angular.module("myApp").directive("autoGrow", function(){
return function(scope, element, attr){
var update = function(){
element.css("height", "auto");
var height = element[0].scrollHeight;
if(height > 0){
element.css("height", height + "px");
}
};
scope.$watch(attr.ngModel, function(){
update();
});
attr.$set("ngTrim", "false");
};
});
@enapupe

enapupe commented Aug 28, 2014

Copy link
Copy Markdown
Author

ngTrim is set to false because otherwise angularjs trims newlines and spaces. That makes watch not being fired when you enter new lines/spaces..

@enapupe

enapupe commented Oct 17, 2014

Copy link
Copy Markdown
Author

Issues

  • If the textarea is invisible it won't come with the automagic height because the scrollheight was zero on modelchange, unless the same model that populates de textarea makes it visible.

@zhouzi

zhouzi commented Dec 30, 2014

Copy link
Copy Markdown

Wow, working even better than the shadow hack trick while being terribly simple. Do you actually use it on a large project? Seems to be well supported so I think I'll go with it! May I suggest the following:

return function (scope, element, attrs) {
    function update () {
        element.css("height", "auto");

        var height = element[0].scrollHeight;
        if (height > 0) element.css("height", height + "px");
    }

    scope.$watch(attrs.ngModel, update);
    attrs.$set("ngTrim", "false");
}

scope.$watch(attr.ngModel, function () { update(); }); is a bit redondant as update is already a function. On the other part, but this is probably a matter of gut, I'm not a big fan of defining function as var.

Also, it'd be cool to include a note about the recommended css for those not coming from thomseddon's gist.

@zhouzi

zhouzi commented Dec 30, 2014

Copy link
Copy Markdown

Note: textarea won't resize if the ng-maxlength is exceeded, which is probably a good thing. Actually the $watcher on ngModel doesn't fire anymore.

@kimardenmiller

Copy link
Copy Markdown

Love it. My only wish is that the textarea did not start out defaulting to two lines. Any idea how to get it to start with a single line, then expand by a line at a time as it does? I guess scrollHeight is doing that?

Here it is in CoffeeScript, injected into a larger project:

autoGrow = ->
  (scope, element, attrs) ->
    update = ->
      element.css 'height', 'auto'
      height = element[0].scrollHeight
      element.css 'height', height + 'px'  if height > 0
    scope.$watch attrs.ngModel, update
    attrs.$set 'ngTrim', 'false'

# Register
App.Directives.directive 'autoGrow', autoGrow

@kimardenmiller

Copy link
Copy Markdown

Fixed for my purposes, though still curious if there might be a more elegant way to somehow control the children element sizes instead:

autoGrow = ->
  (scope, element, attrs) ->
    update = ->
      if element[0].scrollWidth > element.outerWidth isBreaking = true else isBreaking = false
      element.css 'height', 'auto' if isBreaking
      height = element[0].scrollHeight
      element.css 'height', height + 'px'  if height > 0
    scope.$watch attrs.ngModel, update
    attrs.$set 'ngTrim', 'false'

# Register
App.Directives.directive 'autoGrow', autoGrow

@muratsplat

Copy link
Copy Markdown

@kimardenmiller can I use your code in my project ?

@muratsplat

Copy link
Copy Markdown

if I can convert your coffee code to native js code 😃

@muratsplat

Copy link
Copy Markdown

I have written my version with little changes

angular.module('mayApp')

  .directive('autogrow', function () {
    return {

      restrict: 'A',
      link: function postLink(scope, element, attrs) {
          // hidding the scroll of textarea
          element.css('overflow', 'hidden');

          var update = function(){

              element.css("height", "auto");

              var height = element[0].scrollHeight;

              if(height > 0){

                  element.css("height", height + "px");
                }

          };

          scope.$watch(attrs.ngModel, function(){

              update();
          });

          attrs.$set("ngTrim", "false");
      }
    };
  });

on view layer

<textarea autogrow class="form-control" rows="10" placeholder="Enter ..." ng-model="content.body" ng-maxlength="16777215" ng-minlength="10" required  name="content"></textarea>

@laurensnl

Copy link
Copy Markdown

Thanks guys! Works perfect! There does however seem to be an issue with Chrome (Version 46.0.2490.80 (64-bit) on Mac). Setting the element height twice every keystroke (first to auto, then to the desired height) causes a slight but annoying delay while typing. Any suggestions to solve that?

@pacificsharma

Copy link
Copy Markdown

Thanks a lot.

@dcolley

dcolley commented May 5, 2017

Copy link
Copy Markdown

thank you! It works for me on Chrome 57 and IOS 9.

@JavyMB

JavyMB commented May 9, 2017

Copy link
Copy Markdown

Thank you ! works for me too.

@bettysteger

Copy link
Copy Markdown

In case someone uses debounced model updates - this will just work with a timeout, which is not very nice, so i changed the code a bit to also listen to the input event:

angular.module('myApp').directive('autogrow', function () {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      // hide scroll of textarea
      element.css('overflow', 'hidden');

      var autogrow = function () {
        element.css('height', 'auto');

        var height = element[0].scrollHeight;

        if(height > 0){
          element.css('height', height + 'px');
        }
      };

      // using 'input' event on element, because of debounced model update
      element.on('input', autogrow);

      // need this too, for initialize
      scope.$watch(attrs.ngModel, autogrow);
    }
  };
});

@bettysteger

Copy link
Copy Markdown

in case textarea is not yet visible:

if(!element.is(':visible')) {
  scope.$watch(function () { return element.is(':visible'); }, function (visible) {
    if(visible) {
      autogrow();
      scope.$applyAsync();
    }
  });
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment