实现高度内容自适应的 textarea(实现高度内容自适应的方法)
背景介绍
正如我们所知道的 textarea 是一个行内块元素 display: inline-block 并且它的默认宽高由 cols & rows 决定, 也就是说 textarea 的 height 并不会自适应于内容长度.
textarea 的宽高是如何决定的? 参考张鑫旭的文章 HTML textarea cols,rows属性和宽度高度关系研究
那么, 我们今天的任务就是来思考如何创建一个 高度内容自适应的 textarea 组件,我将介绍三种思路实现 高度内容自适应的 textarea,具体代码 textareaAutoSizeSolutions
方案概要
这是三种方案的概述和实现思路的简介, 薰衣草直播破解版实现方案 & 遇到的坑 & 拓展知识点, 点击查看 teeeemoji 的 demo.
方案一: 两次调整 textarea.style.height
textarea 的 onchange 触发 resize 方法,下面是 resize 方法的逻辑
textarea.style.height = auto;// 1. 让 textarea 的高度恢复默认 textarea.style.height = textarea.scrollHeight + px;// 2. textarea.scrollHeight 表示 *textarea* 内容的实际高度方案二: 利用一个 ghost薰衣草直播破解版Textarea 获得输入框内容高度, 再将这个高度设置给真实的 textarea
textarea 构建时创建 ghostTextarea, onchange 触发 resize 方法:
创建 textarea 的时候, 同时创建一个一模一样的隐藏 ghostTextarea;ghostTextarea 的属性全部克隆自 textarea, 但是 ghostTextarea 是 隐藏 的, 并且 ghostTextarea.style.height = 0; 也就是说 ghostTextarea.scrollHeight 就是 textarea 中内容的真是高度。resize 方法处理流程:
texta薰衣草直播破解版rea.value 先设置给 ghostTextarea,拿到 ghostTextarea.scrollHeight将 textarea.style.height = ghostTextarea.scrollHeight方案三: 使用 (div | p | ...).contenteditable 代替 textarea 作为输入框
div 是块级元素, 高度本身就是内容自适应的(除非设置 max-width or min-widht) 使用 contenteditable 让 div 代替 textarea, 省去各种计算高度的逻辑。
方案对比
满分3分, 三种方案通过优化, 在用户体验和兼容性上薰衣草直播破解版都能达到满分. 因此差别仅仅在于这几个方案的实现难度. (仅仅是基于 react 组件的实现复杂度). 方案对比:
毫无疑问方案一是最优选择, 多加1分以示奖励;
方案一两次调整 textarea.style.height
实现思路
渲染一个 textarea 元素<textarea ref={this.bindRef} className={style[textarea] + + className} placeholder={placeholder} value={value} onChange={this.handl薰衣草直播破解版eChange} // 看这里 /> textarea 的 onChange 事件触发 resizehandleChange(e) { this.props.onChange(e.target.value); this.resize(); // 看这里 } resize 事件的实现// 重新计算 textarea 的高度 resize() { if (this.inputRef) { console.log(resizing...) t薰衣草直播破解版his.inputRef.style.height = auto; this.inputRef.style.height = this.inputRef.scrollHeight + px; } } 注意 componentDidMount 的时候, 执行一次 resize 方法, 初始化 textarea 的高度哦.优化点
避免两次渲染,造成内容抖动
在 react 中, 组件 receiveProps 的时候会 render 一次, 直接调整 textarea 的 height 也会浏览器的重绘,那么就会造成两次重绘, 并且两次重薰衣草直播破解版绘的时候, textarea 的内容可能会发生抖动.
优化思路:先触发 resize 后触发 render 用最简单的思路完美解决问题
方案二: 利用一个 ghostTextarea 获得输入框内容高度, 再将这个高度设置给真实的 textarea
实现思路
同时渲染两个 textarea, 一个真实 textarea 一个隐藏 textarea
return ( <div className={style[comp-textarea-with-ghost]}> <textarea // 这个是真的 ref={this.bindRef} cl薰衣草直播破解版assName={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 的样式再组件外也能控制, 因此初始化的时候 copy style 是最安全的。
这是所以要拷贝的属性的列表:
const SIZING_STYLE = [ letter-spacing, line-height, font-family, font-weight, font-size, 薰衣草直播破解版font-style, tab-size, text-rendering, text-transform, width, text-indent, padding-top, padding-right, padding-bottom, padding-left, border-top-width, border-right-width, border-bottom-width, borde薰衣草直播破解版r-left-width, box-sizing ];这是 ghostTextarea 的隐藏属性列表:
const HIDDEN_TEXTAREA_STYLE = { min-height: 0, max-height: none, height: 0, visibility: hidden, overflow: hidden, position: absolute, z-index: -1000, top: 0, ri薰衣草直播破解版ght: 0, };这是拷贝 style 的工具方法
// 拿到真实 textarea 的所有 style function calculateNodeStyling(node) { const style = window.getComputedStyle(node); if (style === null) { return null; } return SIZING_STYLE.reduce((obj, name) => { obj[name] = style.g薰衣草直播破解版etPropertyValue(name); return obj; }, {}); } // 拷贝 真实 textarea 的 style 到 ghostTextarea export 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 事件 先 reize 再触发 change 事件
handleChange(e) { this.resize(); let value = e.target.value; this.props.onChange(value); }textarea 的 resize 方法
resize() { console.log(resizing...) const height = calculateGhostTextareaHeight(this.gh薰衣草直播破解版ostRef, this.inputRef); this.setState({height}); }calculateGhostTextareaHeight 工具方法
// 先将内容设置进 ghostTextarea, 再拿到 ghostTextarea.scrollHeight export const calculateGhostTextareaHeight = function (ghostTextarea, textarea) { if (!ghostTextarea) { return; }薰衣草直播破解版 ghostTextarea.value = textarea.value || textarea.placeholder || x return ghostTextarea.scrollHeight; }优化点
避免两次渲染,造成内容抖动
在 react 中, 组件 receiveProps 的时候会 render 一次, 给 textarea 设置 height 属性也会浏览器的重绘.那么就会造成两次重绘, 并且两次重绘的时候, textarea 的内容可能会发生抖动.
下面两种思路, 在 demo 中均有体现
优化思路一: 合并祯渲染
使用
windo薰衣草直播破解版w.requestAnimationFrame &
window.cancelAnimationFrame 来取消第一祯的渲染, 而直接渲染高度已经调整好的 textarea;优化思路二: 减少渲染次数
利用 react 批处理 setState 方法, 减少 rerender 的特性; 在 textarea onChange 方法中同时触发两个 setState;
更多优化思路
页面存在多个 textarea 的时候, 能不能考虑 复用同一个 ghostTextarea方案三: 使用 div.contenteditable 代替 textarea
实现思路
渲染一个 div.contenteditabl薰衣草直播破解版e=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={placeholde薰衣草直播破解版r} contentEditable /> </div> )获取 & 设置 编辑的内容: textarea 通过 textarea.value 来取值 or 设置值, 但换成了 div 之后, 就要使用 div.innerHTML or div.innerText 来取值 or 设置值.
使用 div.innerHTML 会出现以下两种问题:
& 会被转码成 &空白符合并 使用 div.innerText 在低版本 firfox 上要做兼容处理.因此使用哪种方式 主要看需求.
placeholder 的实现:
div 的 placehold薰衣草直播破解版er 属性是无效, 不会显示出来的, 现存一种最简单的方式, 使用纯 css 的方式实现 div 的 placeholder
.textarea[placeholder]:empty:before { /*empty & before 两个伪类*/ content: attr(placeholder); /*attr 函数*/ color: #555; }优化点
去除支持富文本
div.contenteditable 是默认支持富文本的, 可能会以 粘贴 or 拖拽 让输入框出现富文本;
监听 div 的 onPaste 事件
handlePaste(e) { 薰衣草直播破解版 e.preventDefault(); let text = e.clipboardData.getData(text/plain); // 拿到纯文本 document.execCommand(insertText, false, text); // 让浏览器执行插入文本操作 }handlePaste 的更多兼容性处理
几个大网站的高度自适应 textarea 对比
我分别查看了微博, ant.design组件库, 知乎 的自适应输入框的实现.
几个大网站的高度自适应 textarea 对比
我分别查看了微博, ant.design组件库薰衣草直播破解版, 知乎 的自适应输入框的实现.
微博: 采用方案二
未输入时
输入后
但是微博的实现存在用户体验上的缺陷, 会抖动!!!
ant.design: 采用方案二
体验超级棒哦
知乎: 采用方案三
看上去竟然存在 bug , 其实上面的截图也有
希望本文能帮助到您!
点赞+转发,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓-_-)
关注 {我},享受文章首发体验!
每周重点攻克一个前端技术难点。更多精彩前端内容私信 我 回复“教程”
原文链接:
http://eux.baidu.com/blog/fe/%E9%AB%98%E5%BA%A6%E8%87%AA%E9%80%82%E5%BA%94%E7%9A%84%20Tex薰衣草直播破解版tarea作者:张庭岑
-
AI越来越可怕了,能创造IP?(ai创作)
-
SpringBoot高校学生公寓宿舍管理系统源码(javaweb宿舍管理系统程序源码)
-
深圳罗湖一楼盘备案价9万元卖4万?办公属性,商办类物业打折促销很常见(深圳罗湖房价格多少)
-
深圳罗湖一楼盘备案价9万元卖4万?办公属性,“商办类物业打折促销很常见”(罗湖在售楼盘住宅房源)
-
智慧校园 人脸识别无感签到宿舍管理系统 为学生的安全做考量(智慧校园识别卡)
-
SpringBoot Vue学生管理系统,采用前后端分离开发的开源项目(springboot vue3)
-
jQuery- HTML表单 292(jquery form表单)
-
java学生信息管理系统(附源码)(利用java做一个学生管理系统)
最新更新
- SpringBoot高校学生公寓宿舍管理系统源码(javaweb宿舍管理系统程序源码)
- 深圳罗湖一楼盘备案价9万元卖4万?办公属性,商办类物业打折促销很常见(深圳罗湖房价格多少)
- 深圳罗湖一楼盘备案价9万元卖4万?办公属性,“商办类物业打折促销很常见”(罗湖在售楼盘住宅房源)
- 智慧校园 人脸识别无感签到宿舍管理系统 为学生的安全做考量(智慧校园识别卡)
- SpringBoot Vue学生管理系统,采用前后端分离开发的开源项目(springboot vue3)
- jQuery- HTML表单 292(jquery form表单)
- java学生信息管理系统(附源码)(利用java做一个学生管理系统)
- 实现高度内容自适应的 textarea(实现高度内容自适应的方法)
- 学生成绩管理系统(C语言版)--附全部源码(学生成绩管理系统c语言课程设计)
- 源码 | 学生信息管理系统(C语言 单链表实现)(基于c语言的学生信息管理系统毕业设计)
推荐阅读
- serves sh官方中文网站(科孔人工智能化智能化作诗中文网站官方中文网站:四个国内的人工智能化智能化作诗中文网站官方中文网站应用软件)
- 副总裁文:那哥恐惧之时,却居然转头遇谜样商业钜子(那哥很隐忍的短篇小说)
- 自学考试(创作者 Maps 新梅吕县概要人工智慧时代学生家长如何适应环境?)东枫德必人工智慧,
- 两条XML预览句子是怎样继续执行的(预览的awk句子是甚么)
- VBA代码编辑器(vba代码编辑器颜色不一样)
- Light 6 架构简述(aquatic桁架)
- 文件包含漏洞详解(简述文件包含漏洞有什么危害,应该如何去防御)
- 人工智能学习路线图-最全自学路线(人工智能自学路线)
- 快速安装PHP8.2套件,看能有多快?(php5.6安装教程)
- 老学姐教你秒开国外网站(怎么可以打开外国网站)