插入編輯內容 - daniel-qa/Vue GitHub Wiki

wangEditor 透過 this.savedRange.insertNode(node) 直接操作 DOM。

wangEditor 的編輯區域是一個 contenteditable 的 div,裡面就是普通的 DOM 樹。

savedRange 是指向這個 DOM 樹內某個位置的 Range 物件,所以:

savedRange.insertNode(node0)

等同於直接把 node0 這個 text node 插入到編輯器 DOM 樹的對應位置,編輯器畫面就會即時反映。

整個鏈:

contenteditable div(wangEditor 編輯區)
  └── DOM 樹(p、span、text node...)
        ↑
        savedRange 指向這裡的某個位置
        ↑
        insertNode(node) 把新 text node 插進去
        ↑
        瀏覽器重新渲染 → 畫面出現新字

wangEditor 本身不知道這件事,所以後面才需要 this.editor.change.emit() 通知它內容變了。

  • Ex
// 假設這是在 Vue 組件的方法中,且已經獲取了 editor 實例
insertTextDirectly(text) {
  const editor = this.editor; // wangEditor 實例
  const range = editor.selection.getRange(); // 取得目前的 Range 物件 (savedRange)

  if (!range) return;

  // 1. 建立一個純文字節點 (Text Node)
  const textNode = document.createTextNode(text);

  // 2. 直接操作 DOM:將節點插入到 Range 指向的位置
  // 這一步會讓瀏覽器立即渲染出新文字,但 editor 內部的 html/json 狀態尚未更新
  range.insertNode(textNode);

  // 3. 移動光標到新插入節點的後面,避免連續輸入時位置錯誤
  range.collapse(false);

  // 4. 手動觸發變更通知
  // 讓 wangEditor 執行內部的同步邏輯(例如更新 editor.txt.html() 的回傳值)
  // 並觸發選單高亮狀態檢查等後續動作
  editor.change.emit(); 
}
  • collapse(false):

false 參數代表折疊到 End(終點),也就是新插入節點的後方。

這會讓 Range 變成一個「折疊狀態」的游標(即長度為 0 的點),這樣使用者接下來打字時,字才會順著新節點後面出現。

圖解邏輯

假設編輯器原本內容是 |(游標位置):

insertNode("A"):

內容變為 [A](Range 現在包裹著 A)。

collapse(false):

內容變為 A|(Range 折疊到 A 的後方,準備接受下一個輸入)。

搭配 wangEditor 的完整操作

在 Vibe Coding 流程中,為了確保 UI 狀態同步(例如 iView 組件的響應式顯示),通常會這樣寫:

JavaScript
// 插入節點
range.insertNode(textNode);

// 1. 將 Range 本身折疊到節點後方
range.collapse(false);

// 2. 核心步驟:必須把這個 Range 對象重新同步回瀏覽器的 Selection
const selection = window.getSelection();
selection.removeAllRanges(); // 清除舊選區
selection.addRange(range);    // 放入折疊後的新選區,游標就會閃爍在正確位置

// 3. 通知 wangEditor 更新內部狀態
this.editor.change.emit();

這樣處理後,你在 Windsurf 中開發的自定義功能(如:自動補全、快捷插入變數等)就能擁有跟原生輸入一樣流暢的體驗,不會發生「字插進去了,游標卻跑回行首」的 Bug。