富文本编辑器其实是一个非常有意思的课题,真要深入进去会有很多有意思的东西,也会碰到很多问题,曾经在知乎上看到一个很有意思的话题 《为什么都说富文本编辑器是天坑?》,在这个话题里有很多很具参考性的回答,有兴趣的小伙伴可以去看看。在这里我只提供一个思路,具体的还需要参考更多的资料去实现~
准备阶段
如何在web端实现一个富文本编辑器,会有几个实现方式。而比较方便且能基本满足一般需求的方案是,利用html5的contentEditable属性,再配合上js的document.execCommand()和execCommand相关的一些判断方法。于是我尝试使用这个方案写了一个简单的富文本编辑器,这个编辑器实现了三个功能,加粗、设置标题和设置颜色。先上图让大家有个大致的印象:
在实现功能之前我们先要了解document.execCommand、document.queryCommandValue、document.queryCommandState这三个方法。具体如何使用就不在这里赘述了,具体的用法可以通过下面链接查阅:
www.cnblogs.com/tugenhua070…
了解这些知识后,我们就可以开始实现功能了,如何实现这三个功能呢,这里有个大前提,包裹的标签需要添加contentEditable属性。
<p contenteditable="true">
请编辑内容…
</p>
加粗
设置了contenteditable之后,就可以用document.execCommand来实现上面所说的三个功能了。实现加粗功能比较简单,直接使用execCommand中的bold命令即可。
document.execCommand("bold", false, null)
设置标题
实现标题功能,就没这么简单了,需要先用document.queryCommandValue做一些判断,来保证功能能够正常使用。
if (document.queryCommandValue('formatBlock')!=="div"&&document.queryCommandValue('formatBlock')!=="") {
document.execCommand("formatBlock", false, "div")
} else {
document.execCommand("formatBlock", false, "H1")
}
设置颜色
设置颜色这里直接使用<input type=“color” />,设置颜色也比较简单
document.execCommand("foreColor", false,e.target.value)
// e.target.value是input中获取的色值
一些需要优化的地方
到了这里你也许会觉的编辑器就完成了,但其实还是有需要几个需要优化的地方
- 不能存储选区(一旦点击非输入框区域,之前的选区就会消失不能恢复)
- 不能判断命令的状态(无法完成功能按钮的交互状态)
存储选区
如何存储选区呢,这里就要提到Selection对象了
Selection 对象表示用户选择的文本范围或插入符号的当前位置。
这里具体思路是存储最近一次的选区,然后在需要的时候进行恢复。需要特别提到的是Select对象的范围会被作为Range对象返回,具体实现代码如下:
tip: 需要设置一个全局变量来保存Range
// golbal.js
let Global = {
range: null
}
// selection.js
// 构建一个操作选区的类
class selection {
constructor() {
}
// 存储选区
static saveSelection() {
let range = window.getSelection().getRangeAt(0)
Global.range = range
}
// 恢复选区
static recoverSelection() {
let selection = window.getSelection()
selection.removeAllRanges()
selection.addRange(Global.range)
}
}
然后在输入框设置存储选区功能
// 这里设定element为输入框对象
element['onmouseup'] = function(e) {
selection.saveSelection()
}
每次使用命令之前使用select.recoverSelection()方法就能解决问题了,这里拿加粗功能部分举例
// 恢复选区
select.recoverSelection()
document.execCommand("bold", false, null)
命令状态的判断
还有一个问题就是如何判断命令的状态,在这里就要使用到document.queryCommandValue和document.queryCommandState了。这里还得用上全局变量,用来保存命令的状态。
let Global = {
range: null,
bold: false,
head: false
}
然后就是使用document.queryCommandValue、document.queryCommandState来检查命令的状态。
// bold命令判断
Global.bold = document.queryCommandState('bold')
// head命令判断
if(document.queryCommandValue('formatBlock')!=="div"&&document.queryCommandValue('formatBlock')!=="") {
Global.head = true
} else {
Global.head = false
}
到了这里,这个编辑器的功能是基本实现了,具体实现可以参考我的github,地址:github.com/hahaaha/Ric…