美解決textarea輸入框提示文字,必須添加默認內容
<input/>有placeholder標簽,可以添加提示文字 ,但是<textarea>沒有;所以提出以下解決方案
1.
<textarea id="t" rows="20" cols="20"></textarea> <script> var t = document.getElementById('t'); let aaa = '項目需求概要'; t.innerHTML=aaa; t.onfocus = function(){ if(this.value == aaa){this.value = ''} }; t.onblur = function(){ if(this.value == ''){ this.value = aaa; } }; </script>
2.
<textarea cols="50" rows="5" id="textarea" onfocus="if(value=='限100字'){value=''}" onblur="if (value ==''){value='限100字'}">限100字</textarea>
3.
這你需要把id='note'的div 定位到textarea上面
<div style="position:relative;"> <textarea class="textarea" onfocus="document.getElementById('note').style.display='none'" onblur="if(value=='')document.getElementById('note').style.display='block'"></textarea> <div id="note" class="note"> <font color="#777">項目需求概要</font> </div> </div>
4.
正如我們所知道的 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* 內容的實際高度
方案二: 利用一個 ghostTextarea 獲得輸入框內容高度, 再將這個高度設置給真實的 textarea
textarea 構建時創建 ghostTextarea, onchange 觸發 resize 方法:
resize 方法處理流程:
方案三: 使用 (div | p | ...).contenteditable 代替 textarea 作為輸入框
div 是塊級元素, 高度本身就是內容自適應的(除非設置 max-width or min-widht) 使用 contenteditable 讓 div 代替 textarea, 省去各種計算高度的邏輯。
方案對比
滿分3分, 三種方案通過優化, 在用戶體驗和兼容性上都能達到滿分. 因此差別僅僅在于這幾個方案的實現難度. (僅僅是基于 react 組件的實現復雜度). 方案對比:
毫無疑問方案一是最優選擇, 多加1分以示獎勵;
方案一兩次調整 textarea.style.height
<textarea ref={this.bindRef} className={style['textarea'] + ' ' + className} placeholder={placeholder} value={value} onChange={this.handleChange} // 看這里 />
handleChange(e) { this.props.onChange(e.target.value); this.resize(); // 看這里 }
// 重新計算 textarea 的高度 resize() { if (this.inputRef) { console.log('resizing...') this.inputRef.style.height = 'auto'; this.inputRef.style.height = this.inputRef.scrollHeight + 'px'; } }
避免兩次渲染,造成內容抖動
在 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 的所有 style function 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 到 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.ghostRef, 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 中均有體現
優化思路一: 合并禎渲染
使用 window.requestAnimationFrame & window.cancelAnimationFrame 來取消第一禎的渲染, 而直接渲染高度已經調整好的 textarea;
優化思路二: 減少渲染次數
利用 react 批處理 setState 方法, 減少 rerender 的特性; 在 textarea onChange 方法中同時觸發兩個 setState;
更多優化思路
方案三: 使用 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 會出現以下兩種問題:
因此使用哪種方式 主要看需求.
placeholder 的實現:
div 的 placeholder 屬性是無效, 不會顯示出來的, 現存一種最簡單的方式, 使用純 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%20Textarea
作者:張庭岑
extarea內容某個高度之內自適應,超過指定高度固定,今天講解下實現textarea內容自適應的方法
實現方法如下:
html:
<textarea id="textarea" ></textarea>
css:
javascript:
效果圖:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。