使用 textarea 當輸入框時的各種問題
只是單純想要實現使用 <textarea></textarea> 當作輸入框,並以 Shift+Enter 換行、Enter 或 Ctrl+Enter 送出,最後把送出的訊息自動顯示在畫面上。
但希望能避免在訊息框中只有空白或換行的時候被送出。
我沒想到只是這麼單純的事情也會搞得困難重重QQ(一部分是我忘記有很好用的方法可以用……)
牽涉到畫面處理的部分好像都特別複雜……越來越覺得前端好厲害好辛苦了。
為了怕自己下次又遇到同樣需求卻找不到參考資料(甚至關鍵字都不確定要怎麼下比較好),趕緊趁還沒把參考網頁都關掉的時候先整理一篇文。
今天會提到的內容:
- 使用 Ctrl + Enter 送出
- 使用 Ctrl + Enter 或 Enter 送出
- 避免送出空白內容
- 呈現輸入內容的方法
使用 <textarea> 代替 <input> 當作文本輸入框的理由,通常是因為有字數較多或換行的需求。常用於表單的描述欄位或是通訊軟體的輸入框上。
在輸入時,預設在按下 Enter 或 Shift+Enter 時可以換行,而今天我們希望讓 Ctrl+Enter 可以送出訊息,該怎麼做呢?
使用 Ctrl + Enter 送出
1 | const textarea = document.querySelector(#textarea) |
- 給 textara 的 DOM 加上事件監聽器,監聽
keyDown事件
這會在按鍵按下時觸發,若按住不放,會持續觸發 - 當按住 Ctrl 鍵時,回傳的事件中
ctrlKey = true
按下 Enter 時,keyCode = 13 - 呼叫傳送表單的函式
在開頭的時候有提到,除了 Ctrl+Enter 也希望能按下 Enter 就能發送。
於是我做了這樣的改寫:
1 | const textarea = document.querySelector('#textarea') |
把 e.ctrlKey 改成 !e.shiftKey
前面加上 ! 是 not 的意思,這一段是指在不按下 Shift 鍵的情況下按下 Enter 就會觸發事件。
雖然邏輯上,限定 Ctrl+Enter 才能觸發,和除了 Shift+Enter 以外的狀況按下 Enter 會觸發是不同意義。
但我主要是想保留 Shift+Enter 的換行功能,所以我就這樣寫了。
如果想要寫成 if( (e.ctrlKey && e.keyCode === 13) || e.keyCode === 13 ) 也是可以的。
而按照上面那段程式碼寫後,發生了其他問題:
我的需求是按下送出後,頁面不會跳轉,填入內容出現在同頁上並清空輸入框。
我在程式碼中加上了 textarea.value = '' 的內容, Ctrl+Enter 有正常的送出並清空,但按下 Enter 的時候,輸入框卻自動換行了。
這是原本按下 Enter 或按下 Shift+Enter 就有的效果,所以我加上 preventDefault(),這個能取消事件預設行為的語法,就解決這件事了。
使用 Ctrl + Enter 或 Enter 送出
1 | const textarea = document.querySelector('#textarea') |
現在能正常送出了。
接下來想要完成的需求是希望能避免內容空白的狀況下被送出。
要如何得知輸入框裡頭只有空白和空行呢?
一個簡單的判斷方法如下:
1 | if (textarea.value.trim() === '') return |
trim() 是 JavaScript 字串的一個方法,可以移除字串開頭和結尾的空白字元,包含空格、換行等。
在移除空白後如果剩下空字串,就不做任何事。
避免送出空白內容
1 | const textarea = document.querySelector('#textarea') |
除了移除前後空白外,如果希望取得的內容保留文字前的空白只移除最後的空行,可以使用 trimEnd()。
相對的,想移除開頭空白,可以使用 trimStart()。
我們現在能順利送出訊息,也能避免送出空白內容。
但當我們想顯示送出的內文˙的時候,卻又發現了新的問題。
明明在 textarea 中有輸入換行的,怎麼送出後變成一個個空白了呢?
這是因為 textarea 中的換行是 \r\n,而 HTML 中的換行為 <br>。
保留 textarea 中的空白與換行
1. 將 \r\n 取代為 <br>
1 | textarea.value = textarea.value.replace(/\r\n/g, '<br>') |
/\r\n/g 為正則表達式,正則表達式會用兩個 / 包住條件,尾巴的 g 代表的是所有搜尋到的該條件內容(不加的話找到第一項就會停止了),在這裡就是將符合 \r\n 的字符通通取代為 <br>。
在這之後,使用 textarea.innerHtml 來將內容顯示在網頁中。
這個方法看似最直覺方便,但這會有 XSS 漏洞的問題。
被輸入在輸入框的字串如果被當作 html 文本直接傳入網頁會相當危險。
會需要先把可能造成危險的字串轉義成對應的字符(如將 < 轉換為 < 等),最後再替換 \r\n。
可以搜尋關鍵字「htmlEscape XSS」看看。
2. 加上 <pre></pre>
推薦此做法!
把要顯示內容的地方用 pre 包起來,便能完整顯示原先在輸入框中的空格與排版等。
需要注意的部分是,沒有做任何處理的情況下,字串長度超過版面寬度也不會自動顯示換行,而是直接突破他的框框長到視窗外面去。
想要讓他符合版面寬度換行的話,要修改他的 CSS ,新增 white-space: pre-wrap; 或 white-space: pre-line;,前者會同時保留空格和換行,後者則會把多餘的空白合併,並保留換行。
而 pre 本身也有預設的一些樣式(如上下 margin 等),會需要進行額外的 CSS 調整。
如果覺得還要特地為 pre 調整樣式太麻煩,還有最後一個方法:
3. 在渲染標籤的 style 中加上 white-space
其實和 2. 的效果相同。
被加上 white-space 屬性的元素會決定如何顯示空白與換行符:
1 | white-space: pre-wrap; //保留所有連續的空白字元,換行會發生在有換行符、<br> 或是被文字空間限制的時候 |
所以其實如果希望顯示的文字能配合版面的話,只要加上 white-space 樣式就可以了,並不一定要使用 <pre>。