請啟用 Javascript 查看內容

CompositionEvent

 ·  ☕ 3 分鐘

這週是六角鼠年鐵人賽第三十四週。

前言

在 Vue 文件的 表單輸入綁定 中有提到這一段:

對於需要使用輸入法 (如中文、日文、韓文等) 的語言,你會發現 v-model 不會在輸入法組合文字過程中得到更新。如果你也想處理這個過程,請使用 input 事件。

這是因為 Vue 有使用到 CompositionEvent,你可以在 v-model原始碼 中看到。

另外,在 Element UI 的 Input 元件 原始碼,你可以看到它的身影

CompositionEvent 簡介

CompositionEvent 複合事件,是用來處理輸入法編輯器(Input Method Editor, IME)的事件,從 DOM Level 3 之後才新增的。

在使用輸入法編輯器時,會根據輸入狀態觸發下事件:

CompositionEvent 會在 keydown 事件之後被觸發。

假設我們不使用 v-model 來雙向綁定輸入框:

1
2
3
<input type="text" :value="text" @input="inputHandler">
<p>內容: {{ text }}</p>
<p>字數: {{ text.length }}</p>
1
2
3
4
5
6
7
8
data: {
  text: '',
},
methods: {
  inputHandler(event) {
    this.text = event.target.value;
  },
}

你就會發現,當你使用注音選字時,會觸發更新:

新增一個 isComposing 狀態來判斷輸入法是否在使用中:

1
2
3
4
data: {
  text: '',
  isComposing: false,
}

接著,我們要監聽 compositonstartcompositionend 事件:

1
2
3
4
5
6
7
8
9
  <input 
    type="text"
    :value="text"
    @input="inputHandler"
    @compositionstart="isComposing = true"
    @compositionend="handleCompositionEnd"
  >
  <p>內容: {{ text }}</p>
  <p>字數: {{ text.length }}</p>
  • compositionstart 觸發,isComposing 設為 true
  • compositionend 觸發,isComposing 設為 false 並呼叫 inputHandler
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
methods: {
  inputHandler(event) {
    if (this.isComposing) { return; }
    this.text = event.target.value;
  },
  handleCompositionEnd(event) {
    if (this.isComposing) {
      this.isComposing = false;
      this.inputHandler(event);
    }
  }
}

isComposing 事件屬性

另外,KeyboardEvent 和 InputEvent 有 KeyboardEvent.isComposingInputEvent.isComposingisComposing 屬性在 compositonstartcompositionend 之間其值為 true

但是 Safari(WebKit)瀏覽器不支援。

應用

使用 CompositionEvent 來判斷輸入法狀態,可以有效避免 input 監聽事件在拼字時一直觸發。

也可以在用在使用 Enter 鍵送出資料時的優化。舉例來說,在某些瀏覽器下,如果使用 @keyup.enter="submit" 送出資料,當我們在選字時所按下 Enter 鍵,就會觸發事件:

因此我們可以加上輸入法狀態判斷避免誤觸:

1
2
3
4
5
6
7
8
9
<input type="text"
  v-model="text"
  @keyup.enter="submit"
  @compositionstart="isComposing = true"
  @compositionend="isComposing = false"
>
<ul>
  <li v-for="item of list">{{ item }}</li>
</ul>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
data: {
  text: '',
  list: [],
  isComposing: false,
},
methods: {
  submit(event) {
    if (this.isComposing) { return }
    this.list.push(this.text);
  }
}

CodePen Demo

等同:

1
2
3
4
5
6
7
<input type="text"
  v-model="text"
  @keyup.enter="submit"
>
<ul>
  <li v-for="item of list">{{ item }}</li>
</ul>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
data: {
  text: '',
  list: [],
},
methods: {
  submit(event) {
    if (event.isComposing) { return }
    this.list.push(this.text);
  }
}

但是 isComposing 事件屬性 Safari(WebKit)瀏覽器不支援。


竹白
作者
竹白
前端筆記

文章目錄