开源好用的所见即所得(WYSIWYG)编辑器:Editor.js

@
目录

今天介绍一个开源好用的Web所见即所得(WYSIWYG)编辑器:Editor.js

Editor.js 是一个基于 Web 的所见即所得富文本编辑器,它由CodeX团队开发。源代码托管于Github:https://github.com/codex-team/editor.js

特点

它有两个显著的特点,一个是基于区块(block-styled)的编辑模式,另一个是可以输出干净的数据。

基于区块

基于区块官网是这样解释的:

Editor.js工作区由单独的区块组成:段落、标题、图像、列表、引号等。它们中的每一个都是由 Plugin 提供的独立元素(或更复杂的结构)并由 Editor's Core 连结。

干净的数据

Editor.js 输出干净的json数据而不是 HTML 标记,虽然对浏览器来说,HTML 是更直观的,但对服务器来说,json更精简更关注内容本身,易于重复使用,存储和传输。

对于控件本身也更易于实现,比如在文本"加粗"和"常规"来回切换,基于json的更改一个属性,总要比基于HTML反复添加和删除标记更简单吧?

界面与交互

在编辑区域,Editor.js提供了区块工具栏(Block Tools),内联工具栏(Inline Tools)和区块编辑栏(Block Tunes)

他们分别通过 加号 + 按钮,选中区块内容和菜单(六个点和尚按钮)来访问🤓

插件

每个区块都通过插件提供支持,官方提供了常用的插件,当然也可以自己写插件。

官方提供的插件如下图,在sample中,都以cdn方式引入了这些插件,也可以通过npm安装。

标题和文本

序列化后的数据如下图所示,

{
    "id" : "zcKCF1S7X8",
    "type" : "header",
    "data" : {
        "text" : "Editor.js",
        "level" : 1
    }
},
{
    "id" : "b6ji-DvaKb",
    "type" : "paragraph",
    "data" : {
        "text" : "支持文本,标题,列表,代办,表格,图片,链接,代码片段,引用片段等等"
    }
},
{
    "id" : "SSBSguGvP7",
    "type" : "list",
    "data" : {
        "style" : "ordered",
        "items" : [
            {
                "content" : "支持普通文本,<i>斜体文本</i>,<b>加粗</b>",
                "items" : []
            },
            {
                "content" : "支持<mark class=\"cdx-marker\">文本高亮</mark>、<a href=\"https://baidu.com\">文本链接</a>、<code class=\"inline-code\">代码片段</code><mark class=\"cdx-marker\"></mark>",
                "items" : []
            }
        ]
    }
},

图片

图片支持Base64编码,和url两种方式上传图片

{
    "id" : "VYsWoLL7yj",
    "type" : "image",
    "data" : {
        "url" : "data:image/png;base64, ...",
        "caption" : "codex2x.png",
        "withBorder" : false,
        "withBackground" : false,
        "stretched" : false
    }
}

列表

支持有序和无序列表,列表支持嵌套

无序列表:

{
            "id" : "i_cVQxn3Tb",
            "type" : "list",
            "data" : {
                "style" : "unordered",
                "items" : [
                    {
                        "content" : " 香蕉🍌",
                        "items" : []
                    },
                    {
                        "content" : " 苹果🍎",
                        "items" : []
                    },
                    {
                        "content" : " 葡萄🍇  ",
                        "items" : []
                    }
                ]
            }
        },

有序列表:

        {
            "id" : "nOTdryosj2",
            "type" : "list",
            "data" : {
                "style" : "ordered",
                "items" : [
                    {
                        "content" : "洗手心",
                        "items" : []
                    },
                    {
                        "content" : "搓手背",
                        "items" : []
                    },
                    {
                        "content" : "洗指缝",
                        "items" : []
                    }
                ]
            }
        },

嵌套列表:

        {
            "id" : "LJjzlmGa-3",
            "type" : "list",
            "data" : {
                "style" : "unordered",
                "items" : [
                    {
                        "content" : "序章",
                        "items" : []
                    },
                    {
                        "content" : "第一章",
                        "items" : [
                            {
                                "content" : "第一节",
                                "items" : [
                                    {
                                        "content" : "a)",
                                        "items" : []
                                    },
                                    {
                                        "content" : "b)",
                                        "items" : []
                                    },
                                    {
                                        "content" : "c)",
                                        "items" : []
                                    }
                                ]
                            },
                            {
                                "content" : "第二节",
                                "items" : []
                            }
                        ]
                    }
                ]
            }
        },

Todo

{
            "id" : "Hitrs4RqXw",
            "type" : "checklist",
            "data" : {
                "items" : [
                    {
                        "text" : "满意😊",
                        "checked" : true
                    },
                    {
                        "text" : "一般😐",
                        "checked" : false
                    },
                    {
                        "text" : "不满意☹️",
                        "checked" : false
                    }
                ]
            }
        },

表格

不代表头:

{
    "id" : "xPAQ6AkUiK",
    "type" : "paragraph",
    "data" : {
        "text" : "<b>不带表头</b>"
    }
},
{
    "id" : "_MMoOqlgXs",
    "type" : "table",
    "data" : {
        "withHeadings" : false,
        "content" : [
            [
                "<b>重要紧急</b>",
                "<b>重要不紧急</b>"
            ],
            [
                "吃饭睡觉",
                "订生日蛋糕"
            ],
            [
                "<b>不重要但紧急</b>",
                "<b>不重要不紧急</b>"
            ],
            [
                "上班前定好闹钟",
                "总结这一周的工作"
            ]
        ]
    }
},

带表头:

{
    "id" : "fvfQSljMK8",
    "type" : "table",
    "data" : {
        "withHeadings" : true,
        "content" : [
            [
                "星期一",
                "星期二",
                "星期三",
                "星期四",
                "星期五"
            ],
            [
                "a",
                "b",
                "c",
                "d",
                "e"
            ]
        ]
    }
},

使用

安装

页面中引用Editor.js Core库,可通过npm安装。也可以编译项目,然后引入编译后的js文件。

yarn add @editorjs/editorjs

<script src="lib/editorjs/editorjs.umd.js"></script>

创建编辑器实例

在页面创建编辑器

import EditorJS from '@editorjs/editorjs';


const editor = new EditorJS({
  /**
   * Id of Element that should contain Editor instance
   */
  holder: 'editorjs'
});

这是一个最小化的示例。你会发现没有那些默认的工具。因此需要在配置中指定工具。

配置工具

可以通过传入配置对象创建编辑器实例。以下是示例

holder指定编辑器的容器元素。

window.editor = new window.EditorJS({
    /**
        * Wrapper of Editor
        */
    holder: 'editorjs',

配置工具

配置完成后,区块工具栏将呈现一个较为完整的工具列表。

    /**
        * Tools list
        */
    tools: {
        paragraph: {
            config: {
                placeholder: "Enter something"
            }
        },

        header: {
            class: Header,
            inlineToolbar: ['link'],
            config: {
                placeholder: 'Header'
            },
            shortcut: 'CMD+SHIFT+H'
        },

        /**
            * Or pass class directly without any configuration
            */
        image: ImageTool,

        list: {
            class: NestedList,
            inlineToolbar: true,
            shortcut: 'CMD+SHIFT+L'
        },

        checklist: {
            class: Checklist,
            inlineToolbar: true,
        },

        quote: {
            class: Quote,
            inlineToolbar: true,
            config: {
                quotePlaceholder: 'Enter a quote',
                captionPlaceholder: 'Quote\'s author',
            },
            shortcut: 'CMD+SHIFT+O'
        },


        marker: {
            class: Marker,
            shortcut: 'CMD+SHIFT+M'
        },

        code: {
            class: CodeTool,
            shortcut: 'CMD+SHIFT+C'
        },

        delimiter: Delimiter,

        inlineCode: {
            class: InlineCode,
            shortcut: 'CMD+SHIFT+C'
        },

        linkTool: LinkTool,

        embed: Embed,

        table: {
            class: Table,
            inlineToolbar: true,
            shortcut: 'CMD+ALT+T'
        },

    },
    

    /**
        * Initial Editor data
        */
    data: obj,
    onReady: function () {
        saveButton.click();
    },
});

本地化

可以通过传入i18n配置对象来设置编辑器的本地化。以下是一个较为完整的中文化示例:

i18n: {
    messages: {
        "ui": {
            "blockTunes": {
                "toggler": {
                    "Click to tune": "点击转换",
                    "or drag to move": "拖动调整"
                },
            },
            "inlineToolbar": {
                "converter": {
                    "Convert to": "转换成"
                }
            },
            "toolbar": {
                "toolbox": {
                    "Add": "添加",
                    "Filter": "过滤",
                    "Nothing found": "无内容"
                },
                "popover": {
                    "Filter": "过滤",
                    "Nothing found": "无内容"
                }
            }
        },
        "toolNames": {
            "Text": "段落",
            "Heading": "标题",
            "List": "列表",
            "Warning": "警告",
            "Checklist": "清单",
            "Quote": "引用",
            "Code": "代码",
            "Delimiter": "分割线",
            "Raw HTML": "HTML片段",
            "Table": "表格",
            "Link": "链接",
            "Marker": "突出显示",
            "Bold": "加粗",
            "Italic": "倾斜",
            "InlineCode": "代码片段",
            "Image": "图片"
        },
        "tools": {
            "link": {
                "Add a link": "添加链接"
            },
            "stub": {
                'The block can not be displayed correctly.': '该模块不能放置在这里'
            },
            "image": {
                "Caption": "图片说明",
                "Select an Image": "选择图片",
                "With border": "添加边框",
                "Stretch image": "拉伸图像",
                "With background": "添加背景",
            },
            "code": {
                "Enter a code": "输入代码",
            },
            "linkTool": {
                "Link": "请输入链接地址",
                "Couldn't fetch the link data": "获取链接数据失败",
                "Couldn't get this link data, try the other one": "该链接不能访问,请修改",
                "Wrong response format from the server": "错误响应",
            },
            "header": {
                "Header": "标题",
                "Heading 2": "二级标题",
                "Heading 3": "三级标题",
                "Heading 4": "四级标题",
                "Heading 5": "五级标题",
            },
            "paragraph": {
                "Enter something": "请输入笔记内容",
            },
            "list": {
                "Ordered": "有序列表",
                "Unordered": "无序列表",
            },
            "table": {
                "Heading": "标题",
                "Add column to left": "在左侧插入列",
                "Add column to right": "在右侧插入列",
                "Delete column": "删除列",
                "Add row above": "在上方插入行",
                "Add row below": "在下方插入行",
                "Delete row": "删除行",
                "With headings": "有标题",
                "Without headings": "无标题",
            },
            "quote": {
                "Align Left": "左对齐",
                "Align Center": "居中对齐",
            }
        },
        "blockTunes": {
            "delete": {
                "Delete": "删除",
                'Click to delete': "点击删除"
            },
            "moveUp": {
                "Move up": "向上移"
            },
            "moveDown": {
                "Move down": "向下移"
            },
            "filter": {
                "Filter": "过滤"
            }
        },
    }
}

自定义样式

varaiables.css中包含了大部分的样式变量,更改这些变量可以实现自定义样式。

如通过重写 .root样式选择器可以实现自定义的背景色, 重写.ce-popover 改变弹出框样式等。

:root {
    --color-bg-main: #F0F0F0;
    --color-border-light: #E8E8EB;
    --color-text-main: #000;
    --selectionColor: #e1f2ff;
}


.ce-popover {
    --border-radius: 6px;
    --width: 200px;
    --max-height: 270px;
    --padding: 6px;
    --offset-from-target: 8px;
    --color-border: #e8e8eb;
    --color-shadow: rgba(13,20,33,0.13);
    --color-background: white;
    --color-text-primary: black;
    --color-text-secondary: #707684;
    --color-border-icon: rgb(201 201 204 / 48%);
    --color-border-icon-disabled: #EFF0F1;
    --color-text-icon-active: #388AE5;
    --color-background-icon-active: rgba(56, 138, 229, 0.1);
    --color-background-item-focus: rgba(34, 186, 255, 0.08);
    --color-shadow-item-focus: rgba(7, 161, 227, 0.08);
    --color-background-item-hover: #eff2f5;
    --color-background-item-confirm: #E24A4A;
    --color-background-item-confirm-hover: #CE4343;
}
.dark-mode {
  --color-border-light: rgba(255, 255, 255,.08);
  --color-bg-main: #1c1e24;
  --color-text-main: #737886;
}

-- 完 --

相关推荐
小政爱学习!22 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。27 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼34 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k093337 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
web行路人1 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱0011 小时前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
子非鱼9211 小时前
【Ajax】跨域
javascript·ajax·cors·jsonp
超雄代码狂2 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
周亚鑫2 小时前
vue3 pdf base64转成文件流打开
前端·javascript·pdf
落魄小二2 小时前
el-table 表格索引不展示问题
javascript·vue.js·elementui