文本框是很常见的输入控件,我相信只要写过表单的确定接触过 textarea 这个元素。html
OK。可是如今产品经理说了:须要这个文本框能够根据用户输入内容自适应其高度。vue
有些初学者可能会想:自适应高度不就是 height: auto
么?但是你想一下,一个 textarea
没有手工给它指定过样式,不该该就默认是 height: auto
么?可是它仍是有本身的初始高度,并无像一个 div
那样高度为 0。浏览器
与 div
不一样,textarea
的默认高度不是根据其内容自适应,而是由属性 rows 指定,其默认值是 2
。rows
这个属性(Attribute)只接受正整数,指定其余值浏览器会忽略掉其值,好比你写 rows="auto"
那么 rows
就是 2,rows="0"
也是 2。框架
因此指定 height: auto
是行不通的,height
属性必须人工指定其值。性能
遇到过这个问题的同窗(好比当初的笔者),确定想到过 scrollHeight
这个 DOM 属性。想法很简单,当用户输入的文本超过了文本框自身高度时不是会出现滚动条嘛,那么天然而然就能想到 scrollHeight 这个属性。scrollHeight
就应该是用户输入文本的真实高度,至少超过文本框既定高度时是这样。字体
那么问题来了:若是没超过呢?this
OK 我知道你会先指定 rows="1"
让文本框默认高度只有一行。可是考虑这种状况:用户先输入了不少行文本spa
而后删除了一段:code
scrollHeight
值没有变化。MDN 上说了:htm
没有垂直滚动条的状况下,scrollHeight值与元素视图填充全部内容所须要的最小值clientHeight相同
scrollHeight
确实会随着用户输入内容多少而增减,可是仅限于出现滚动条的状况,对于题设这个状况必然不适用,由于需求就是不能出现滚动条(严格来讲是超出可视区域)。你能够在获取 scrollHeight
的值以前先把文本框高度设为 0 强制让滚动条出现,可是这样可能使页面发生闪烁,并且性能也低。
DOM 属性靠不住,那我本身算文本高度不行吗?说我拿出全部文本,按换行符拆分,看有多少行,行数 * 行高
不就是最终文本高度吗?
额。。。当文本没有折行的状况下是这样。。。
contenteditable 确实是一个(相对)可行的方案,可是做为一个踩过坑的先行者劝解你:不到万不得已,contenteditable 不要碰。这个玩意各个浏览器实现都不同,各类奇葩行为,光一个换行符就足够折磨你半天。
固然这里尚未到那么复杂的地步,可是你得先会把“复制——粘贴”过去的样式去掉才行
说了那么多废话,那么究竟该怎么办呢?这里笔者提供一种方法。
固然首先声明:笔者的方法未必是最简单的,若有其它更简单的方案欢迎留言提出。
咱们想一下,textarea
不能按照内容自适应高度,div
能够啊,能不能先把文本填到一个 div
里,div
的高度就应该是文本框所需高度(当 padding
、line-height
等样式都一致的状况下),这时获取 div
的高度赋值给文本框高度不就好了吗。
就是这样的思路。咱们也不须要专门使用 JS 获取,只要让 div
把父元素撑起来,绝对定位 textarea
元素让文本框占满整个父元素大小就行了。
直接上代码:
<style> #parent { width: 500px; font: 12px monospace; position: relative; } #dummy { padding: 2px; border: 1px solid; visibility: hidden; } #dummy::after { content: "\A"; } #textarea { position: absolute; left: 0; right: 0; top: 0; bottom: 0; resize: none; width: 100%; font: inherit; } </style> <div id="parent"> <div id="dummy"></div> <textarea id="textarea" oninput="document.getElementById('dummy').textContent = this.value"></textarea> </div>
这里查看运行结果:https://codepen.io/CarterLi/p...
三个要点:
#dummy
和 textarea
两元素必须彻底一致,差一点就可能出现二者高度对不上的状况。#dummy
中 white-space: pre-wrap
醒目。不然会出现 HTML 中吞空格、换行符的状况。white-space: pre-wrap
,HTML 仍然会吞掉最后的换行符。解决方案是在 #dummy
最后插入一个换行符元素。能够是 <br />
,也能够是一个伪元素。伪元素中换行符的写法是 \A
(即换行符的 ASCII 码 10 的十六进制表示。不能写 \n
)代码中是用 JS 给 #dummy
赋值。项目中若是你用 vuejs
或 angular
等 MVVM 框架,直接把文本框的值绑定到 div
上就好,很是方便。
若是你要限制文本框的最大最小高度,在 #dummy
上直接设置 min-height
max-height
便可。
最后说一句:把 textarea
盖到一个 div
上的作法还能够简单的实现文本框的语法高亮,读者能够想一想怎么作。