富文本编辑器quill.js开发之自定义格式扩展

 更新时间:2023年08月14日 09:37:11   作者:Grewer  
这篇文章主要为大家介绍了富文本编辑器quill.js开发之自定义格式扩展,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

鉴于各种繁杂的需求,quill.js 编辑器也面临着各种挑战,例如我们需要添加“table”布局样式以适应邮件发送格式,手动扩展表情符号功能等等。本文将对这些可定制化功能进行讲解和实现。

区分 format 和 module

首先需要明确的是,我们应该清楚自己所需的扩展具体是什么?

比如想要新增一个自定义 emoji, 那么想象一下步骤:

  • 点击工具栏
  • 弹出弹窗或者对应的 popover
  • 在 2 中选中 emoji

这些步骤是一种常见的添加流程。

我们需要明确的是,添加自定义表情符号必然需要一个相应的格式。

本文将以 format 为例,对此进行详细讲解。

quill 的格式类型

说起 quill 的格式类型, 他的常用格式可以分成 3 类:

  • Inline

常见的有 BoldColorFont 等等, 不占据一行的标签, 类似于 html 里 span 的特性, 是一个行内样式, Inline格式之间可以相互影响

  • Block

添加 Block 样式, 必然会占据一整行, 并且 Block 样式之间不能兼容(共存), 常见的有 ListHeaderCode Block 等等

  • Embeds

媒体文件, 常见的有 ImageVideoFormula, 这类格式扩展的比较少, 但是本次要加的 emoji 但是这种格式

自定义样式

新增 emoji.ts 文件来存储格式, 关于他的类型, 我们选择 Embeds 格式, 使用这种格式有以下原因:

  • 他是一种独特的类型, 不能和颜色, 字体大小等等用在一起
  • 需要和字体并列, 所以也不能是 Block 类型
import Quill from 'quill';
const Embed = Quill.import('blots/embed');
class EmojiBlot extends Embed {
  static blotName: string;
  static tagName: string;
  static create(value: HTMLImageElement) {
    const node = super.create();
    node.setAttribute('alt', value.alt);
    node.setAttribute('src', value.src);
    node.setAttribute('width', value.width);
    node.setAttribute('height', value.height);
    return node;
  }
  static formats(node: HTMLImageElement) {
    return {
      alt: node.getAttribute('alt'),
      src: node.getAttribute('src'),
      width: node.getAttribute('width'),
      height: node.getAttribute('height'),
    };
  }
  static value(node: HTMLImageElement) {
    // 主要在有初始值时起作用
    return {
      alt: node.getAttribute('alt'),
      src: node.getAttribute('src'),
      width: node.getAttribute('width'),
      height: node.getAttribute('height'),
    };
  }
}
EmojiBlot.blotName = 'emoji';
EmojiBlot.tagName = 'img';
EmojiBlot.className = 'emoji_icon'
export default EmojiBlot;

因为还有正常的图片类型会使用 img, 这里就需要加上 className, 来消除歧义
一般来说, 新开发的扩展性类型, 尽量都加上 className

这样一个 emoji 类型就创建完成了!

最后我们注册到 Quill 上即可:

import EmojiBlot from "./formats/emoji";
Quill.register(EmojiBlot);

这里我们在加上自定义的 popover, 用来点击获取 emoji:

<Popover content={<div className={'emoji-popover'} onClick={proxyEmojiClick}>
  <img alt={'图片说明'} width={32} height={32} src="https://grewer.github.io/dataSave/emoji/img.png"/>
  <img alt={'图片说明'} width={32} height={32} src="https://grewer.github.io/dataSave/emoji/img_1.png"/>
</div>}>
  <button className="ql-emoji">emoji</button>
</Popover>

通过代理的方式, 来获取 dom 上的具体属性:

const proxyEmojiClick = ev => {
  const img = ev.target
  if (img?.nodeName === 'IMG') {
    const quill = getEditor();
    const range = quill.getSelection();
    // 这里可以用 img 的属性, 也可以通过 data-* 来传递一些数据
    quill.insertEmbed(range.index, 'emoji', {
      alt: img.alt,
      src: img.src,
      width: img.width,
      height: img.height,
    });
    quill.setSelection(range.index + 1);
  }
}

展示下新增 emoji 的效果:

基础格式说明

我们的自定义格式都是基于 quill 的基础库: parchment

这里我们就介绍下他的几个重要 API:

class Blot {
  // 在手动创建/初始值时, 都会触发 create 函数
  static create(value?: any): Node;
  // 从 domNode 上获取想要的数据
  static formats(domNode: Node);
  // static formats 返回的数据会被传递给 format
  // 此函数的作用是将数据设置到 domNode
  // 如果 name 是 quill 里的格式走默认逻辑是会被正确使用的
  // 如果是特殊的name, 不处理就不会起效
  format(format: name, value: any);
  // 返回一个值, 通常在初始化的时候传给 static create
  // 通常实现一个自定义格式, value 和 format 使用一个即可达到目标
  value(): any;
}

上述几个 API 便是创建自定义格式时常用到的

详情可参考: https://www.npmjs.com/package/parchment#blots

在上文讲到了 format 和 value 的作用, 我们也可以对于 EmojiBlot 做出一些改造:

class EmojiBlot extends Embed {
  static blotName: string;
  static tagName: string;
  static create(value: HTMLImageElement) {
    const node = super.create();
    node.setAttribute('alt', value.alt);
    node.setAttribute('src', value.src);
    node.setAttribute('width', value.width);
    node.setAttribute('height', value.height);
    return node;
  }
  static formats(node: HTMLImageElement) {
    return {
      alt: node.getAttribute('alt'),
      src: node.getAttribute('src'),
      width: node.getAttribute('width'),
      height: node.getAttribute('height'),
    };
  }
  format(name, value) {
    if (['alt', 'src', 'width', 'height'].includes(name)) {
      this.domNode.setAttribute(name, value);
    } else {
      super.format(name, value);
    }
  }
}

目前来说, 这两种方案都能实现我们的 EmojiBlot

当然 format 的作用, 并不仅仅在于 新增属性到 dom 上, 也可以针对某些属性, 修改、删除 dom 上的信息

其他格式

上面我们讲述了三个常见的格式: Inline 、Embeds 、Block, 其实在 quill 还有一些特殊的 blot:
如: TextBlot 、 ContainerBlot 、 ScrollBlot

其中 ScrollBlot 属于是所有 blot 的根节点:

class Scroll extends ScrollBlot {
  // ...
}

Scroll.blotName = 'scroll';
Scroll.className = 'ql-editor';
Scroll.tagName = 'DIV';
Scroll.defaultChild = Block;
Scroll.allowedChildren = [Block, BlockEmbed, Container];

至于 TextBlot, 是在定义一些属性时常用到的值:

例如源码中 CodeBlock 的部分:

CodeBlock.allowedChildren = [TextBlot, Break, Cursor];

意味着 CodeBlock 的格式下, 他的子节点, 只能是文本, 换行, 光标
(换行符和光标都属于 EmbedBlot)

这样就控制住了子节点的类型, 避免结构错乱

ContainerBlot

最后要说一下 ContainerBlot, 这是一个在自定义节点时, 创建 Block 类型时经常会用到的值:

在源码中, 并没有默认的子节点配置, 所以导致看上去就像这样, 但其实 container 的自由度是非常强的

这里就给出一个我之前创建的信件格式例子:

在富文本中扩展格式生成能兼容大部分信件的外层格式, 格式要求:

格式占据一定宽度, 如 500px, 需要让这部分居中, 格式内可以输入其他的样式

大家可能觉得简单, 只需要 div 套上, 再加上一个样式 width 和 text-align 即可

但是这种方案不太适合邮件的场景, 在桌面和移动端渲染电子邮件大约有上百万种不同的组合方式。

所以最稳定的布局方案只有 table 布局

所以我们开始创建一个 table 布局的外壳:

class WidthFormatTable extends Container {
  static create() {
    const node = super.create();
    node.setAttribute('cellspacing', 0);
    node.setAttribute('align', 'center');
    return node;
  }
}
WidthFormatTable.blotName = 'width-format-table';
WidthFormatTable.className = 'width-format-table';
WidthFormatTable.tagName = 'table';

有了 table 标签, 那么同样也会需要 tr 和 rd:

也是类似的创建方法:

class WidthFormatTR extends Container {
}
class WidthFormatTD extends Container {
}

最后通过 API 将其关联起来:

WidthFormatTable.allowedChildren = [WidthFormatTR];
WidthFormatTR.allowedChildren = [WidthFormatTD];
WidthFormatTR.requiredContainer = WidthFormatTable;
WidthFormatTD.requiredContainer = WidthFormatTR;
WidthFormatTD.allowedChildren = [WidthFormat];
WidthFormat.requiredContainer = WidthFormatTD;

这一段的含义就是, 保证各个格式的父元素与子元素分别是什么, 不会出现乱套的情况

格式中最后的主体:

class WidthFormat extends Block {
  static register() {
    Quill.register(WidthFormatTable);
    Quill.register(WidthFormatTR);
    Quill.register(WidthFormatTD);
  }
}
WidthFormat.blotName = 'width-format';
WidthFormat.className = 'width-format';
WidthFormat.tagName = 'div';

register 函数的作用就是在注册当前的 WidthFormat 格式时, 自动注册其他的依赖格式; 避免人多注册多次

最后我们新增一个按钮, 来格式化编辑器内容:

const widthFormatHandle = () => {
  const editor = getEditor();
  editor.format('width-format', {})
}

展示下效果:

比较遗憾的是, 同样作为 Block 格式, 这两类是不能兼容的, 也就是说在 width-format 格式中, 不能使用 List , Header , Code 这几项属性
个人吐槽几句, 之前尝试兼容过, 但是在 HTML 和 delta 相互转换时被卡主了, 感觉转换的方式没做好

总结

demo链接: 点此查看

本文介绍了 quill.js 在面临多种需求挑战时需要添加可定制化功能。quill.js 的常用格式包括 Inline、Block 和 Embeds 三类,而ContainerBlot 则是创建 Block 类型时常用的值,具有极高的自由度。希望本文能够帮助读者更好地了解和思考富文本编辑的相关问题。

以上就是富文本编辑器quill.js开发之自定义格式扩展的详细内容,更多关于富文本编辑器quill.js格式扩展的资料请关注脚本之家其它相关文章!

相关文章

  • Vue实现步骤条效果

    Vue实现步骤条效果

    这篇文章主要为大家详细介绍了Vue实现步骤条效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • vue实现导出Word文件(数据流方式)

    vue实现导出Word文件(数据流方式)

    这篇文章主要介绍了vue实现导出Word文件(数据流方式),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • vue的props实现子组件随父组件一起变化

    vue的props实现子组件随父组件一起变化

    这篇文章主要为大家详细介绍了vue的props如何实现子组件随父组件一起变化,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • vue中项目页面空白但不报错产生的原因及分析

    vue中项目页面空白但不报错产生的原因及分析

    这篇文章主要介绍了vue中项目页面空白但不报错产生的原因及分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • .html页面引入vue并使用公共组件方式

    .html页面引入vue并使用公共组件方式

    这篇文章主要介绍了.html页面引入vue并使用公共组件方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • vue项目中使用qrcodesjs2生成二维码简单示例

    vue项目中使用qrcodesjs2生成二维码简单示例

    最近项目中需生成二维码,发现了很好用的插件qrcodesjs2,所以下面这篇文章主要给大家介绍了关于vue项目中使用qrcodesjs2生成二维码的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • Antd的Table组件嵌套Table以及选择框联动操作

    Antd的Table组件嵌套Table以及选择框联动操作

    这篇文章主要介绍了Antd的Table组件嵌套Table以及选择框联动操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • Vue+Antv F2实现混合图表

    Vue+Antv F2实现混合图表

    这篇文章主要为大家详细介绍了Vue+Antv F2实现混合图表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • vue-cli3添加模式配置多环境变量的方法

    vue-cli3添加模式配置多环境变量的方法

    这篇文章主要介绍了vue-cli3添加模式配置多环境变量的方法,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06
  • vue实现多个元素或多个组件之间动画效果

    vue实现多个元素或多个组件之间动画效果

    这篇文章主要为大家详细介绍了vue实现多个元素或多个组件之间动画效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-09-09

最新评论