Salesforce Lightning Resizable Text Area Component

In my series of component-based posts, I have another small, but useful, component – an auto resizing text area.

Salesforce Lightning Dynamic Text Area

For some of my other component based blogs, look here:

This time, I’m going to show you how to create an Auto Sizing Text Area.

Background

To build this component, I used a lot of basic HTML and JavaScript as well as just a little Lightning.
A key part of the component is binding its data properly. This means it can be used as easily as any other built-in Lightning Component (no special events needed, just bind to the value attribute).

Important features of this component:

  • Debounced data binding – this means that the component won’t send out a flood of notifications on changes; it will send out a rate limited update stream.
  • No form divs are wrapped around this component because this sometimes proves difficult to manage if not in a form. It’s easy to put in a regular slds form though.
  • No third party static resources used.

Method

Start by defining a Generic ComponentEvent – this has two attributes, TYPE and DATA (I use it for most events in my systems). I use it for onfocus and onclick event handling.

<aura:event type="COMPONENT" description="General change event" >
 <aura:attribute name="type" default="DATA_CHANGE" type="string" description="can be anything - use this to verify that the data you receiving is what you expect"/>
 <aura:attribute name="data" default="{}" type="Object" description="Object holding changed data"/>
 </aura:event>

The actual markup of the component is quite small – just a text area with all requisite bindings and methods. You can see the two events that the component emits and also note the use of the Lightning `GlobalId`
Which is a special feature of Lightning and is replaced with an id that is Global to THIS component, but unique across all components. This means we won’t get id collisions if we have multiple text areas.

<aura:component>
 <!--some elements omitted-->
 <aura:method name="recalculateHeight" action="{!c.recalculateHeight}"/>
 <aura:registerEvent name="onfocus" type="c:ComponentEvent" />
 <aura:registerEvent name="onclick" type="c:ComponentEvent" />
 <textarea aura:id="simpletextarea" value="{!v.value}" id="{!GlobalId + '-textarea'}" readonly="{!v.readonly}" tabindex="0" onfocus="{!c.handleOnFocus}" onclick="{!c.handleOnClick}" oninput="{!c.handleOnChange}" class="{! v.value.length gt 1 ? (v.class + ' dynamic-textarea slds-textarea') : (v.class + ' dynamic-textarea-empty slds-textarea') }" />
 </aura:component>

The controller code contains most of the logic. I’ll explain it method by method:

`onRender` is the init routine of this component. I used `onRender` as an init event because I needed window height, which is not available on init.

onRender: function(component, event, helper) {
 if (component.get("v.rendered")) {
 return;
 }
 component.set("v.rendered", true);
 helper.setHeight(component);
 },

`focus` allows focus to be set via an `aura:method` from outside the component. This then triggers the following method, `handleOnFocus`.

focus: function(component, event, helper) {
 var el = document.getElementById(component.getGlobalId() + '-textarea');
 el.focus();
 },

`handleOnFocus` handles input and sets the focus ring. This also allows the text area to resize via css that targets the `:focus` pseudo class.

handleOnFocus: function(component, event, helper) {
 component.getEvent("onfocus").fire();
 },

`getValue` returns the value of the text area – note the use of the `GlobaId`.

getValue: function(component, event, helper) {
 var el = document.getElementById(component.getGlobalId() + '-textarea');
 return el.value;
 },

`handleOnChange` fires on every change to the text area, but is debounced to prevent too many screen updates / events being emitted.

handleOnChange: function(component, event, helper) {
 var el = document.getElementById(component.getGlobalId() + '-textarea');
 component.set("v.value", el.value);
 var timer = component.get("v.storedTimer");
 var timeout = 20;
 if (timer) {
 window.clearTimeout(timer);
 }
 timer = window.setTimeout(
 $A.getCallback(function() {
 helper.setHeight(component);
 }), timeout
 );
 component.set("v.storedTimer", timer);
 },

Finally, as you can see in the `handleOnChange` method, the autoresize part of the component is triggered in the helper class. This is the `setHeight` method.
This method measures the scroll height of the text area, adds a small buffer and resizes the text area to that.

setHeight: function(component) {
 var el = document.getElementById(component.getGlobalId() + '-textarea');
 // compute the height difference which is caused by border and outline
 var outerHeight = parseInt(window.getComputedStyle(el).height, 10);
 //3 pix is just a tiny static extra buffer. Adjust if necessary
 var diff = (outerHeight - el.clientHeight) + 3;
 // set the height to 0 incase it needs to be set smaller
 el.style.height = 0;
 // set the correct height
 // el.scrollHeight is the full height of the content, not just the visible part
 el.style.height = Math.max(60, el.scrollHeight + diff) + 'px';
 }

As you can see, it’s really quite a simple component.

To use it, just place it in your form.

<c:DynamicTextArea aura:id="noteTextArea" value="{!v.noteBody}" readonly="{!v.editing ? false : true}" onfocus="{!c.handleEditClick}" onclick="{!c.handleDivClick}" class=" slds-m-top_xx-small slds-m-left_x-small " />

Download the Files

All the files are located on Github.

Enjoy!

Moving Forward in Lightning

Have questions? Let us know in a comment below or contact our team directly. You can also check out our other posts on customizing your Salesforce solution.

1 thought on “Salesforce Lightning Resizable Text Area Component”

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top