一個實現@人效果的vue3元件

語言: CN / TW / HK

最近剛好有個需求需要實現在輸入框裡@人的效果,看到之前有人開發了 at.js,不過這個依賴jquery,對於目前的前端來說,很少會用到jquery了。因為我們專案用的是vue,也剛好找到了個vue-at,但這個元件其實實現的有點小問題,在使用者用快捷鍵撤銷的時候會無法撤銷插入的標籤。研究了一下,發現是因為他底層實現,是直接使用了dom元素的插入,而瀏覽器不認為這是使用者操作的,所以是無法撤銷的,如果我們需要可以撤銷的效果,是需要使用document.execCommand這個api來實現相關操作。

目前元件已釋出,歡迎學習和使用:https://github.com/SHISME/vue3-at

下面分享一下開發這個元件的關鍵點:

關鍵功能點

image.png

首先確認這個元件的一些關鍵的互動:

  • 當輸入@時候展示對應的列表,並根據@後面的關鍵字進行匹配
  • 選中列表中的人後,將選中的人插入到輸入框裡並高亮
  • 刪除的時候高亮模組能夠整塊刪除

關鍵功能點實現思路

首先普通的輸入框肯定是無法滿足我們高亮輸入項這個需求點的,而html也剛好提供了 contenteditable 這個屬性,我們可以看到市面上很多複雜的富文字編輯器都是用這個屬性來實現的。

如何響應@的輸入

監聽輸入的方式無外乎兩種,監聽keydown或者input,這裡其實用這兩個事件都行,但考慮到input是實打實的輸入,所以選用了監聽input的方式來實現。

  1. 當我們監聽到input事件時,可以通過 document.getSelection 這個api來拿到當前游標的位置,然後我們可以拿到游標前面的內容。 javascript const selection = window.getSelection(); if (selection && selection.rangeCount > 0) { const range = selection.getRangeAt(0); // range物件,能夠獲取到游標的一些相關資訊 }
  2. 拿到內容後,我們只需要截取出@關鍵字後面的內容,這部分內容就是我們需要匹配的內容了。
  3. 在匹配到內容後,將對應的列表展示給使用者選擇

如何將選中項高亮插入到模組之中

可以通過 document.execCommand('insertHTML', false, '<span>something</span>') 來實現插入html的能力,使用這個api插入的內容是支援撤銷的。不過插入之前我們需要將 @ 和@後面輸入的內容給移除掉,但其實這一步只需要我們,通過range物件將這部分內容選中後,在執行 document.execCommand('insertHTML', false, '') 瀏覽器就會把這部分內容給替換掉了,我們也不用單獨做刪除的操作了。

刪除整個高亮模組

這個之前本來想著是將插入的高亮模組的 contenteditable 設定為false,這樣既滿足了,整個模組整體刪除,也滿足了對插入模組無法二次編輯這個需求。但是這樣處理也會造成一個問題,就是使用者用快捷鍵做撤銷操作的時候,也是無法撤銷插入這個操作的,具體原因暫時沒找到為啥。

最後就用了監聽keydown的方式來實現這個功能,實現的方式就是,在監聽到按下刪除鍵的時候,判斷一下游標的位置,如果游標在高亮模組裡面或者高亮模組的後面的時候就刪除整個高亮模組。但實際開發的時候會發現,只要游標緊貼著高亮模組,無論是在游標的最前面或者最後面,range.endContainer都會是高亮模組的dom,所以就需要藉助range.endOffset來判斷游標是在高亮模組的哪裡,如果實在高亮模組的裡面或者後面,則用range選中整個高亮模組的dom,然後執行document.execCommand('delete') 這樣刪除的操作也是可以通過快捷鍵撤銷的,而如果使用dom的remove,則是無法通過快捷鍵撤銷的。

javascript // 把整個tag刪除 range.selectNode(tagContainer); selection.removeAllRanges(); selection.addRange(range); document.execCommand("delete");