[转载]自适应高度输入框

初步实现代码如下:

textarea {
     100%;
    height: 92px;
    padding: 20px;
    line-height: 50px;
    resize: none;
    outline: none;
    border: 1px solid #ccc;
    background: #eee;
    font-size: 32px;
    box-sizing: border-box;
}
复制代码
<textarea id="textarea"></textarea>
复制代码
var $textarea = document.getElementById('textarea');

$textarea.addEventListener('input', function() {
    // 总高度 = scrollHeight + 上下边框的宽度(1px * 2)
    $textarea.style.height = $textarea.scrollHeight + 2 + 'px';
});
复制代码

然而,当内容高度缩减时,输入框的高度并没有跟随缩减。

输入框的高度有误

由于根据scrollHeight设置的元素高度的存在,即使内容高度缩减,此时scrollHeight也不会低于元素高度。所以,在做自适应高度缩减时就无法直接通过同步scrollHeight来实现,而是要先清掉高度样式:

$textarea.addEventListener('input', function() {
    // 清除原来高度
    $textarea.style.height = '';

    $textarea.style.height = $textarea.scrollHeight + 2 + 'px';
});
复制代码

实现后发现,输入到临近换行处,内容高度提前增高了。

临界点异常

调试后发现,清掉高度样式后,textarea恢复到原来的高度,此时内容超过textarea高度,因此会出现滚动条。滚动条会占据一定的空间,导致一行能容纳的字符减少,于是就提前换行了(如下图所示)。而因为在清理高度样式后,又立刻把高度设为新的scrollHeight,所以在界面上没有体现出现。

原因

要解决这个问题,只需要把滚动条隐藏掉。

textarea {
    overflow: hidden;
}
复制代码

虽然功能是做出来了,但是性能上还有优化的余地。因为当前的做法,相当于每次输入都要同步高度。如果高度没有发生变化,这个同步操作是没有意义的。所以,优化的思路就在于如何检查内容高度是否发生了变化:

  • 内容增加时,scrollHeight有可能会发生变化,所以可以记录上一次的scrollHeight,并与当前的scrollHeight对比,有变化时才设置高度样式。
  • 内容减少时,没有有效的方式可以知道内容高度是否有变更(scrollHeight不会减少),所以这种情况目前无法优化。

实现代码如下:

var $textarea = document.getElementById('textarea');
var lastLength = 0;
var lastHeight = 0;

$textarea.addEventListener('input', function() {
    var currentLength = $textarea.value.length;

    // 判断字数如果比之前少了,说明内容正在减少,需要清除高度样式,重新获取
    if (currentLength < lastLength) {
        $textarea.style.height = '';
    }

    var currentHeight = $textarea.scrollHeight;

    // 如果内容高度发生了变化,再去设置高度值
    if (lastHeight !== currentHeight || !$textarea.style.height) {
        $textarea.style.height = currentHeight + 2 + 'px';
    }

    lastLength = currentLength;
    lastHeight = currentHeight;
});
自己写的一个,有点bug,聚焦的时候输入框没清空

<div class="list-group-textarea" id="groupAreaEle" :value="desc" placeholder="填写聚会介绍,详细介绍主题、费用、流程等" v-on:input="onInputDesc  | debounce 500" contenteditable="true"></div>
<div class="list-blank"></div>
<span class="counter" v-html="teatareaWordCount"></span>

.list-group-textarea {
        100%;
        min-height:shift(138);
        font-size:shift(26);
        font-weight:400;
        color:rgba(50,51,51,1);
        line-height:shift(37);
        padding-top:shift(27);
        background:rgba(249,249,249,1);
        outline: 0 none;
        overflow:hidden;
        box-sizing:border-box;
        -webkit-user-select:text;
        -webkit-user-select:auto;
        &:empty:before {
            content: attr(placeholder);
            letter-spacing: 0;
            text-align: justify;
            font-size:shift(26);
            font-weight:400;
            color:rgba(205,205,205,1);
        }
        &:focus {
            content: none;
        }
    }
    .list-blank{
        100%;
        height:shift(102);
        margin:0;
        padding:0;
    }
    .counter {
        position: absolute;
        right: 0;
        font-size: shift(24);
        line-height: shift(33);
        color: #aaaaaa;
        font-weight:400;
    }
原文地址:https://www.cnblogs.com/sybboy/p/9493572.html