前言
印象中,富文本编辑器一直是一个高大上的玩意儿,想实现一个应该也挺难的。既然如此,那必须得学啊,这篇文章就是记录如何实现一个超简易的富文本编辑器。
实现方式
实现富文本编辑器通常来说有两种方式:
给元素设置 contenteditable=’true’
1 | <div contenteditable="true"> |
设置 iframe 的 designMode 为 ‘on’
1 | <iframe style="width: 500px;height: 330px;"></iframe> |
现在大多数都使用 contenteditable=’true’ 方法来实现富文本编辑器,我这里也采用这种方式
document.execCommand
定义:当一个HTML文档切换到设计模式时,document暴露 execCommand 方法,该方法允许运行命令来操纵可编辑内容区域的元素。
使用:document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
参数:
- aCommandName 命令的名称,具体可看命令列表
- aShowDefaultUI 布尔值,是否展示用户界面,一般为 false。
- aValueArgument 额外的命令参数,默认为 null。
可以使用 document.queryCommandState(commandName) 来检测是否支持命令
命令列表
详细的可以查看这里,这里只列一些常见的: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
35
36
37
38
39
40
41
42
43
44backColor
修改文档的背景颜色。在styleWithCss模式下,则只影响容器元素的背景颜色。这需要一个<color> 类型的字符串值作为参数传入。注意,IE浏览器用这个设置文字的背景颜色。
bold
开启或关闭选中文字或插入点的粗体字效果。IE浏览器使用 <strong>标签,而不是<b>标签。
createLink
将选中内容创建为一个锚链接。这个命令需要一个hrefURI字符串作为参数值传入。URI必须包含至少一个字符,例如一个空格。(浏览器会创建一个空链接)
copy
拷贝当前选中内容到剪贴板。启用这个功能的条件因浏览器不同而不同,而且不同时期,其启用条件也不尽相同。使用之前请检查浏览器兼容表,以确定是否可用。
cut
剪贴当前选中的文字并复制到剪贴板。启用这个功能的条件因浏览器不同而不同,而且不同时期,其启用条件也不尽相同。使用之前请检查浏览器兼容表,以确定是否可用。
delete
删除选中部分.
fontName
在插入点或者选中文字部分修改字体名称. 需要提供一个字体名称字符串 (例如:"Arial")作为参数。
fontSize
在插入点或者选中文字部分修改字体大小. 需要提供一个HTML字体尺寸 (1-7) 作为参数。
foreColor
在插入点或者选中文字部分修改字体颜色. 需要提供一个颜色值字符串作为参数。
heading
添加一个标题标签在光标处或者所选文字上。 需要提供 aValueArgument 参数 (例如. "H1", "H6"). (IE 和 Safari不支持)
italic
在光标插入点开启或关闭斜体字。 (Internet Explorer 使用 EM 标签,而不是 I )
justifyCenter、justifyFull、justifyLeft、justifyRight
对光标插入位置或者所选内容进行文字居中、文本对齐、左对齐、右对齐。
selectAll
选中编辑区里的全部内容。
redo
重做被撤销的操作。
undo
撤销最近执行的命令。
到这里正式开始撸
HTML部分: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<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Editor</title>
</head>
<style>
* { margin: 0; padding: 0; }
html, body { height: 100%; width: 100%; }
ul { list-style: none; }
.wrap-editor{ width: 800px; height: 500px; border: 1px solid #ccc; position: fixed;
top: 0; left: 0; right: 0; bottom: 0; margin: auto; overflow: hidden; }
.wrap-editor .menu{ width: 100%; height: 35px; border-bottom: 1px solid #ccc;
cursor: pointer; font-size: 0; line-height: 35px; }
.wrap-editor .menu li{ display: inline-block; font-size: 14px; padding: 0 10px; }
.wrap-editor .editor{ width: 100%; height: 465px; outline: 0; overflow: auto; padding: 8px; box-sizing: border-box; }
</style>
<body>
<div class="wrap-editor">
<!-- 按钮区域 -->
<ul class="menu">
<li class="bold">B</li>
</ul>
<!-- 编辑区域 -->
<div class="editor" contenteditable="true">
</div>
</div>
</body>
<script src="./main.js"></script>
</html>
接下来是Js部分
一步步来,实现最基本的功能先:1
2
3
4
5
6
7
8
9
10
11
12
13
14var editor = document.querySelector('.editor') // 编辑器
var bold = document.querySelector('.bold') // 加粗按钮
editor.innerHTML = '请输入内容' // 给编辑器一个初始内容
// 执行命令
function execCommand(commandName, value) {
value = value ? value : null
document.execCommand(commandName, false, value)
}
bold.onclick = function (e) {
execCommand('bold')
}
这段代码十分简单,连我之前也没想到会如此简单。
接着我们点击加粗按钮,这里会遇到一个坑无论怎么操作都无法加粗选择的文字,而且编辑区还失去了焦点
。
这个问题也很好解释,因为我们点击的按钮的文字也是可以选择的,点击按钮时所选区域自然就清空了
,当然会出现没有加粗与失去焦点的问题啦。
下面是我所知道的几种解决方法:
在加粗按钮上阻止 onmousedown 的默认事件:
1
<li class="bold" onmousedown="event.preventDefault();">B</li>
在按钮区域添加CSS
user-select: none;
这种方式存在兼容问题,如果不考虑低版本浏览器则可以使用。1
2
3.wrap-editor .menu{
user-select: none;
}缓存所选区域,点击事件触发后,手动调用API重新选中文字:
监听editor区域的onkeyup、onmouseup、onmouseout等操作,在这些操作后缓存editor中的选中区域,点击事件触发后,手动调用API重新选中文字。
这种方法下面会讲。
缓存用户所选区域
- 在键盘、鼠标、移出编辑区等操作后,缓存当前所选区域
- 点击按钮后恢复所选区域、focus()编辑器,之后执行 document.execCommand 修改内容
具体用法见 Window.getSelection
修改后的代码如下: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
35
36
37
38
39
40
41
42
43
44
45
46var editor = document.querySelector('.editor') // 编辑器
var bold = document.querySelector('.bold') // 加粗按钮
editor.innerHTML = '请输入内容' // 给编辑器一个初始内容
// 执行命令
function execCommand(commandName, value) {
value = value ? value : null
document.execCommand(commandName, false, value)
}
// 定义一个变量
var selectedRange
// 保存选择区域
function saveSelection() {
var sel = window.getSelection()
if (sel.getRangeAt && sel.rangeCount) {
selectedRange = sel.getRangeAt(0)
}
}
// 恢复选择区域
function restoreSelection() {
var selection = window.getSelection()
if (selectedRange) {
try {
selection.removeAllRanges()
} catch (ex) {
document.body.createTextRange().select()
document.selection.empty()
}
selection.addRange(selectedRange)
}
}
// 在editor内做了鼠标键盘操作就保存一下选择区域
editor.onmouseup = editor.onkeyup = editor.onmouseout = function () {
saveSelection();
}
bold.onclick = function (e) {
restoreSelection() // 恢复选择区域
editor.focus() // focus一下
execCommand('bold')
saveSelection() // 再保存一下选择区域
}
如何插入图片
使用 FileReader 将图片读为 base64 格式,调用对应 API 将图片插入编辑区。1
document.execCommand('insertImage', false, imgUrl)
将图片读为 base64 格式
部分HTML:1
<input class="inster-img" type="file" />
部分Js:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 将图片读为base64格式
function imgToBase64(file) {
return new Promise((resolve, reject) => {
var reader = new FileReader()
reader.readAsDataURL(file);
reader.onload = function () {
resolve(this.result)
}
})
}
var insterImg = document.querySelector('.inster-img')
insterImg.onchange = function (e) {
restoreSelection() // 恢复选择区域
editor.focus() // focus一下
var file = e.target.files[0]
imgToBase64(file).then((val) => {
var base64Url = val
execCommand('insertImage', base64Url)
saveSelection() // 再保存一下选择区域
})
}
到此学习如何实现一个简易富文本编辑器的目的已经达到了,这里就不再写了
下面放上简易编辑器的完整的代码,写的比较随意⊙﹏⊙‖∣,将就看吧: