3

I've implemented the logic of auto-expanding the height of a textarea on the keyup event. However, I want the textarea to also initialise its height once the value is bound to the textarea via a knockout custom binding. Any solutions? (With the use of only KnockoutJS, without using jQuery or any other library.)

Balázs
  • 2,929
  • 2
  • 19
  • 34
user2235768
  • 283
  • 1
  • 4
  • 18

2 Answers2

6

I'd strongly advice against using an event to trigger the resize. Instead, you can use the textInput binding to keep track of the input in an observable and subscribe to changes there.

Here's an example:

<textarea data-bind="textInput: value, autoResize: value"></textarea>
ko.bindingHandlers.autoResize = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    ko.computed(function() {
      ko.unwrap(valueAccessor());
      resizeToFitContent(element);
    })
  }
};

This works because:

  • The textInput binding writes any input change to an observable value variable.
  • The computed uses this value to trigger a resize. This creates a subscription automatically.

This is better than a keydown approach because it deals with stuff like Right Mouse Button > cut

Example showing the event equivalent as well:

var resizeToFitContent = function(el) {
  // http://stackoverflow.com/a/995374/3297291
  el.style.height = "1px";
  el.style.height = el.scrollHeight + "px";
}


ko.bindingHandlers.autoResize = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    ko.computed(function() {
      ko.unwrap(valueAccessor());
      resizeToFitContent(element);
    })
  }
};

ko.applyBindings({ 
  value: ko.observable("Test"), 
  
  onKey: function(data, event) {
   resizeToFitContent(event.target);
  } 
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<textarea data-bind="textInput: value, autoResize: value"></textarea>
<textarea data-bind="event: { keyup: onKey }"></textarea>
user3297291
  • 22,592
  • 4
  • 29
  • 45
0

You can create custom binding that will apply autoresize() to all textareas:

ko.bindingHandlers.jqAutoresize = {
    init: function(element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        $(element).autoResize(options);
    }
};

Update your view like this:

<div data-bind="foreach: Rows" >
  <textarea data-bind="jqAutoresize: {}, value: RowText" ></textarea>
</div>
Balázs
  • 2,929
  • 2
  • 19
  • 34
amit 1984
  • 356
  • 3
  • 9
  • Thank you so much for replying my question, I am new to knockout js. its not working, how to create custom binding on autoresize()? would you mind if you could set it in JSfiddle, please? – user2235768 Feb 17 '17 at 11:02
  • using a binding handler is definitely the way to do it in knockout. to actually check if the text area needs resizing is trickier, you would need to get the value of the text area and check the length of the string. Then you would probably need to make an informed decision about when to grow the size of the textarea. Basically you will need to compare the ```textarea.value.length``` to ```textarea.rows``` – dpix Feb 17 '17 at 11:17