欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

荐 手撸实现一个富文本编辑器

程序员文章站 2022-07-07 19:42:35
重点是弄懂以下几个知识点contenteditable 、 window.getSelection()、Range、document.execCommand附上链接Window/getSelectionDocument.createRange()document.execCommand()注意:document.execCommand 这个方法已经废弃,但是浏览器还是支持,只不过随时可能不支持实现方式 ES6 class类和jq1、首先创建个html文件 做好准备工作......

 重点是弄懂以下几个知识点

contenteditable window.getSelection()Rangedocument.execCommand 

附上链接

Window/getSelection

Document.createRange()

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