整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          textarea中提示文本的實現,默認顯示,點擊消失

          美解決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.

          rea 對象

          Area 對象代表圖像映射的一個區域(圖像映射指的是帶有可點擊區域的圖像)

          在 HTML 文檔中 <area> 標簽每出現一次,就會創建一個 Area 對象。


          Area 對象的屬性

          accessKey 設置或返回訪問某個區域的快捷鍵。

          alt 設置或返回當瀏覽器無法顯示某個區域時的替換文字。

          coords 設置或返回圖像映射中可點擊區域的坐標。

          hash 設置或返回某個區域中 URL 的錨部分。

          host 設置或返回某個區域中 URL 的主機名和端口。

          href 設置或返回圖像映射中鏈接的 URL。

          id 設置或返回某個區域的 id。

          noHref 設置或返回某個區域是否應是活動的還是非活動的。

          pathname 設置或返回某個區域中的 URL 的路徑名。

          protocol 設置或返回某個區域中的 URL 的協議。

          search 設置或返回某個區域中 URL 的查詢字符串部分。

          shape 設置或返回圖像映射中某個區域的形狀。

          tabIndex 設置或返回某個區域的 tab 鍵控制次序。

          target 設置或返回在何處打開區域中的 link-URL。

          景介紹

          正如我們所知道的 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 方法:

          1. 創建 textarea 的時候, 同時創建一個一模一樣的隱藏 ghostTextarea;
          2. ghostTextarea 的屬性全部克隆自 textarea, 但是 ghostTextarea 是 隱藏 的, 并且 ghostTextarea.style.height = 0; 也就是說 ghostTextarea.scrollHeight 就是 textarea 中內容的真是高度。

          resize 方法處理流程:

          1. textarea.value 先設置給 ghostTextarea,
          2. 拿到 ghostTextarea.scrollHeight
          3. 將 textarea.style.height = ghostTextarea.scrollHeight

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

          div 是塊級元素, 高度本身就是內容自適應的(除非設置 max-width or min-widht) 使用 contenteditable 讓 div 代替 textarea, 省去各種計算高度的邏輯。

          方案對比

          滿分3分, 三種方案通過優化, 在用戶體驗和兼容性上都能達到滿分. 因此差別僅僅在于這幾個方案的實現難度. (僅僅是基于 react 組件的實現復雜度). 方案對比:

          毫無疑問方案一是最優選擇, 多加1分以示獎勵;

          方案一兩次調整 textarea.style.height

          實現思路

          1. 渲染一個 textarea 元素
          <textarea
           ref={this.bindRef}
           className={style['textarea'] + ' ' + className}
           placeholder={placeholder}
           value={value}
           onChange={this.handleChange} // 看這里
          />
          
          1. textarea 的 onChange 事件觸發 resize
          handleChange(e) {
           this.props.onChange(e.target.value);
           this.resize();	// 看這里
          }
          
          1. resize 事件的實現
          // 重新計算 textarea 的高度
          resize() {
           if (this.inputRef) {
           console.log('resizing...')
           this.inputRef.style.height = 'auto';
           this.inputRef.style.height = this.inputRef.scrollHeight + 'px';
           }
          }
          
          1. 注意 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 的所有 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;

          更多優化思路

          • 頁面存在多個 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 { /*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

          作者:張庭岑


          主站蜘蛛池模板: 国产色综合一区二区三区 | 国产一区二区久久久| 日亚毛片免费乱码不卡一区| 无码成人一区二区| 中文字幕一区二区免费| 狠狠色成人一区二区三区| 国产一区二区视频在线播放| 中文字幕一区二区免费 | 中文字幕一区二区三区在线播放| 美女视频一区二区三区| 无码精品前田一区二区| 久久精品国产亚洲一区二区三区| 国产精品无码一区二区在线观一| 亚洲免费一区二区| 日本高清不卡一区| 中文字幕在线看视频一区二区三区| 亚洲美女视频一区| 日韩一区二区三区射精| 无码一区二区三区亚洲人妻| 美女视频免费看一区二区| 三上悠亚日韩精品一区在线| 国产一区二区三区免费| 亚洲AV无码一区二区三区在线| 亚洲一区二区久久| 国产精品乱码一区二区三| 无码人妻精品一区二| 国产一区二区三区在线2021| 亚洲一区二区三区四区在线观看| 国产精品揄拍一区二区| 国产精品综合一区二区三区| 一区二区三区无码高清| 精品一区二区久久| 亚洲愉拍一区二区三区| 一区二区三区免费高清视频| 精品一区二区三区在线视频| 亚洲乱码av中文一区二区| 亚洲国产高清在线一区二区三区| 亚洲AV无码一区二区三区系列| 欧美亚洲精品一区二区| 国产午夜精品一区二区| 国产欧美一区二区精品仙草咪|