Implementation of textarea adaptive height solution in Vue

Implementation of textarea adaptive height solution in Vue

Give the solution first. Students who need Vue stack can directly download vue-awesome-textarea

Hidden Problems

Apart from native JS, most of the UI libraries of the framework support the adaptive textarea height function, but one function is generally overlooked, which is the adaptive height echo.

When using these libraries, we can easily type content in the textarea, and it will automatically extend one line when it exceeds the range to ensure the content is highly adaptive. When we submit content and use the same UI to render on other pages, trouble arises. Some UI libraries do not support adaptive echo, which requires us to calculate a base value between line height, number of lines, and even height to achieve echo.

Solution to adaptive height

There are two common solutions. One is to add a ghost DOM in the "remote area" of the page to simulate input line breaks. This DOM may be a div with the editable attribute set to true or an identical textarea.
Taking the input component of element-ui as an example, when we enter a value in the component, the resizeTextarea method will be called

resizeTextarea() {
 if (this.$isServer) return;
 const { autosize, type } = this;
 if (type !== 'textarea') return;
 if (!autosize) {
  this.textareaCalcStyle = {
   minHeight: calcTextareaHeight(this.$refs.textarea).minHeight
  };
  return;
 }
 const minRows = autosize.minRows;
 const maxRows = autosize.maxRows;

 this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
}

When autosize is set to true, the textarea is set to adaptive height. At this time, the height of the textarea will be calculated in real time through the calcTextareaHeight method.

 export default function calcTextareaHeight(
 targetElement,
 minRows = 1,
 maxRows = null
) {
 if (!hiddenTextarea) {
  hiddenTextarea = document.createElement('textarea');
  document.body.appendChild(hiddenTextarea);
 }

 let {
  paddingSize,
  borderSize,
  boxSizing,
  contextStyle
 } = calculateNodeStyling(targetElement);

 hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
 hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';

 let height = hiddenTextarea.scrollHeight;
 const result = {};

 if (boxSizing === 'border-box') {
  height = height + borderSize;
 } else if (boxSizing === 'content-box') {
  height = height - paddingSize;
 }

 hiddenTextarea.value = '';
 let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

 if (minRows !== null) {
  let minHeight = singleRowHeight * minRows;
  if (boxSizing === 'border-box') {
   minHeight = minHeight + paddingSize + borderSize;
  }
  height = Math.max(minHeight, height);
  result.minHeight = `${minHeight}px`;
 }
 if (maxRows !== null) {
  let maxHeight = singleRowHeight * maxRows;
  if (boxSizing === 'border-box') {
   maxHeight = maxHeight + paddingSize + borderSize;
  }
  height = Math.min(maxHeight, height);
 }
 result.height = `${ height }px`;
 hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
 hiddenTextarea = null;
 return result;
};

We can see

 if (!hiddenTextarea) {
  hiddenTextarea = document.createElement('textarea');
  document.body.appendChild(hiddenTextarea);
}

Element-ui creates a textarea DOM and copies the style of the real textarea to the hiddenTextarea through the calculateNodeStyling method (the overflow is not synchronized, the real textarea is hidden). Then monitor the input value of textarea and synchronize it to hiddenTextarea. At the same time, synchronize the scrollHeight of hiddenTextarea to the height of textarea, and finally destroy the dom.

Regarding style synchronization, element uses the two APIs getComputedStyle and getPropertyValue. Of course, if you wrap it yourself, you can also use the CSS preprocessor mixin.

The second solution is similar to the first solution, but no additional DOM is created. Take the vue-awesome-textarea at the beginning as an example:

 init() {
  this.initAutoResize()
},
initAutoResize () {
  this.autoResize && this.$nextTick(this.calcResize)
}

When the page is mounted or the content changes and autoResize is turned on, the this.calcResize method is executed.

 calcResize() {
 this.resetHeight()
 this.calcTextareaH()
},

resetHeight() {
 this.height = 'auto'
},

calcTextareaH() {
 let contentHeight = this.calcContentHeight()
 this.height = this.calcHeightChange(contentHeight) + 'px'
 if (this.needUpdateRows(contentHeight)) {
  this.updateRows(contentHeight)
 }
 this.oldContentHeight = contentHeight
},

calcContentHeight () {
 const { paddingSize } = this.calcNodeStyle(this.$el)
 return this.$el.scrollHeight - paddingSize
},

resetHeight() is used to initialize the height of the textarea, the default is auto. The calcTextareaH() method is used to calculate the height of the content area (the scrollHeight of the textarea minus the height of the padding), and synchronize the calculated height to the height of the textarea in real time:
this.height = this.calcHeightChange(contentHeight) + 'px'

Compared with Solution 1, this solution adopts the same idea (dynamically modifying the height), but reduces the additional DOM creation and destruction process.
In addition, vue-awesome-textarea also supports callback of the number of lines during the adaptive process, which can better support data echo. The implementation method is also very simple:

 computed: {
 ...
 oneRowsHeight() {
  return this.calcContentHeight() / Number(this.rows) || 0
 }
 ...
}

In computed we calculate the height of a single line, and when executing the this.calcTextareaH() method we record the content height:

 this.oldContentHeight = contentHeight

Next, we will check whether there is an added row operation. If it is added, the new content height will be different from the old content height:

 needUpdateRows(newContentHeight) {
  return this.oldContentHeight !== newContentHeight
},

At this point we will emit the latest row height outside the component:

 updateRows(contentHeight) {
  this.$emit('getRows', Math.round(contentHeight / this.oneRowsHeight))
}

This is the end of this article about the implementation of textarea adaptive height solution in Vue. For more relevant Vue textarea adaptive content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed explanation of the idea of ​​Vue to implement fixed number of input lines and add underline style for textarea
  • Vue implements dynamic display of the remaining words in textarea

<<:  Text pop-up effects implemented with CSS3

>>:  Detailed explanation of some settings for Table adaptation and overflow

Recommend

Web page creation basic declaration document type description (DTD

Using CSS layout to create web pages that comply w...

Example of using negative margin to achieve average layout in CSS

For evenly distributed layouts, we generally use ...

Analysis of Linux configuration to achieve key-free login process

1.ssh command In Linux, you can log in to another...

Detailed tutorial for installing mysql5.7.18 on centos7.3

1 Check the Linux distribution version [root@type...

What you need to understand about MySQL locks

1. Introduction MySQL locks can be divided into g...

The easiest way to debug stored procedures in Mysql

A colleague once told me to use a temporary table...

Vue implements horizontal scrolling of marquee style text

This article shares the specific code for Vue to ...

Join operation in Mysql

Types of joins 1. Inner join: The fields in the t...

Introduction to HTML_PowerNode Java Academy

What is HTML? HTML is a language used to describe...

Four data type judgment methods in JS

Table of contents 1. typeof 2. instanceof 3. Cons...

Share 101 MySQL debugging and optimization tips

MySQL is a powerful open source database. With th...

Docker container connection implementation steps analysis

Generally speaking, after the container is starte...

Apache ab concurrent load stress test implementation method

ab command principle Apache's ab command simu...

Analysis of the principles and usage of Docker container data volumes

What is a container data volume If the data is in...

Vue basic instructions example graphic explanation

Table of contents 1. v-on directive 1. Basic usag...