zoukankan      html  css  js  c++  java
  • vue-textarea 自适应高度

    需求简介

    一个搜索页面,上面输入框,下面列表展示搜索到的结果。

    重点是:产品要求搜索框默认显示一行,当输入的文字超过一行时,输入框的高度会随着改变,直到输入完毕。

    解决思路设想

    本想利用textarea实现,但textarea不支持自适应高度,而是定好高度或者是行数之后,超出部分就会显示滚动条。只能另想。

    根据需求,首先想到了张鑫旭伪类匹配列表数目实现微信群头像CSS布局的技巧一文提到的文字多字号自动变小的技巧,但仔细一琢磨,不行。这个是根据内容元素的个数,进行处理,而这儿是输入框,没有内容元素。

    后面想到可以利用html属性contenteditable="true",加在div上让其可编辑来模拟自适应高度。可是需要在vue中双向绑定实现,这个不是很好处理。

    后面想到利用textarea的row属性,根据输入内容的长度控制row的值,为1-n行,但这个似乎不是很智能,因为多少个字体一行不一定,英文、中文、数字的宽度不一致,而且row属性在每个浏览器中的表现不一致。

    最后利用textarea,监听change事件,让其高度=其滚动条高度,来达到高度自适应。

    没想到最后还是利用了textarea。

    实现

    参考:textarea如何实现高度自适应?

    util.js

    /**
    * 文本框根据输入内容自适应高度
    * @param       {HTMLElement}   输入框元素
    * @param       {Number}        设置光标与输入框保持的距离(默认0)
    * @param       {Number}        设置最大高度(可选)
    * @callback    {Function}      设置回调函数(可选)
    */
    export const autoTextarea = function (elem, extra, maxHeight, callback) {
      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);
    
          callback(parseInt(style.height));
        };
      };
    
      addEvent('propertychange', change);
      addEvent('input', change);
      addEvent('focus', change);
      change();
    };
    
    export const debounce = function (func, delay) {
      let timer;
    
      return function (...args) {
        if (timer) {
          clearTimeout(timer);
        }
        timer = setTimeout(() => {
          func.apply(this, args);
        }, delay || 500);
      }
    }
    

    说明:由于下面是列表,需要计算高度,为了避免重新再去获取高度,所以加了个回调方法,把高度回调出去。

    vue-search.vue

    <template>
      <div class="search-list">
        <div>
            <textarea ref="textarea" v-model="keywords" :maxlength="keywordsMax" @change="searchChange"></textarea>
            <span class="clear" @click="clearKeywords">x</span>
        </div>
        <div class="list" ref="list">
          <ul>
            <li v-for="item in list" :key="item.id">
              <span class="icon"></span>
              <dl>
                <dt>{{item.title}}</dt>
                <dd>{{item.desc}}</dd>
              </dl>
            </li>
          </ul>
        </div>
      </div>
    </template>
    
    <script>
      import { debounce, autoTextarea } from '@/util.js';
    
      let rootFontSize = parseFloat(document.documentElement.style.fontSize);
    
      export default {
        data () {
          return {
            keywordsMax: 128,
            keywords: '',
            list: []
          }
        },
        mounted () {
            this.$nextTick(() => {
              let textarea = this.$refs.textarea;
              textarea.focus();
              let prevHeight = 65;
              textarea && autoTextarea(textarea, 5, 0, (height) => {
                height += 20;
                if (height !== prevHeight) {
                  prevHeight = height;
                  let rem = height / rootFontSize;
                  this.$refs.list.style.height = `calc(100% - ${rem}rem)`;
                }
              });
            })
        },
        methods: {
          clearKeywords () {
            this.keywords = '';
            this.list = [];
            let textarea = this.$refs.textarea;
            let height = 40;
            let rem = height / rootFontSize;
            textarea.style.height = `${rem}rem`;
            rem = (height + 20) / rootFontSize;
            this.$refs.list.style.height = `calc(100% - ${rem}rem)`;
            textarea.focus();
          },
          searchChange: debounce(function () {
            let trim = this.keywords.trim();
            if (!trim) {
              this.list = [];
              return;
            }
            const params = {
              keywords: this.keywords
            }
            // 调api ...
          })
        }
      }
    </script>
    

    补充div模拟textarea自适应

    <style>
        .textarea{
             400px;
            min-height: 20px;
            max-height: 300px;
            _height: 120px;
            margin-left: auto;
            margin-right: auto;
            padding: 3px;
            outline: 0;
            border: 1px solid #a0b3d6;
            font-size: 12px;
            line-height: 24px;
            padding: 2px;
            word-wrap: break-word;
            overflow-x: hidden;
            overflow-y: auto;
         
            border-color: rgba(82, 168, 236, 0.8);
            box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
        }
    </style>
    
    <div class="textarea" contenteditable="true"></div>
    
  • 相关阅读:
    oracle 11g 更改字符集,9i导入11g 出现 ORACLE 错误 12899 处理
    使用XUACompatible来设置IE浏览器兼容模式
    server2008中如何关闭internet explorer增强的安全配置
    oracle修改密码
    ORA28000: the account is locked的解决办法
    C#与word
    Javascript 使用大全
    双机热备、集群及高可用性入门(转载 rdxx.com)
    table画细线
    CSS总结
  • 原文地址:https://www.cnblogs.com/EnSnail/p/10770173.html
Copyright © 2011-2022 走看看