荐 手撸实现一个富文本编辑器
重点是弄懂以下几个知识点
contenteditable 、 window.getSelection()、Range、document.execCommand
附上链接
注意:document.execCommand 这个方法已经废弃,但是浏览器还是支持,只不过随时可能不支持
- 实现方式 ES6 class类和jq
1、首先创建个html文件 做好准备工作
引入所需的jq文件
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
body中富文本所需要渲染哪个div中
<div id="box"></div>
创建一个类 里面声明菜单配置以及生成dom结构的方法
class Editor {
constructor(id) {
this.id = id //渲染dom
this.lastAddress = '' //焦点最后位置
this.config = [ //菜单配置
{
title: 'B',
name: 'bold'
},
{
title: 'C',
name: 'foreColor',
list: [{
title: 'blue',
val: '#007ACC'
},
{
title: 'red',
val: '#E21918'
},
{
title: 'green',
val: '#1AA15F'
}
]
},
{
title: 'H',
name: 'FontSize',
list: [{
title: 'H1',
val: '6'
},
{
title: 'H2',
val: '5'
},
{
title: 'H3',
val: '4'
},
{
title: 'H4',
val: '3'
},
{
title: 'H5',
val: '2'
},
{
title: 'H6',
val: '1'
}
]
}
]
}
//创建编辑dom
createDom() {
}
}
new个事例调用createDom()方法
<script>
let editor = new Editor('#box')
editor.createDom()
</script>
2、createDom()的实现
判断一下是否传入渲染的dom的id
//检验是否传入dom
if ($(this.id).length === 0) {
console.log('未传入DOM')
return;
}
首先创建菜单和编辑的区域
//创建菜单 和 编辑区域
let div_top = $("<div id='div_top'></div>");
let div_bottom = $("<div id='div_bottom'><div id='area' contenteditable></div></div>");
$(this.id).append(div_top, div_bottom)
contenteditable 这个属性指定元素内容是否可编辑。 这个也是编辑的基础
接下来就是生成菜单了,jq的each方法遍历this.config
//生成菜单
$(this.config).each(function (i, n) {
var menu = ''
if (!n.list) {
menu = `<div class='memu-l'><i class='memu-l-btn' name='${n.name}'>${n.title}</i></div>`
} else {
var str = ''
if (n.name === 'foreColor') {
$(n.list).each(function (j, k) {
str = str + `<div class='memu-l-list' name='${n.name}' val='${k.val}' ><i class='memu-l-btn' name='${n.name}' val='${k.val}' style='color:${k.val}'>${k.title}</i></div>`
})
} else if (n.name === 'FontSize') {
$(n.list).each(function (j, k) {
str = str + `<div class='memu-l-list'><${k.title}><i class='memu-l-btn' name='${n.name}' val='${k.val}'>${k.title}</i></${k.title}></div>`
})
}
menu = `<div class='memu-l'><i class='memu-l-btn' name='${n.name}'>${n.title}</i><div class="memu-l-btn_"> ${str}</div></div>`
// console.log(i, menu)
}
$("#div_top").append(menu)
})
最后我们来看看生成后的样子(css 就不看了)
接下来就是实现相对应的功能了
1、封装好document.execCommand方法
//编辑插入事件
execCommand(type, bool, val = null) {
document.execCommand(type, bool, val);
}
2、然后就是给功能按钮添加点击事件
//功能
$('.memu-l-btn').on('click', function (e) {
//阻止默认事件
e.preventDefault();
//执行功能
self.execCommand($(e.target).attr("name"), false, $(e.target).attr("val"))
})
比如加粗,当你点击的时候发现原本编辑区域有焦点的,结果一点击焦点消失了
这样自热也就没有加粗了,
这时候会想到该如何去处理焦点的保存和恢复 这个时候就要用到 window.getSelection()、Range
3、封装保存焦点的方法
在编辑区域具有焦点的时候,点击粗体按钮的时候 getSelection().getRangeAt(0)获取焦点的位置然后保存起来
// 保存当前焦点位置
saveRangeAddress() {
const self = this
// 获取selection对象 保存焦点
const selection = window.getSelection ? window.getSelection() : document.getSelection()
self.lastAddress = selection.getRangeAt(0)
console.log('保存******', self.lastAddress)
}
4、封装设置焦点的方法
先判断存在是否getSelection,不存在的话就直接让编辑区域得到焦点,存在的话判断是否有焦点的最后位置,有的话selection.removeAllRanges()清除所有焦点在addRange恢复最后一个焦点位置,如果不存在使用 document.createRange 方法让焦点恢复到最后一个子节点的位置,在保存当时焦点的位置便于下次使用
// 设置焦点最后所处位置
setRangeAddress() {
const self = this
//清除焦点 还原最后焦点的位置
const selection = window.getSelection ? window.getSelection() : document.getSelection()
//防止直接点击功能按钮没有焦点
$('#area').focus()
//判断是否具有getSelection对象
if (selection) {
// 判断是否有焦点最后位置
if (self.lastAddress) {
//存在最后焦点位置 回到原来的位置
selection.removeAllRanges()
selection.addRange(self.lastAddress)
} else {
// 如果之前没有保存焦点则新建一个
const content = $('#area')[0]
const range = document.createRange()
range.setStart(content, 0)
range.setEnd(content, 0)
selection.addRange(range)
self.lastAddress = range
}
} else {
$('#area').focus()
}
}
5、在回到第二步 改进一下方法
添加了保存焦点位置,和设置焦点位置
保存位置一定要在执行了事件之后保存才不会导致焦点变化
//功能
$('.memu-l-btn').on('click', function (e) {
//阻止默认事件
e.preventDefault();
//设置最后焦点位置
self.setRangeAddress()
//执行功能
self.execCommand($(e.target).attr("name"), false, $(e.target).attr("val"))
//发生改变后在执行一次保存焦点
// console.log('memu-l-btn 保存')
self.saveRangeAddress()
})
看看效果吧
6、不关功能按钮需要点击的时候保存,手动调整调整和键盘事件也需要添加保存焦点事件
点击保存
//点击 记录焦点最后的位置
$('#area').on('click', function () {
const selection = window.getSelection ? window.getSelection() : document.getSelection()
if (selection) {
// console.log('click 保存')
self.saveRangeAddress()
}
})
键盘点击保存
//按键 插入dom 记录焦点最后的位置
$('#area').on('keyup', function (e) {
self.saveRangeAddress()
})
7、输出代码和渲染代码
//输出代码
exportHtml() {
return $('#area').html()
}
//渲染
visibleHtml() {
console.log(this.exportHtml())
$('#visible').html(this.exportHtml())
}
初次接触富文本这个东西,写的不好,还请各位大佬勿怪
本文地址:https://blog.csdn.net/qq_39235055/article/details/107229315