移动端富文本实践篇(三)
laizimo opened this issue · 0 comments
前言
之前,几篇文章我们了解到了一定的基础知识,如果你还未曾看过,可以点击这个链接观看。
本篇内容主要是讲一下文章中文字部分的处理,如'bold'、'italic'、'blockqueto'、'h1'等,以及分割行的插入和链接的插入和修改等模块的代码分析。那么接下来,我们就源码开始,对于我们上述所概述的知识点进行分析。如果你喜欢我的文章,欢迎评论,欢迎Star~。欢迎关注我的github博客
正文
从这里开始我们就会根据源码来对每个模块的实现,进行深入的分析和探讨。
字体
首先,我们可以来看一下加粗,斜体和删除线的实现。我们先来看一下,源码:
commandSet: ['bold', 'italic', 'strikethrough', 'redo', 'undo'],
exec: function(command){
const _self = this;
if(_self.commandSet.indexOf(command) !== -1){
document.execCommand(command, false, null);
}else{
let value = '<'+command+'>';
document.execCommand('formatBlock', false, value);
}
},
首先,我们来了解一下document.execCommand()函数,它具备3个参数:
- command:可以理解为命令,它具备许多原生的命令,例如: 'bold'、'italic'等。同时,它也需要一些自定义标签的命令。它们之间的区别就是性能问题。之后,再做详细的分析
- aShowDefaultUI:是否展示用户界面,一般都会设置成false
- value:额外的参数值,一般特殊的命令会需要用到特殊的参数,这时就需要去设置,但是默认为null
讲一下,这里的设计。由于我们需要去区分有参数的命令和无参数的命令,所以我们首先将无参数命令,做成一个集合。每当调用这个函数时,首先回去查找是否这个命令是无参数命令。我们来看两个例子:
//bold
document.execCommand('bold', false, null);
//h1
document.execCommand('formatBlock', false, '<h1>');
那么,你可以讲个上面的优化,然后在调用函数的过程中,使用如下的方式:
//bold
RE.exec('bold');
//h1
RE.exec('h1');
这样整体等代码风格会非常的节俭,之后需要添加无参数命令时,只需要在命令集中增加原生命令就可以实现增加。
此处,在编写函数时使用了一点小技巧,有兴趣的朋友可以学习一下
这个函数可以满足我们接下来的字体处理部分,包括引用块。下面我将列举每个功能的调用方式,当然了,android调用也是一样的。
//bold 加粗
RE.exec('bold');
//italic 斜体
RE.exec('italic');
//strikethrough 删除线
RE.exec('strikethrough');
//h1 h1标签
RE.exec('h1');
//h2 h2标签
RE.exec('h2');
//h3 h3标签
RE.exec('h3');
//h4 h4标签
RE.exec('h4');
//blockquote 引用块
RE.exec('blockquote');
有兴趣的同学也可以将JS部分内容进行扣取,然后自己去进行实现。同时,IOS中的运用也是一致的。
分割行
接下来,我们来聊一下分割行的插入问题。
首先,我们来知道一下如何插入分割行。通过execCommand函数中的insertHtml命令执行,例子:
//hr
document.execCommand('insertHtml', false, '<hr>');
这样子,是可以形成一个分割行,但是,你会发现一个问题——焦点不会置换行。
「焦点无法换行」,其感觉就是缺乏一个回车操作。但是,我们不可能要求用户在插入的过程中,都去执行一个回车操作,所以我们需要来讲解一个小知识:
回车操作:在大多数人的认识中,回车就是插入的一个
标签,但是,如果你仔细去观察dom中时,会发现回车操作其实是在后面插入了这样子的格式块。这里是一个小技巧,在后面插入图片的时候,也会被使用到。
所以,我们需要将代码修改一下:
//hr
document.execCommand('insertHtml', false, '<hr><div><br></div>');
这样,我们就实现了一个正常的分割行的插入,同时也保证了焦点的换行。下面我们需要对上述的操作进行封装,因为这个命令会被频繁的调用,而变化的只是后面value部分。
所以,我们的源码中会进行这样子的封装:
insertHtml: function(html){
const _self = this;
document.execCommand('insertHtml', false, html);
},
insertLine: function(){
const _self = this;
const html = '<hr><div><br></div>';
_self.insertHtml(html);
},
其中,这里的insertHtml函数,我们在之后的链接和图片插入过程都会被使用到。所以,我只能提前在这里先分析掉了。
链接
讲完分割行的操作,我们紧接着来分析一下链接的操作。
链接的操作,或许会比较繁琐一点,因为,我们必须保证这个链接的插入和修改。
插入过程会比较简单,我们可以先来看一下源码:
//link
insertLink: function(name, url){
const _self = this;
const html = `<a href="${url}" class="editor-link">${name}</a>`;
_self.insertHtml(html);
}
你会发现,这里需要一个name参数和一个url参数。所以,需要android在调用时,先获取到用户输入的链接名和链接地址。这个具体的样式,在github项目中有展示,所以,你也可以模仿我们样式,在点击插入链接按钮的时候,跳出一个输入表单,然后让用户进行输入,最终获得到相应的数据之后,调用这个值。
这个部分比较简单,主要的逻辑部分还在android部分,有兴趣的同学,可以自行去研究android的源码。
之后,我们需要来阐述一下修改链接部分。
按照惯例,我们也先来看一下修改链接部分的源码:
//change link
changeLink: function(name, url){
const _self = this;
const current = _self.cache.currentLink;
const len = name.length;
current.innerText = name;
current.setAttribute('href', url);
const selection = window.getSelection();
const range = selection.getRangeAt(0).cloneRange();
const { startContainer, endContainer } = _self.currentRange;
selection.removeAllRanges();
range.setStart(startContainer, len);
range.setEnd(endContainer, len);
selection.addRange(range);
}
首先,要明白的是,我们在修改链接部分的逻辑并不是简单的索取name和url这么简单。
从头开始说起的话,我们要继续插入链接之后的话题说起。当我们插入链接之后,我们如果需要修改一个链接的逻辑部分可以看成一下几个步骤:
- 首先,确定你点击的部分是一个链接(这个部分我们在之前的增加点击事件部分内容中已经讲过了)
- 之后,你需要将链接的这个部分获取出它的链接名和链接地址显示出来,方便用户修改
- 然后,重新插入的这一步时,我们需要去控制这个range块(因为你会发现,你不仅仅只是修改这个链接,你还需要保证的是,你在修改之后的焦点位置是正确的,如果你不对这里进行处理的话,就会导致焦点的失去,这个行为本身就是不符合逻辑的)
- 最后,我们需要根据修改的内容,来调整焦点的位置
所以,看过这个部分的内容之后,我们就可以来理解一下源码中我们所作的处理了。
- 首先,我们会在最初点击事件内部保存这个链接节点currentLink,
- 之后,我们需要根据我们输入的name和url来修改当前这个节点的innerText内容和href属性。
- 最后一步,就是我们之前提到的修改range。因为,我们之前讲过range的基础知识,知道它具备startContainer、startOffset、endContainer、endOffset。这么四个基本属性,那么,我们调整焦点的时候,只需要去调整整个offset距离就可以了,我们只要将startOffset和endOffset都保证与输入内容相同长度,就可以保证这个焦点一定会在链接末尾处。
这里,我们就将修改链接部分的逻辑表述完了,不知道你现在是否清楚我们为什么需要这样去操作了呢。欢迎讨论。
总结
这篇文章中,我们对于字体部分的内容,分割行,最后是链接等三部分的操作做了一个详尽的分析。也将我在开发过程中的思考,写在了里面,可以说是一篇十足的实践类的干货文章。同时,也希望你在看完这篇文章之后,会对富文本操作方面有更加深入的了解,下面一篇文章,讲述的主题是图片操作这块的内容,尽情期待!!!
最后,如果你对我写的有疑问,可以与我讨论。如果我写的有错误,欢迎指正。你喜欢我的博客,请给我关注Star~呦。大家一起总结一起进步。欢迎关注我的github博客。同时也希望你关注我们的项目,github项目地址,谢谢支持