今年早些时候,我写了一些关于 自动增长文本区和输入 的内容。 想法是使 <textarea>
更像 <div>
,以便它根据需要扩展高度以包含当前值。 它几乎很奇怪,没有一个简单的原生解决方案,不是吗? 回顾那篇文章,我的想法都不是特别好。 但 Stephen Shaw 的 想法 我在文章结尾链接到的实际上是一个 *非常* 好的主意,所以我希望能够阐明这一点并讨论它如何工作,因为它似乎是最终答案,直到我们得到一些原生和更好的东西。
如果您只想获得一个可工作的示例,请查看演示
<textarea>
的内容到一个 *可以* 自动扩展高度并匹配其大小的元素中。
技巧是,您需要精确地复制 因此,您有一个 <textarea>
,它 *不能* 自动扩展高度。
相反,您需要 *精确地* 复制该元素的外观、内容和位置到另一个元素中。 您隐藏复制的元素的视觉效果(最好让技术上可用的元素保持可见)。
现在,所有三个元素都相互绑定。 哪个子元素最高,父元素的高度就会被推到该高度,另一个子元素也会随之调整。 这意味着 <textarea>
的最小高度将成为“基础”高度,但如果复制的文本元素碰巧长高了,所有元素都会随之长高。
太聪明了。 我太喜欢了。
您需要确保复制的元素 *完全* 相同
相同的字体、相同的填充、相同的边距、相同的边框……所有内容。 这是一个完全相同的副本,只是使用 visibility: hidden;
在视觉上隐藏了它。 如果它不完全相同,所有内容将不会完全按预期增长。
我们还需要在复制的文本上使用 white-space: pre-wrap;
,因为这就是文本区的工作方式。
这是最奇怪的部分
在我的演示中,我使用的是 ::after
来复制文本。 我不确定这是否是最佳方法。 对我来说感觉很干净,但我想知道使用 <div aria-hidden="true">
对屏幕阅读器是否更安全? 或者 visibility: hidden;
对此是否足够? 无论如何,这不是奇怪的部分。 这是奇怪的部分
content: attr(data-replicated-value) " ";
因为我使用的是伪元素,所以它会将 data
属性从元素中移除,并将内容渲染到页面上 **带有一个额外的空格**(这就是奇怪的部分)。 如果您没有这样做,最终结果会感觉“跳跃”。 我不能说我完全理解它,但它似乎更好地尊重了文本区和文本元素之间的换行行为。
如果您不想使用伪元素,没问题,只是要注意跳跃的行为。
Will Earp 和 Martin Tillmann 向我发来了邮件,提醒我 Shaw 的技巧是多么聪明,他们都在同一天发来邮件。 这是一个示例 Martin 使用 Alpine.js 和 Tailwind 创建的,它最终也像一行代码(但请注意它是如何跳跃的)。
我相信大家可以想象如何使用 Vue 和 React 等等以一种可以轻松维护文本区和其他元素之间状态的方式来做到这一点。 我不会在这里提供示例,部分原因是我懒惰,但主要是因为我认为您应该了解 *如何* 工作。 它会让您更聪明,更好地理解您的网站。
感谢您分享如此创新的方法,文本区在 Firefox 中显示垂直滚动条,因为它变得更高。
我在
<textarea>
上添加了一个overflow: hidden;
。 这似乎适用于所有浏览器,并修复了 Firefox 中的可见滚动条。只是注意到 Jim Nielsen 最近介绍了调整文本区大小
https://blog.jim-nielsen.com/2020/automatically-resize-a-textarea-on-user-input/
不过,它是一个小库,而不是这篇文章中非常少量的 JavaScript 技术。 这是我分叉的他的演示,使用 Skypack 的导入
还要查看他的文章,了解使用 Web Components 的有趣方法。
两年前就做过类似的技巧。
不同之处在于我使用
position: absolute
和left: 0; right: 0; top: 0; bottom: 0
来匹配文本区与其父元素的大小;并使用<br />
来保持换行符这就像浏览器应该实现的东西(可能是在某些属性后面,例如)。
很长一段时间以来,我一直使用 size 属性来自动增长
<input/>
。 您可以使用简单的onchange
监听器,然后将 size 设置为value.length
您可以查看 我代码笔中的演示。
我在 EmberJS 中使用 handlebars 做过类似的事情
嘿,您不能直接使用带有 contenteditable 属性的 div 吗?
也许可以! 原文稍微提到了这一点。 我只是不知道所有关于它的可访问性和 UX 影响。 我怀疑它的行为是否足够相似(当然它不会“正确”提交)。
将高度设置为其滚动高度?
scrollheight + 2px ;)
很酷,看起来很棒。 我想知道如何使用此方法来限制最大行数?
如何只更改 rows 属性的值? 这将动态地尊重行高、字体大小等。
是的,我们仍然需要关注字符数量,但 DOM 中会减少两个元素?
对我来说似乎更难,要准确地知道您需要添加另一行的时刻。 但请尝试!
感谢您提供出色的解决方案,Chris! 对于一个相对刚接触 CSS 网格的人来说,在这种情况下,如何最好地防止网格在将一个很长的“单词”输入文本区时水平增长? 本质上,我希望文本区的“overflow-wrap: break-word”属性生效。
希望这些文章能有所帮助
太棒了,正是我需要的。 我不喜欢使用 jQuery,我喜欢干净的 html、css 和 javascript。
如何更改文本区的初始高度?
@Gellert: 好问题。这个演示很好,但它不能开箱即用地用于已经填充了多行或换行内容的文本区域。因此,您必须在页面加载时执行 JavaScript 的操作:遍历所有文本区域,并将父级的 dataset 值设置为文本区域的值。
parent.dataset.replicatedValue = textarea.value
您能提供一个关于初始高度的示例吗?
如何将初始文本区域设置为 100% 宽度,并限制其增长仅限于行,而不是溢出页面?
基本上,如何让文本区域只在高度上增长,而不是在宽度上增长。我尝试了 overflow-wrap,这是我能想到的唯一方法。
@Mike,
请查看上面 Chris 关于处理长单词和 URL 的评论。
在 CSS 中粘贴该文章中引用的 CSS(减去与连字符相关的那些)。
这似乎解决了文本中没有换行符/空格时换行的问题。(至少在 Chrome 中,没有测试其他任何东西),尽管我认为它只需要应用于 ::after。
@Chris Coyier 有没有理由你的演示中不应将此设置为默认行为?这似乎是大多数人想要的,因为无限地水平增长可能会导致布局中断,而 max-width 可以由放置其中的 div 控制,从而允许一些水平增长(如果需要),而不会让它超出屏幕。
太棒了!非常感谢!!
有没有带占位符的实现?
这太棒了!!
如果文本区域以固定高度开始,比如比默认高度更小的尺寸,会怎样?
这是我的变体
它使用 flexbox 而不是 css 网格,并在文本区域后面有一个隐藏的 DIV。
已在 Chrome、FireFox 和 Safari 上测试
这是一个非常复杂的解决方案,我想出了一个更简单的解决方案。
在“input”事件中,将文本区域的高度更改为“auto”值,然后将其更改为 textarea.scrollHeight + “px”。
请记住在 CSS 中添加以下属性
resize: none;
overflow-y: hidden;
希望对您有所帮助。
我在 Chrome 中快速查看了一下... 似乎没有为我扩展。
对不起,我做错了,现在就可以了。
这个解决方案对我来说在 Chrome 中有效。我喜欢它的简单性。
唯一的问题是,在我开始键入时,文本区域会增长几个像素。
当我改变
textArea.style.height = textArea.scrollHeight + “px”
到
textArea.style.height = textArea.scrollHeight – 4 + “px”
它对我有用。
有谁在其他浏览器中测试过这个解决方案?
这是否是一个适合在生产中使用的解决方案,或者我应该继续使用 https://npmjs.net.cn/package/autosize?
我想用一个简单的解决方案来替换 NPM autosize 包的使用,以减少依赖项数量。
RJ,我意识到你减去的“神奇”数量“-4”是垂直填充。我的文本区域周围有 2px 填充,所以当我完成
textArea.style.height = textArea.scrollHeight – 4 + “px” 解决了。
因此,您必须减去的数值是 pt+pb 的总和。
https://codepen.io/matthias-hermsdorf/embed/PoOVYaY
必要的 height 存储在 textarea.scrollHeight 中。
但要获取它,height 必须设置为 inherit。
在将元素的 height 设置为 inherit 时,某些浏览器会将其设置为
非常小的初始 height 并以这种方式缩短文档主体。
为了防止由于 height 更改为 inherit 而导致的滚动,文本区域被包装在 flexbox 中,并排放置一个不可见的
元素。Heightkepper 尽其所能并设置 flexbox 的 height。
仅仅为了好玩,因为它可能是一个错误,这里有一个在 Chrome 中有效的版本,完全不需要任何 js。
不要使用它!!
我只是觉得它可能很有趣,即使它不能使用。
对于 Safari(iOS),我必须在末尾添加此内容,因为文本区域上有一个固有的 1px 边距
试图在 React 中实现它,而不使用伪元素... 我无法弄清楚如何复制额外“ ”的作用...
嗨!我按照 CSS 的方法做,得到了和演示一样,双行/行高... 我真的想要一行干净的文本,然后换行才会开始自动增长。我怎样才能将初始高度设置为不高于所需的文本行?
谢谢,它有效