今天分享下”Quill编辑器操作实例详解“这篇文章,文中根据实例编码详细介绍,或许对大家的编程之路有着一定的参考空间与使用价值,需要的朋友接下来跟着云南仟龙Mark一起学习一下吧。
一转眼早已2020年,难耐的人们不会再能够满足于简易的文本,因此拥有花哨的带上各种各样款式的文本,殊不知有文本还不够,大家还必须让使用者在小编的情况下,可以插进各种各样自定义信息种类,使我们传出去的推广软文更为漂亮,因而拥有本文。
序言
因为Quill在线编辑器内置的富文本过虑(绝大多数流行在线编辑器都是会对富文本开展过虑解决),造成开发人员要想配备自定义HTML模板时,碰到了许多不便。
一、Quill3D渲染逻辑性剖析
为了更好地自定义Quill中的HTML块内容,最先必须掌握Quill內部的3D渲染步骤,这儿几个主要的定义必须掌握:
1、Delta
Delta是Quill內部界定的一个数据类型,用以表明文档内容及其文档改动实际操作,最易读且文件格式简易,根据Delta的方式来维护保养文档内容,HTML内容和Delta二者还可以互相转换。
举个事例:
这样一段富文本会被表示成以下的格式:
1
2
3
4
5
6
7
8
9
10
{
"ops":[
{“insert”:"this is a simple text.\nbut when "},
{“attributes”:{“bold”:true},“insert”:"it is "},
{“insert”:"not bold.\nlet me try "},
{“attributes”:{“italic”:true},“insert”:"italic "},
{“insert”:"haha\nwhat about "}, {“attributes”:
{“italic”:true,“bold”:true},“insert”:“both”},
{“insert”:" ?\n"} ]
}"
普通的文本会被定义成一个个的insert动作,每一项代表这一个delta,都是对文本内容的描述。
类似的,如果修改和删除也会生成对应的delta,之后会将新生成的change delta,与原有的delta进行合并操作,生成新的delta。(delta中一共包含三种操作:insert、delete、retain)
保留前10个字符,对后续的20个字符进行加粗操作的delta如下:
1
2
3
4
5
6
{
"ops": [
{ “retain”: },
{ “retain”: , “attributes”: { “bold”: } }
]
}
保留前10个字符,对后续的20个字符进行删除操作如下:
1
2
3
4
5
6
{
"ops": [
{ “retain”: },
{ “delete”: }
]
}
2、Parchment
Parchment是抽象的文档模型,对Blot进行管理。
将Parchment理解成完整的DOM树结构的话,那么Blot就是其中一个个单一的节点。而Blot去了Quill中默认的以外,还允许我们进行自定义,给了更大的扩展空间。
3、Blot
Blot是Parchment文档的组成部分,相当于对DOM节点类型的抽象,而一个具体的Blot实例里仍有其他的节点信息。
全局的根节点Blot是由Quill内部自定义的Scroll类型Blot,管理其下面的所有Blot。
对于Blot的实现定义可以参照这里: https://github.com/quilljs/parchment#blots
Quill中默认定义的Blot如下:
这其中常见的包括TextBlot(行内普通文本)、Inline(行内携带样式的普通文本)、Block(块级行,一般以段落p为单位)、Break(换行)、Image(图片IMG插入)、Bold(加粗文本)。
而一段HTML如何构建出Blot?Quill中会根据节点类型优先排除文本节点,如果是元素节点会根据节点的ClassName进行再次判断,如果仍然无法找到匹配的BlotName,则默认匹配以下的映射关系,来找到对应的BlotClass。
4、Delta的实际意义
既然已经有Blot可以来表示我们的内容结构了,为什么还需要Delta?Delta本身只是一份内容数据的维护,也就是说HTML的更新,无论是用户输入,还是API操作,都会同步更新到Delta中,而Delta如果不作为HTML的数据源的话,那么维护一份Delta数据的意义又在哪里?
如果HTML => Delta,而不存在Delta=>HTML,那么不停地去维护一份delta的意义是什么?
1、由Delta生成HTML其实是存在的,只不过应用场景只限于初始化文档的时候,Quill会对传入的初始化HTML字符串进行解析处理,生成对应的Delta,其次通过applyDelta的方式,生成DOM节点回显与页面中。
2、看到这里你可能还不满意,为啥非要走这一步流程,初始化的时候直接一段字符串document.getElementById(‘container’).innerHTML = val不行吗,是的,可以,但是Delta的存在让用户的文档变得粒度更细小,变得易维护,变得可追溯。假如A和B同时编辑着一份文档,A删除了第二行的10个字符,不需要将文档内容全量更新,只需要提交action操作,同步自己的行为,而B这边也只需要进行冲突处理后merge即可。虽然Delta的维护让逻辑变得复杂了不少,但它的存在也让文档有了更多扩展的可能。
5、编辑器渲染与更新流程
对于内容的修改一共有以下3种方式:
1、初始化编辑器内容:初始化调用quill.pasteHTML,经过HTML过滤和解析回显到编辑框中。
2、Input Event:用户输入和编辑操作,通过MutationObserver监听处理,更新delta。
3、API调用:调用内部提供API,通过modify方法,而后调用全局Scroll实例的方法去修改。
二、插入自定义HTML块
由于文章内容越来越多样化,在文章插入地图、音乐播放器、广告面板等需求的存在,让我们需要对富文本编辑器扩展出更多的功能。但是同时也要做好xss防护。
按照第一部分的讲述,我们需要插入一个自定义HTML块,同时又要Quill能够识别,聪明的你一定想到了,我们需要自定义一个Blot。通过定义好Blot的方式,让Quill在初始化的时候能够识别我们的HTML块展示,同时也让我们在插入HTML块的时候不会被Quill进行脏HTML过滤。
注册Blot方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
export default function (Quill) {
// 引入源码中的BlockEmbed
const BlockEmbed = Quill.import(‘blots/block/embed’);
// 定义新的blot类型
class AppPanelEmbed extends BlockEmbed {
static create(value) {
const node = super.create(value);
node.setAttribute(‘contenteditable’, ‘false’);
node.setAttribute(‘width’, ‘100%’);
// 设置自定义html
node.innerHTML =http://www.qlyl1688.com/products/ycxsyc.html this.transformValue(value)
return node;
}
static transformValue(value) {
let handleArr = value.split(’\n’)
handleArr = handleArr.map(e => e.replace(/1+/, ‘’)
.replace(/[\s]+$/, ‘’))
return handleArr.join(’’)
}
// 返回节点自身的value值 用于撤销操作
static value(node) {
return node.innerHTML
}
}
// blotName
AppPanelEmbed.blotName = ‘AppPanelEmbed’;
// class名将用于匹配blot名称
AppPanelEmbed.className = ‘embed-innerApp’;
// 标签类型自定义
AppPanelEmbed.tagName = ‘div’;
Quill.register(AppPanelEmbed, true);
}
接下来你只需要这样调用,便可以在编辑器中插入自定义的HTML块:
1
2
3
4
5
6
7
8
9
10
11
quill.insertEmbed(quill.getSelection().index || 0, ‘AppPanelEmbed’, `
<div class="app_card_header">
自定义面板标题
</div>
<div class="app_card_content">
自定义面板内容
</div>
<div class="app_card_footer">
footer
</div>
`);
传参格式要求如下:
1
insertEmbed(index: Number, type: String, value: any, source: String = ‘api’): Delta
这里仅仅这是个简单的示例,如果想丰富自定义Blot的功能,可以参照: https://github.com/quilljs/parchment#blots
由于contenteditable属性放开,为了防止造成xss进攻,所以需要我们对该属性做特殊的过滤处理,这里以xss模块处理为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
handleWithXss(content) {
const options = {
whiteList: {
…
div: [‘class’, ‘style’, ‘data-id’,‘contenteditable’],
…
},
css: {
whiteList: {
color: true,
’background-color’: true,
’max-width’: true,
},
},
stripIgnoreTag: true,
onTagAttr: (tag, name, value, isWhiteAttr) => {
// 针对div的contenteditable 处理
if (isWhiteAttr && tag === ‘div’ && name === ‘contenteditable’) {
return ‘contenteditable=“false”’;
}
},
} // 自定义规则
const myxss = new xss.FilterXSS(options)
return myxss.process(content)
}
今天的文章就分享到这啦,内容转自脚本之家,下篇文章再见。
\s ↩︎