textarea实现高度自适应

2019-12-09  本文已影响0人  LM林慕

业务场景

需求:当用户在输入框中输入内容时,原有设计是最多输入三行,超出时会有滚动条,现改为不需要滚动条,输入的内容为多高,textarea就是多高。
思路:

  1. 使用contenteditable属性用div模拟textarea文本框;
  2. javascript实现。

技术实现

1. 使用div模拟textarea文本框实现高度自适应

  1. 给块状元素加contenteditable="true"实现可编辑,如:
<div contenteditable="true"></div>

contenteditable属性虽是HTML5里面的内容,但是IE很早就支持此标签属性了。所以,兼容性方面是不用太担心的。

需求:如何实现模拟placeholder?

在项目中,我是采用定位的方式,给可编辑的div加了一个同级标签spandiv/span的父级相对定位,span绝对定位,定位的位置和可编辑的div在同一位置。

// 技术栈:vue
// html
// 因为在移动端模拟的placeholder是两行,所以加了一个最小高度
<div>
  <div contenteditable="true" @input="questionProject" ref="question" :style="!questionProject?'minHeight:1.12rem':''"></div>
  <span class="iplaceholder" v-show="!questionProject">聚焦于技术经理在管的重点项目,包括疑难、应收项目,问题描述、处理进展</span>
</div>

2. 简单版的JavaScript实现,如不考虑兼容问题,已可满足使用

textarea加一个keyupkeydown事件,代码很简单,如下:

// 技术栈:vue,布局方式:rem
// html
<textarea placeholder="请输入内容" v-model.trim="xxxxxx" @keydown="autoTextarea"></textarea>
// js
autoTextarea = (e) => {
  let elem = e.target
  let rootFontSize = parseFloat(document.documentElement.style.fontSize)
  elem.style.height = 'auto'
  elem.scrollTop = 0 // 防抖动
  elem.style.height = elem.scrollHeight / rootFontSize + 'rem'
}

3. 兼容版JavaScript实现

我看了好几篇文章,几乎都是互相借鉴的,这个方法确实考虑的相对周全,具体代码如下:

// js
/**
* 文本框根据输入内容自适应高度
* @param                {HTMLElement}        输入框元素
* @param                {Number}                设置光标与输入框保持的距离(默认0)
* @param                {Number}                设置最大高度(可选)
*/

var autoTextarea = function (elem, extra, maxHeight) {
  extra = extra || 0;
  var isFirefox = !!document.getBoxObjectFor || 'mozInnerScreenX' in window,
    isOpera = !!window.opera && !!window.opera.toString().indexOf('Opera'),
    addEvent = function (type, callback) {
      elem.addEventListener ?
        elem.addEventListener(type, callback, false) :
        elem.attachEvent('on' + type, callback);
    },
    getStyle = elem.currentStyle ? function (name) {
      var val = elem.currentStyle[name];

      if (name === 'height' && val.search(/px/i) !== 1) {
        var rect = elem.getBoundingClientRect();
        return rect.bottom - rect.top -
          parseFloat(getStyle('paddingTop')) -
          parseFloat(getStyle('paddingBottom')) + 'px';
      };

      return val;
    } : function (name) {
      return getComputedStyle(elem, null)[name];
    },
    minHeight = parseFloat(getStyle('height'));

  elem.style.resize = 'none';

  var change = function () {
    var scrollTop, height,
      padding = 0,
      style = elem.style;

    if (elem._length === elem.value.length) return;
    elem._length = elem.value.length;

    if (!isFirefox && !isOpera) {
      padding = parseInt(getStyle('paddingTop')) + parseInt(getStyle('paddingBottom'));
    };
    scrollTop = document.body.scrollTop || document.documentElement.scrollTop;

    elem.style.height = minHeight + 'px';
    if (elem.scrollHeight > minHeight) {
      if (maxHeight && elem.scrollHeight > maxHeight) {
        height = maxHeight - padding;
        style.overflowY = 'auto';
      } else {
        height = elem.scrollHeight - padding;
        style.overflowY = 'hidden';
      };
      style.height = height + extra + 'px';
      scrollTop += parseInt(style.height) - elem.currHeight;
      document.body.scrollTop = scrollTop;
      document.documentElement.scrollTop = scrollTop;
      elem.currHeight = parseInt(style.height);
    };
  };

  addEvent('propertychange', change);
  addEvent('input', change);
  addEvent('focus', change);
  change();
};

//html
<textarea id="textarea" placeholder="回复内容"></textarea>
<script>
  var text = document.getElementById("textarea");
  autoTextarea(text);// 调用
</script>

总结

需使用的小伙伴可比较一下,我在项目里面使用的是第一种和第二种方法,因为我们做的是基于webview的混合开发(内嵌vue SPA),不需要考虑那么多的兼容问题,如果需要考虑更过兼容问题的话,可以尝试使用第三种方法。

上一篇下一篇

猜你喜欢

热点阅读