今天看 Quill Editor 的 Model 技術文件介紹,根據文件的描述,語意版本控制(Semantic Versioning)不適用於實驗性 API,意思是 Model 的 API 目前仍然處於實驗性階段,代表未來可能會出現一些重大的改動而影響到 API 的穩定性,但可以先看過一遍並嘗試玩看看,未來有機會正式發布後,再考慮應用到正式的專案上。

透過 Model API 找到的 Blot 物件是 LinkedList 的資料結構:
LinkedList 的資料結構

find

這是一個靜態方法,可以代入 DOM 節點並回傳 Quill 或 Blot Instance。在後者的情況下,對 bubble 參數傳入 true 會向上尋找目標 DOM 的祖先,直到找到相應的 Blot。

方法:

Quill.find(domNode: Node, bubble: boolean = false): Blot | Quill

範例:

find(quill: Quill, container: HTMLElement) {
  // 帶入 container 尋找並取得 quill instance
  const target = Quill.find(container);
  console.log('target is quill instance', target === quill);

  // 編輯器輸入連結文字並嘗試取得 link node
  quill.insertText(0, 'Hello, World!', 'link', 'https://google.com');
  const linkNode = container.querySelector('a');
  const findLinkNode = Quill.find(linkNode!);
  console.log('linkNode', findLinkNode);
}

getIndex

回傳從文件開頭到帶入的 blot 之間的距離長度。

方法:

getIndex(blot: Blot): Number

範例:

// 預先輸入文字並取得第 10 個字元的 blot
quill.insertText(0, 'Hello, World!');
const [line, offset] = quill.getLine(10);
console.log('line', line);

// 帶入 blot 取得 index
const index = quill.getIndex(line); // index + offset should == 10
console.log('index', index);
console.log('offset', offset);

getLeaf

回傳文件中指定索引處的葉節點。leaf 通常指的是資料結構中的末端節點。

方法:

getLeaf(index: Number): Blot

範例:

quill.setText('Hello Good World!');
quill.formatText(6, 4, 'bold', true);

const [leaf, offset] = quill.getLeaf(7);
// leaf 會是帶有值為 "Good" 的葉節點
// offset 應為 1,因為回傳的葉節點在索引 6 開始
console.log('leaf', leaf);
console.log('offset', offset);

getLine

回傳帶入的索引值指定位置的行 blot 。

方法:

getLine(index: Number): [Blot, Number]

範例:

quill.setText('Hello\nWorld!');
const [line, offset] = quill.getLine(7);
// line 應為代表第二個 "World!" 行的 Block Blot
console.log('line', line);
// offset 為 1,因為 index 7 是在第二行 "World!" 的第二個字元
console.log('offset', offset);

getLines

回傳指定位置的行中所包含的 blot。

方法:

getLines(index: Number = 0, length: Number = remaining): Blot[]
getLines(range: Range): Blot[]

範例:

quill.setText('Hello\nGood\nWorld!');
quill.formatLine(1, 1, 'list', 'bullet');

const lines = quill.getLines(2, 5);
// 帶有 ListItem 與 Block Blot 的陣列
// 代表是前面的兩行
console.log('lines', lines);

// 帶入 range 物件
const linesByRange = quill.getLines({ index: 8, length: 5 });
console.log('linesByRange', linesByRange);

小結

今天看了 Model 提供的 API,這些 API 主要是用於尋找 Blot 的相關應用,對於未來要自訂編輯器模組的功能實現時,可以利用這些 API 來找到正確的 Blot 並進一步處理文本內容,並且 Blot 提供的是 linkedList 的資料結構,因此對於節點的尋找來說,未來編輯內容量大的時候,可以研究看看linkedList 訪問節點的技巧來實現較有效率的搜尋處理。

Quill 的觀念基本上不難,較有挑戰的地方在於未來要滿足各種特殊需求時,要建立自訂的 Blot 必須要很清楚底層的生命週期與處理過程,這樣才能打造出高效且實用的自訂功能。找時間再繼續研究使用一些第三方套件,並嘗試了解這些套件是如何實現的,對於自訂功能的實現與優化應該會有所幫助。

再整理一下今天嘗試的 API:

  • find:透過 DOM 節點找到 Quill 或 Blot 實例。
  • getIndex:回傳文件開頭到指定 blot 之間的距離。
  • getLeaf:回傳指定索引處的葉節點。
  • getLine:回傳指定索引位置的行 blot。
  • getLines:回傳指定位置內的所有行 blot。

雜記

今天突然意識到,這星期上完班之後,又是一個連續假期,這次的假期是要回宜蘭帶朋友四處走走,雖然住在宜蘭很久了,但還是有不少地方沒去過,趁這個機會去走走看。不過 11 月就完全沒有連假了,週末期望能好好的學習,並嘗試一些新玩意兒,還有買了一些書,要好好的閱讀一番。

Reference

文章同步發表於2023 iThome 鐵人賽