如何避免 Contenteditable 在按下 Enter 時自動換行

使用 Contenteditable 送出輸入訊息時,我們會監聽 Enter 按鍵來換行,但是這個動作會觸發換行效果,導致送出的訊息多出一個空行,這個問題該怎麼解決呢?

綁定事件

在進入正題前,我們先來把事件綁定到要監聽的元素上:

  • 如果是使用 Vue,可以透過 v-on:keydown 來監聽元素,像是 @keydown="handleInput",這個 handleInput 就是用來判定是否要送出內容的
  • 如果是使用 Vanilla JS 🤔,你可以直接用 window.addEventListener('keydown', function) 來綁定與監聽元素

OK,都綁定好監聽的事件之後,我們就可以正式地來處理送出的部分了!

禁止預設行為

這邊的關鍵程式碼就是 e.keyCode === 13 && !e.shiftKey,這行的意思是,如果你只按 Enter 沒按 Shift 的話,會禁止後續的預設動作,並送出內容 submitMsg

反之,按下 Enter 的同時也有 Shift 的話,就會跳離這個 if 的判斷,因此不會送出訊息,但是依然會進行 Enter 本來就會做的預設動作(換行)。

1
2
3
4
5
6
7
8
9
10
11
12
handleInput(e) {
if (e.keyCode === 13 && !e.shiftKey) {
console.log('只有按下 Enter 按鍵');
e.preventDefault();
this.submitMsg();
}
},
submitMsg() {
if (this.msg) {
console.log('送出訊息')
}
}

藉由這個方法就可以完成 Enter 送出,Shift + Enter 換行的功能了!

不過,為什麼這邊要用 Keydown,而不是 Click 或 Keyup 呢?

Keyup vs. Keydown

首先,在動作順序上是 Keydown → Keydown 的事件 → Enter → Keyup → Keyup 的事件。

意思就是如果使用 Keyup,那麼 Enter 所觸發的換行動作,會在 Keyup 之前就執行。因此按下 Enter 之後,游標會先移動到下面一行,而在產生新的一行後 (Enter),才去送出訊息 (Keyup)。

但是這不是我們想要的結果,我們希望 Enter 按鍵只要單純用來送出訊息就好,不需要幫我們多加一行空行。

所以我們要改用「觸發順序最前面」的 Keydown,讓我們在按下按鍵之後,會直接先送出訊息,並且在 Keydown 的事件裡面禁止後續 Enter 將會執行的預設動作(換行),這樣後續執行到 Enter 的時候就不會再換行哩 👍

以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫。