欢迎您访问高等教育自学考试信息服务网平台!

实现高度内容自适应的文本区域

更新时间:2024-09-15 19:10:40作者:匿名

textarea的宽度和高度是如何确定的?参考张鑫旭的文章HTML textarea cols、rows属性与宽高关系研究

所以,我们今天的任务就是思考如何创建一个高度内容自适应的textarea组件。我将介绍实现高度内容自适应文本区域的三个想法。具体代码是textareaAutoSizeSolutions。

方案概要

这是三种解决方案的概述以及实现思路的介绍。实施方案中遇到的坑,拓展了知识点。点击查看teeeemoji 的演示。

解决方案1 : 调整textarea.style.height两次

textarea的onchange触发resize方法。以下是resize方法的逻辑。

textarea.style.height='自动'; //1.让textarea的高度恢复默认textarea.style.height=textarea.scrollHeight + 'px'; //2.textarea.scrollHeight代表*textarea*内容的实际高度方案2: 使用一个ghostTextarea获取输入框的内容高度,然后将这个高度设置为真实的textarea

GhostTextarea在textarea构建时创建,onchange触发resize方法:

创建textarea时,同时创建一个相同的隐藏ghostTextarea; GhostTextarea的所有属性都是从textarea克隆的,但是ghostTextarea是隐藏的,并且ghostTextarea.style.height=0;也就是说,ghostTextarea.scrollHeight是textarea中内容的真实高度。 resize方法处理流程:

Textarea.value首先设置为ghostTextarea。得到ghostTextarea.scrollHeight后,textarea.style.height=GhostTextarea.scrollHeight。解决方案三:使用(div | p | .).contenteditable代替textarea作为输入框。

div 是块级元素,高度本身是内容自适应的(除非设置了max-width 或min-widht)。使用contenteditable将textarea替换为div,消除各种高度计算逻辑。

方案比较

满分3分,三种方案通过优化在用户体验和兼容性方面均能取得满分。因此,区别仅在于这些解决方案的实施难度。 (仅基于react组件的实现复杂度)。解决方案比较:

毫无疑问,选项1是最好的选择,并且会额外加分作为奖励;

计划调整textarea.style.height一次或两次

实现思路

渲染一个textarea元素textarea ref={this.bindRef} className={style['textarea'] + ' ' + className} placeholder={placeholder} value={value} onChange={this.handleChange} //参见Here/textarea的onChange事件触发resizehandleChange(e) { this.props.onChange(e.target.value); this.resize(); //看这里} resize事件的实现//重新计算textarea的高度resize() { if (this.inputRef) { console.log('resizing.') this.inputRef.style.height='auto'; this.inputRef.style.height=this.inputRef.scrollHeight + 'px'; } }注意,在componentDidMount时,执行一次resize方法,初始化textarea的高度。

优化点

避免渲染两次,导致内容抖动

在React 中,组件receiveProps 将渲染一次。直接调整textarea的高度也会导致浏览器重绘,从而导致两次重绘,而在两次重绘期间,textarea的内容可能会出现抖动。

优化思路:先触发resize,再渲染。用最简单的想法完美解决问题。

解决方案2 : 使用ghostTextarea获取输入框的内容高度,然后将这个高度设置为真实的textarea

实施思路

实现高度内容自适应的文本区域

同时渲染两个文本区域,一个真实文本区域和一个隐藏文本区域

return ( div className={style['comp-textarea-with-ghost']} textarea //这是true ref={this.bindRef} className={style['textarea'] + ' ' + className} placeholder={ placeholder} value={value} onChange={this.handleChange} style={{height}}/textarea //这是GhostTextarea className={style['textarea-ghost']} ref={this.bindGhostRef} onChange={ noop} //div) 在初始化期间复制属性。初始化时必须使用工具方法将textarea属性复制到ghostTextarea。因为textarea的样式也可以在组件外部控制,所以初始化时复制样式是最安全的。

这是要复制的属性列表:

const SIZING_STYLE=['字母间距','行高','字体系列','字体粗细','字体大小','字体样式','制表符大小','文本渲染','文本变换','宽度','文本缩进','上边距','右边距','下边距','左边距','上边距宽度', '边框右宽度','边框底部宽度','边框左宽度','盒子大小'];这是ghostTextarea :的隐藏属性列表

const HIDDEN_TEXTAREA_STYLE={'最小高度': '0','最大高度': '无',height: '0',visibility: '隐藏',overflow: '隐藏',position: '绝对','z-index': '-1000',top: '0',right: '0',};这是复制样式的工具方法

//获取真实文本区域的所有样式函数calculateNodeStyling(node) { const style=window.getCompulatedStyle(node);if (style===null) {return null;}return SIZING_STYLE.reduce((obj, name)={ obj [name]=style.getPropertyValue(name);return obj;}, {});}//复制真实textarea的样式到ghostTextareaexport const copyStyle=function (toNode, fromNode) { const nodeStyling=calculateNodeStyling(fromNode) ;if (nodeStyling===null) {return null;}Object.keys(nodeStyling).forEach(key={toNode.style[key]=nodeStyling[key];});Object.keys(HIDDEN_TEXTAREA_STYLE).forEach( key={ toNode.style.setProperty(key,HIDDEN_TEXTAREA_STYLE[key],'important',);});}textarea的onChange事件先reizes,然后触发change事件

handleChange(e) {this.resize();let value=e.target.value;this.props.onChange(value);} textarea 的调整大小方法

resize() {console.log('resizing.')const height=calculateGhostTextareaHeight(this.ghostRef, this.inputRef);this.setState({height});}calculateGhostTextareaHeight 工具方法

//先将内容设置到ghostTextarea中,然后获取ghostTextarea.scrollHeightexport constcalculateGhostTextareaHeight=function (ghostTextarea, textarea) {if (!ghostTextarea) {return;}ghostTextarea.value=textarea.value ||文本区域.placeholder || 'x'return GhostTextarea.scrollHeight;}优化点

避免渲染两次,导致内容抖动

在react中,组件receiveProps会渲染一次,给textarea设置height属性也会导致浏览器重绘。这会导致两次重绘,并且在两次重绘期间,textarea的内容可能会出现抖动。

下面两个想法都在demo中得到了体现

优化思路1 : 合并帧渲染

使用window.requestAnimationFrame window.cancelAnimationFrame取消第一帧的渲染,直接渲染调整了高度的textarea;

优化思路2 : 减少渲染次数

使用react批量setState方法减少重新渲染功能;在textarea的onChange方法中同时触发两个setState;

更多优化思路

当页面上有多个textarea时,是否可以考虑复用同一个ghostTextarea?解决方案3: 使用div.contenteditable 而不是textarea

实施思路

渲染一个div.contenteditable=true

return ( div className={style['comp-div-contenteditable']} div ref={this.bindRef} className={classname(style['textarea'], className, {[style['empty']]: value})} onChange={this.handleChange} onPaste={this.handlePaste} placeholder={placeholder} contentEditable //div) 获取要编辑的内容:textarea使用textarea.value来获取或设置值,但是它是替换为div后,需要使用div.innerHTML或div.innerText来获取或设置值。

使用div.innerHTML会导致以下两个问题:

实现高度内容自适应的文本区域

在低版本的firfox 上会转码为空白并使用div.innerText 进行兼容性处理。所以使用哪种方法主要还是看需求。

占位符:的实现

div的placeholder属性无效,不会显示。目前有最简单的方法是使用纯CSS实现div的占位符。

.textarea[placeholder]:empty:before { /*两个伪类之前为空*/content: attr(placeholder); /*attr函数*/color: #555;}优化点

删除对富文本的支持

div.contenteditable 默认支持富文本。您可以粘贴或拖动使输入框中出现富文本;

监听div的onPaste事件

处理粘贴(e) { e.preventDefault();让文本=e.clipboardData.getData('text/plain'); //获取纯文本document.execCommand('insertText', false, text); //让浏览处理器执行插入文本操作}handlePaste的更多兼容性处理

几个大型网站的高自适应文本区域对比

我分别在微博、ant.design组件库、知乎查看了自适应输入框的实现。

几个大型网站的高自适应文本区域对比

我分别在微博、ant.design组件库、知乎查看了自适应输入框的实现。

微博:采用方案2

未输入时

输入后

但是微博的实现在用户体验上存在缺陷,会抖动!

ant.design:采用方案2

很棒的经历

知乎:采用方案三

看起来有一个错误。其实上面的截图也有。

用户评论

有些人,只适合好奇~

这篇关于实现高度内容自适应的文本区域的博文太实用了!我一直在找这样的解决方案,希望能早点看到这样的技术分享。

    有7位网友表示赞同!

执笔画眉

高度内容自适应的文本区域,听起来好高大上,不知道具体操作起来难不难?

    有20位网友表示赞同!

恰十年

用了那么多自适应布局工具,还是觉得手动控制更靠谱,这篇博文给了我希望。

    有7位网友表示赞同!

呆萌

实现高度内容自适应的文本区域,这是不是意味着我可以不用再担心页面排版问题了?

    有8位网友表示赞同!

花花世界总是那么虚伪﹌

高度内容自适应的文本区域,听起来好像可以解决我写文章时排版乱码的问题。

    有7位网友表示赞同!

孤败

看了这篇博文,我才发现自己之前用的那些自适应布局方法都是小儿科,哈哈。

    有12位网友表示赞同!

Edinburgh°南空

实现高度内容自适应的文本区域,这是否意味着我可以摆脱那些繁琐的CSS代码了?

    有6位网友表示赞同!

棃海

高度内容自适应的文本区域,听起来好复杂,不知道自己能不能学会。

    有15位网友表示赞同!

心亡则人忘

这篇文章介绍的方法好专业,我得好好研究研究,看看能不能应用到我的项目中。

    有20位网友表示赞同!

有一种中毒叫上瘾成咆哮i

实现高度内容自适应的文本区域,这对我来说是个福音,终于不用再为排版头疼了。

    有20位网友表示赞同!

葵雨

看了这篇博文,我觉得高度内容自适应的文本区域其实也没有那么难,关键是掌握方法。

    有6位网友表示赞同!

放肆丶小侽人

实现高度内容自适应的文本区域,这个功能对我来说太重要了,希望博主能详细讲解一下。

    有16位网友表示赞同!

反正是我

这篇博文让我对高度内容自适应的文本区域有了新的认识,感谢分享。

    有10位网友表示赞同!

江山策

高度内容自适应的文本区域,听起来好像可以解决很多网页设计中的问题,期待能学到更多。

    有5位网友表示赞同!

墨染天下

实现高度内容自适应的文本区域,这是不是意味着我可以用更少的代码实现更好的效果?

    有16位网友表示赞同!

陌離

这篇文章太棒了,高度内容自适应的文本区域解决了我一直以来的痛点,感谢分享。

    有9位网友表示赞同!

太易動情也是罪名

实现高度内容自适应的文本区域,这个技术听起来好高大上,我得好好研究一下。

    有5位网友表示赞同!

箜篌引

看了这篇博文,我对高度内容自适应的文本区域有了更深入的了解,感觉自己离专业又近了一步。

    有17位网友表示赞同!

残留の笑颜

实现高度内容自适应的文本区域,这个功能太实用了,希望博主能分享更多相关技巧。

    有12位网友表示赞同!

为您推荐

....