先看东西
思路
<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>
这样就能兼容换行和空格了。完整的代码非常简单,在这里。
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。