編寫自適應高度的 textarea

文本框是很常見的輸入控件,我相信只要寫過表單的確定接觸過 textarea 這個元素。html

OK。可是如今產品經理說了:須要這個文本框能夠根據用戶輸入內容自適應其高度。vue

height: auto

有些初學者可能會想:自適應高度不就是 height: auto 麼?但是你想一下,一個 textarea 沒有手工給它指定過樣式,不該該就默認是 height: auto 麼?可是它仍是有本身的初始高度,並無像一個 div 那樣高度爲 0。瀏覽器

div 不一樣,textarea 的默認高度不是根據其內容自適應,而是由屬性 rows 指定,其默認值是 2rows 這個屬性(Attribute)只接受正整數,指定其餘值瀏覽器會忽略掉其值,好比你寫 rows="auto" 那麼 rows 就是 2,rows="0" 也是 2。框架

clipboard.png

因此指定 height: auto 是行不通的,height 屬性必須人工指定其值。性能

scrollHeight

遇到過這個問題的同窗(好比當初的筆者),確定想到過 scrollHeight 這個 DOM 屬性。想法很簡單,當用戶輸入的文本超過了文本框自身高度時不是會出現滾動條嘛,那麼天然而然就能想到 scrollHeight 這個屬性。scrollHeight 就應該是用戶輸入文本的真實高度,至少超過文本框既定高度時是這樣。字體

那麼問題來了:若是沒超過呢?this

OK 我知道你會先指定 rows="1" 讓文本框默認高度只有一行。可是考慮這種狀況:用戶先輸入了不少行文本spa

clipboard.png

而後刪除了一段:code

clipboard.png

scrollHeight 值沒有變化。MDN 上說了:htm

沒有垂直滾動條的狀況下,scrollHeight值與元素視圖填充全部內容所須要的最小值clientHeight相同

scrollHeight 確實會隨着用戶輸入內容多少而增減,可是僅限於出現滾動條的狀況,對於題設這個狀況必然不適用,由於需求就是不能出現滾動條(嚴格來講是超出可視區域)。你能夠在獲取 scrollHeight 的值以前先把文本框高度設爲 0 強制讓滾動條出現,可是這樣可能使頁面發生閃爍,並且性能也低。

split('\n')

DOM 屬性靠不住,那我本身算文本高度不行嗎?說我拿出全部文本,按換行符拆分,看有多少行,行數 * 行高 不就是最終文本高度嗎?

額。。。當文本沒有折行的狀況下是這樣。。。

contenteditable

contenteditable 確實是一個(相對)可行的方案,可是做爲一個踩過坑的先行者勸解你:不到萬不得已,contenteditable 不要碰。這個玩意各個瀏覽器實現都不同,各類奇葩行爲,光一個換行符就足夠折磨你半天。

固然這裏尚未到那麼複雜的地步,可是你得先會把「複製——粘貼」過去的樣式去掉才行

這段話是直接從MDN上覆制的,contenteditable會貼心的把樣式也給你放進去

筆者的方法

說了那麼多廢話,那麼究竟該怎麼辦呢?這裏筆者提供一種方法。

固然首先聲明:筆者的方法未必是最簡單的,若有其它更簡單的方案歡迎留言提出。

咱們想一下,textarea 不能按照內容自適應高度,div 能夠啊,能不能先把文本填到一個 div 裏,div 的高度就應該是文本框所需高度(當 paddingline-height 等樣式都一致的狀況下),這時獲取 div 的高度賦值給文本框高度不就好了嗎。

就是這樣的思路。咱們也不須要專門使用 JS 獲取,只要讓 div 把父元素撐起來,絕對定位 textarea 元素讓文本框佔滿整個父元素大小就行了。

直接上代碼:

<style>
  #parent {
    width: 500px;
    font: 12px monospace;
    position: relative;
  }
  #dummy {
    padding: 2px;
    border: 1px solid;
    visibility: hidden;
  }
  #dummy::after {
    content: "\A";
  }
  #textarea {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    resize: none;
    width: 100%;
    font: inherit;
  }
</style>
<div id="parent">
  <div id="dummy"></div>
  <textarea id="textarea"
            oninput="document.getElementById('dummy').textContent = this.value"></textarea>
</div>

這裏查看運行結果:https://codepen.io/CarterLi/p...

三個要點:

  1. 字體相關樣式 #dummytextarea 兩元素必須徹底一致,差一點就可能出現二者高度對不上的狀況。
  2. #dummywhite-space: pre-wrap 醒目。不然會出現 HTML 中吞空格、換行符的狀況。
  3. 就算有了 white-space: pre-wrap,HTML 仍然會吞掉最後的換行符。解決方案是在 #dummy 最後插入一個換行符元素。能夠是 <br />,也能夠是一個僞元素。僞元素中換行符的寫法是 \A(即換行符的 ASCII 碼 10 的十六進制表示。不能寫 \n

代碼中是用 JS 給 #dummy 賦值。項目中若是你用 vuejsangular 等 MVVM 框架,直接把文本框的值綁定到 div 上就好,很是方便。

若是你要限制文本框的最大最小高度,在 #dummy 上直接設置 min-height max-height 便可。

最後說一句:把 textarea 蓋到一個 div 上的作法還能夠簡單的實現文本框的語法高亮,讀者能夠想一想怎麼作。

相關文章
相關標籤/搜索