您的位置:首页 > 数码常识数码常识

实现高度内容自适应的textarea(页面高度自适应)

2025-05-10人已围观

实现高度内容自适应的textarea(页面高度自适应)
  背景介绍正如我们所知道的 textarea 是一个行内块元素 display: inline-block 并且它的默认宽高由 cols & rows 决定, 也就是说 textarea 的 height 并不会自适应于内容长度.

  页面高度自适应背景介绍

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

  那么, 我们今天的任务就是来思考如何创建一个 高度内容自适应的 textarea 组件,我将介绍三种思路实现 高度内容自适应的 textarea,具体代码 textareaAutoSizeSolutions

  方案概要

  这是三种方案的概述和实现思路的简介, 实现方案 & 遇到的坑 & 拓展知识点, 点击查看 teeeemoji 的 demo.

  方案一: 两次调整 textarea.style.Height</strong>

  textarea 的 onchange 触发 resize 方法,下面是 resize 方法的逻辑

  textarea.style.height='auto';// 1. 让 textarea 的高度恢复默认textarea.style.height=textarea.scrollHeight + 'px';// 2. textarea.scrollHeight 表示 *textarea* 内容的实际高度

  方案二: 利用一个 ghostTextarea 获得输入框内容高度, 再将这个高度设置给真实的 textarea

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

  创建 textarea 的时候, 同时创建一个一模一样的隐藏 ghostTextarea;ghostTextarea 的属性全部克隆自 textarea, 但是 ghostTextarea 是 隐藏 的, 并且 ghostTextarea.style.height=0; 也就是说 ghostTextarea.scrollHeight 就是 textarea 中内容的真是高度。

  resize 方法处理流程:

  textarea.value 先设置给 ghostTextarea,拿到 ghostTextarea.scrollHeight</li>将 textarea.style.height=ghostTextarea.scrollHeight</li>

  方案三: 使用 (div | p | ...).contenteditable 代替 textarea 作为输入框

  div 是块级元素, 高度本身就是内容自适应的(除非设置 max-width or min-widht) 使用 contenteditable 让 div 代替 textarea, 省去各种计算高度的逻辑。

  方案对比

  满分3分, 三种方案通过优化, 在用户体验和兼容性上都能达到满分. 因此差别仅仅在于这几个方案的实现难度. (仅仅是基于 react 组件的实现复杂度). 方案对比:

  毫无疑问方案一是最优选择, 多加1分以示奖励;

  方案一两次调整 textarea.style.Height</strong>

  实现思路渲染一个 textarea 元素

  <textarea ref={this.bindRef} className={style['textarea'] + ' ' + className} placeholder={placeholder} value={value} onChange={this.handleChange} // 看这里/>textarea 的 onChange 事件触发 resize

  handleChange(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 的时候会 render 一次, 直接调整 textarea 的 height 也会浏览器的重绘,那么就会造成两次重绘, 并且两次重绘的时候, textarea 的内容可能会发生抖动.

  优化思路:先触发 resize 后触发 render 用最简单的思路完美解决问题

  方案二: 利用一个 ghostTextarea 获得输入框内容高度, 再将这个高度设置给真实的 textarea

  实现思路

  同时渲染两个 textarea, 一个真实 textarea 一个隐藏 textarea

  return ( <div className={style['comp-textarea-with-ghost']}> <textarea // 这个是真的 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 的样式再组件外也能控制, 因此初始化的时候 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','border-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',right: '0',};

  这是拷贝 style 的工具方法

  // 拿到真实 textarea 的所有 stylefunction calculateNodeStyling(node) {const style=window.getComputedStyle(node);if (style===null) {return null;}return SIZING_STYLE.reduce((obj, name)=> {obj[name]=style.getPropertyValue(name);return obj;}, {});}// 拷贝 真实 textarea 的 style 到 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 事件 先 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.ghostRef, this.inputRef);this.setState({height});}

  calculateGhostTextareaHeight 工具方法

  // 先将内容设置进 ghostTextarea, 再拿到 ghostTextarea.scrollHeightexport 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 中均有体现

  优化思路一: 合并祯渲染

  使用 window.requestAnimationFrame & window.cancelAnimationFrame 来取消第一祯的渲染, 而直接渲染高度已经调整好的 textarea;

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

  利用 react 批处理 setState 方法, 减少 rerender 的特性; 在 textarea onChange 方法中同时触发两个 setState;

  更多优化思路

  页面存在多个 textarea 的时候, 能不能考虑 复用同一个 ghostTextarea

  方案三: 使用 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 来取值 or 设置值, 但换成了 div 之后, 就要使用 div.innerHTML or div.innerText 来取值 or 设置值.

  使用 div.innerHTML 会出现以下两种问题:

  & 会被转码成 &空白符合并 使用 div.innerText 在低版本 firfox 上要做兼容处理.

  因此使用哪种方式 主要看需求.

  placeholder 的实现:

  div 的 placeholder 属性是无效, 不会显示出来的, 现存一种最简单的方式, 使用纯 css 的方式实现 div 的 placeholder

  .textarea[placeholder]:empty:before { content: attr(placeholder); 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 , 其实上面的截图也有

  上面就是小居数码小编今天给大家介绍的关于(页面高度自适应)的全部内容,希望可以帮助到你,想了解更多关于数码知识的问题,欢迎关注我们,并收藏,转发,分享。

  94%的朋友还想知道的:

  css实现三栏布局(css3实现三栏布局,左右固定,中间自适应)

  htmlcss手机端自适应网页(自适应网页设计和响应式网页设计)

  echarts高度自适应(echarts图表自适应div大小)

  如何根据高度选择不同风格?(客厅电视柜多高合适)



  154641
 

很赞哦! ()

随机图文