【quill.js】如何使用Container创建表格结构

前言

这篇文章更适合对quill.js已经有一定了解的读者,如果您是正打算调研/学习quill.js,可以通过官方文档或我之前的一篇文章《深入理解quilljs》Quill编辑器有一定了解后,再来阅读这篇文章。

为了更清晰的介绍Container的用法,我选择使用个人基于quill.js实现的表格模块 quill-better-table 作为示例来讲解,因为quill.js官方内置的模块都相对简单,难以覆盖各个方面。

quill-better-table项目地址:github.com/soccerloway…

Container类

Parchemnt是quill.js用于描述/管理编辑器内容结构及对应DOM树的底层依赖, quill.js中内置的Blot类均继承自Parchment的基础类,这些基础类继承自Parchemnt的抽象类。关于Container类的继承关系如下:


其中,ShadowBlot是所有blot(Inline、Block、Embed、Container等)的父类。实际上,在Quill/blots的源码中,Container类(quill.js)仅仅是继承了Parchment中的Container类,没有任何逻辑代码。故我们真正需要了解的是Parchment中的Container类

如何定义嵌套结构及原理

接下来,我们来看看表格模块中单元格行(TableCellLine)、单元格(tableCell)的定义(为避免代码过多,影响阅读体验,format及表格业务逻辑相关的代码会省略掉,完整代码可到这里查看):

// 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>复制代码

实际上,控制嵌套结构关系的重点就是:

  1. 定义容器Blot类,且实现checkMerge接口方法;
  2. 将被包裹Blot类的requiredContainer设置为容器Blot类。

Blot.requiredContainer

定义该Blot需要被哪一个容器Blot包裹。当该Blot被创建完成后,会执行到ShadowBlot类中的optimize方法,其主要逻辑就是:检查该Blot的requiredContainer是否被设置,并且该Blot的父Blot不是requiredContainer设置的Blot类的实例,调用wrap方法(wrap的作用就是创建容器Blot实例,插入的该Blot的父级中,然后将该Blot插入到容器中)。

以最终生成前面的HTML结构为例,经过wrap的过程后,HTML结构的变化:


checkMerge

该方法就是用于检查是否需要将这个容器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>复制代码

利用checkMerge阻止并列(兄弟关系)的Blot合并

通过上面的定义,我们已经能够得到一个支持多行的表格单元格结构了。但在实际的表格中,同一表格行中有若干个单元格,结构如下:

<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的同学大有帮助。


转载于:https://juejin.im/post/5cd7eafa6fb9a032196eecd0

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值