React实现自适应高度的<textarea>

2018-06-25

先看东西

Demo


思路

<textarea>,文本区域,在表单中经常要用到。使用rows属性可以定义文本域的行数,但是有时候我们需要让他自定义高度,输入多少行就显示多少行,不要出现滚动条。常见的三种思路如下:


思路一:用JS实时修改textarea的高度

这个是最直观的办法,用jQuery的话来说,就是:

$('textarea').keyup(function () {
  $(this).height(this.scrollHeight);
});

网上搜到不少<textarea>自适应高度的解决方案,其原理都是如此。然而体验并不完美,逻辑也很复杂,要考虑换行、删除换行等诸多操作,实现起来很容易出bug。


思路二:contentEditable

<textarea>自己是没有这样的功能,但是<div>有啊,那咱用contentEditable属性让div的内容可以编辑,不就实现了跟<textarea>一样的功能了吗?代码相当精简,如下:

<div contenteditable></div>

兼容性也很好,参考:https://caniuse.com/#search=contenteditable


思路三:用一个隐藏的DIV把父容器撑开


 非常巧妙的方式。父容器下装着<pre>和<textarea>。<pre>里显示着文本域输入的内容,这样就能根据内容量撑开父容器。再给<textarea>一个绝对定位,搞定。原理如下:

<div class="textarea_wrapper">
  <div class="textarea_content">这里显示文本域的内容,把父容器撑开</div>
  <textarea class="textarea_input"></textarea>
</div>

.textarea_wrapper {
    position: relative;

    .textarea_content {
        position: relative;
        z-index: -1;
        opacity: 0;
        display: block;
        width: 100%;
    }

    .textarea_input {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        width: 100%;
        height: 100%;
        color: #333;
        resize: none;
        overflow: hidden;
    }
}

在jQuery时代,有一个挺好用的库flexText,用的就是这种思路,非常值得借鉴。


实现

在React中实践一下,思路二行不通,因为React文档里明确说过了,别随便用contentEditable,参考suppressContentEditableWarning。于是我们用思路三。


React做双向绑定非常简单,需要注意的是,文本域可以输入空格和换行,而JSX对多个空格会合并为一个空格,换行符也会转化成一个空格。所以我们需要用一个函数对输入内容进行转义:

renderContent (text) {
    let str = text.replace(/\r\n/g, "<br/>")
    str = str.replace(/\n/g, "<br/>")
    str = str.replace(/\s/g," ")
    return {__html: str}
}

然后再使用dangerouslySetInnerHTML来输出转义后的HTML:

<div dangerouslySetInnerHTML={this.renderContent(this.state.content)}></div>

这样就能兼容换行和空格了。完整的代码非常简单,在这里



本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。