這篇文章更適合對quill.js已經有必定了解的讀者,若是您是正打算調研/學習quill.js,能夠經過官方文檔或我以前的一篇文章《深刻理解quilljs》對Quill編輯器有必定了解後,再來閱讀這篇文章。javascript
爲了更清晰的介紹Container的用法,我選擇使用我的基於quill.js實現的表格模塊 quill-better-table 做爲示例來說解,由於quill.js官方內置的模塊都相對簡單,難以覆蓋各個方面。java
quill-better-table項目地址:github.com/soccerloway…git
Parchemnt是quill.js用於描述/管理編輯器內容結構及對應DOM樹的底層依賴, quill.js中內置的Blot類均繼承自Parchment的基礎類,這些基礎類繼承自Parchemnt的抽象類。關於Container類的繼承關係以下:github
其中,ShadowBlot是全部blot(Inline、Block、Embed、Container等)的父類。實際上,在Quill/blots的源碼中,Container類(quill.js)僅僅是繼承了Parchment中的Container類,沒有任何邏輯代碼。故咱們真正須要瞭解的是Parchment中的Container類。bash
接下來,咱們來看看錶格模塊中單元格行(TableCellLine)、單元格(tableCell)的定義(爲避免代碼過多,影響閱讀體驗,format及表格業務邏輯相關的代碼會省略掉,完整代碼可到這裏查看):dom
// TableCellLine
class TableCellLine extends Block { ...... // create/formats/optimize等}
TableCellLine.blotName = "table-cell-line"
TableCellLine.ClassName = "qlbt-cell-line"
TableCellLine.tagName = "DIV"
// TableCell
class TableCell extends Container {
......
checkMerge() {
if (super.checkMerge() && this.next.children.head != null) {
const thisHead = this.children.head.formats()["table-cell-line"]
const thisTail = this.children.tail.formats()["table-cell-line"]
const nextHead = this.next.children.head.formats()["table-cell-line"]
const nextTail = this.next.children.tail.formats()["table-cell-line"]
return (
thisHead.cell === thisTail.cell &&
thisHead.cell === nextHead.cell &&
thisHead.cell === nextTail.cell
)
}
return false
}
......
}
TableCell.blotName = "table"
TableCell.tagName = "TD"
TableCell.allowedChildren = [TableCellLine]
TableCellLine.requiredContainer = TableCell複製代碼
通過上面的定義,最終生成的HTML結構以下:編輯器
<td>
<div class="qlbt-cell-line"></div>
<div class="qlbt-cell-line"></div>
</td>複製代碼
實際上,控制嵌套結構關係的重點就是:post
checkMerge
接口方法;requiredContainer
設置爲容器Blot類。定義該Blot須要被哪個容器Blot包裹。當該Blot被建立完成後,會執行到ShadowBlot類中的optimize
方法,其主要邏輯就是:檢查該Blot的requiredContainer
是否被設置,而且該Blot的父Blot不是requiredContainer
設置的Blot類的實例,調用wrap
方法(wrap的做用就是建立容器Blot實例,插入的該Blot的父級中,而後將該Blot插入到容器中)。學習
以最終生成前面的HTML結構爲例,通過wrap的過程後,HTML結構的變化:ui
該方法就是用於檢查是否須要將這個容器Blot實例和它的下一個兄弟Blot實例合併爲同一個容器Blot實例。返回值爲true
則合併。checkMerge
在Container類的optimize
方法中調用,緊接shadowBlot.optimize
過程。當checkMerge
返回值爲true
時,下一個兄弟Blot實例的children
會被插入到這個容器實例中,獲得最終HTML結構。
<td>
<div class="qlbt-cell-line"></div>
<div class="qlbt-cell-line"></div>
</td>複製代碼
經過上面的定義,咱們已經可以獲得一個支持多行的表格單元格結構了。但在實際的表格中,同一表格行中有若干個單元格,結構以下:
<tr>
<td>
<div class="qlbt-cell-line">1</div>
</td>
<td>
<div class="qlbt-cell-line">2</div>
</td>
<td>
<div class="qlbt-cell-line">3</div>
</td>
</tr>複製代碼
如何讓checkMerge合適的時候返回false,阻止單元格被合併呢?讓咱們來看看quill-better-table中,TableCell類的checkMerge
。
checkMerge() {
if (super.checkMerge() && this.next.children.head != null) {
const thisHead = this.children.head.formats()["table-cell-line"]
const thisTail = this.children.tail.formats()["table-cell-line"]
const nextHead = this.next.children.head.formats()["table-cell-line"]
const nextTail = this.next.children.tail.formats()["table-cell-line"]
return (
thisHead.cell === thisTail.cell &&
thisHead.cell === nextHead.cell &&
thisHead.cell === nextTail.cell
)
}
return false
}複製代碼
示例代碼中,checkMerge
方法主要是經過檢查當前tableCell實例和下一個tableCell實例的children
的formats中屬性cell是否相等,相等則合併兩個單元格的內容,不然不合並。
實際上,quill-better-table在定義TableCellLine
的時候,爲它定義了表示單元格和行的惟一標識符,設置到domNode的屬性上了,且經過formats方法可以獲得這些信息,TableCellLine的DOM結構爲:
<div class="qlbt-cell-line" data-row="row-xaes" data-cell="cell-hsop">複製代碼
checkMerge
方法既是經過這個惟一標識符,也是data-cell
的值來區別td
是否須要被合併。依照這樣的方式,咱們就可以一層一層的把表格結構相關的Blot所有定義出來。最終實如今Quill編輯器中插入表格。
注意:像表格這種多層嵌套的內容結構,須要在最內層把惟一標識符都設計好,quill.js中嵌套結構的基礎在最內層,而後一層一層wrap和merge,在wrap的時候把所須要的惟一標識符往外傳遞和使用。
表格相關Blot的定義,以及表格編輯經常使用功能的實現涉及到的具體細節太多,在這裏不贅述,有興趣的同窗能夠到個人quill-better-table開源項目的源代碼中查看具體實現細節,相信對將要在Quill中使用Container的同窗大有幫助。