不到200行 JavaScript 代码实现富文本编辑器的方法

作者:开源拾贝

时间:2026-10-22

浏览:576

本文深入解析 Pell 项目,一个仅 130 行核心代码的轻量级富文本编辑器。通过分析 exec 函数、actions 对象及 init 初始化流程,详解如何利用 document.execCommand 实现加粗、链接等编辑功能,并展示如何自定义工具栏按钮及处理用户交互,适合希望理解富文本底层原理的开发者。

文章目录

Pell 编辑器简介与核心结构

近期在调研富文本编辑器相关资料时,发现了 Pell 项目。这是一个所见即所得(WYSIWYG)文本编辑器,尽管功能基础,但其体积令人惊叹,整体仅 1kb。核心文件 pell.js 仅包含 130 行代码,即便计入其他部分,总 JavaScript 代码量也不足 200 行。这激发了我的兴趣,决定深入剖析其源码实现机制。

项目的核心逻辑集中在 pell.js 文件中,其结构清晰,主要功能由以下几个关键部分支撑:

  • actions 对象
  • exec() 函数
  • init() 函数

Document.execCommand()

exec 函数与 execCommand 核心机制

首先从最简单的部分开始,exec() 函数仅包含以下三行代码:


export const exec = (command, value =null) => {
  document.execCommand(command, false, value);
};

该函数对 document.execCommand() 进行了简单封装。Document.execCommand() 是编辑器的核心 API,其语法格式如下:


bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)

  • aCommandName 参数用于指定要执行的命令字符串,例如:加粗使用 'bold',创建链接使用 'createLink',调整字体大小使用 'fontSize' 等。
  • aShowDefaultUI 参数用于控制是否显示默认用户界面。
  • aValueArgument 参数用于提供某些命令所需的额外输入,例如插入图片或链接时需要指定地址。

注:经实测,在 Chrome 浏览器中修改 aShowDefaultUI 的值并未观察到明显差异。该参数源自旧版 IE,因此此处保持默认值 false 即可。参考相关 StackOverflow 讨论。

actions 对象与按钮配置

actions 对象

文件中定义了一个名为 actions 的对象,对应工具栏中的一行按钮。actions 对象中的每个子对象保存了对应按钮的属性配置。

部分代码示例如下:


const actions = {
  bold: {
    icon: '<b>B</b>',
    title: 'Bold',
    result: ()=> exec('bold')
  },
  italic: {
    icon: '<i>I</i>',
    title: 'Italic',
    result: ()=> exec('italic')
  },
  underline: {
    icon: '<u>U</u>',
    title: 'Underline',
    result: ()=> exec('underline')
  },
  // …
}

上述代码展示了 bold、italic、underline 三个对象属性,分别对应工具栏中的加粗、斜体、下划线按钮。可以看出它们的结构一致,均包含以下三个属性:

  • icon:定义如何在工具栏中显示图标。
  • title:定义鼠标悬停时显示的提示文本。
  • result:一个函数,被赋值给按钮作为点击事件处理程序,负责调用前述的 exec() 函数对文本执行操作。

既然已定义 actions 对象,接下来需了解如何调用它。这涉及 init() 函数,该函数根据特定规则从 actions 对象中选取元素组成数组,数组中的每一项将生成一个按钮。以下代码中的 settings.actions 即为此数组,其元素对应工具栏上显示的按钮。settings.actions 的生成规则将在后文解释。


// pell.js 中的 init() 函数
settings.actions.forEach(action=> {
  // 新建一个按钮元素
  const button = document.createElement('button')
  // 给按钮加上 css 样式
  button.className = settings.classes.button
  // 把 icon 属性作为内容显示出来
  button.innerHTML = action.icon
  button.title = action.title
  // 把 result 属性赋给按钮作为点击事件
  button.onclick = action.result
  // 将创建的按钮添加到工具栏上
  actionbar.appendChild(button)
})

这样,数组中的每个元素便生成了工具栏上的一个按钮。

init 初始化函数与工具栏生成

init() 初始化函数

使用 pell 编辑器时,只需调用 init() 函数进行初始化。它接收一个 setting 对象作为参数,包含以下属性:

  • element:编辑器的 DOM 元素。
  • styleWithCSS:设置为 true 时,将使用 <span style="font-weight: bold;"></span> 代替 <b></b>。
  • actions
  • onChange

其中最重要的是 actions,它是一个数组,包含希望在工具栏中显示的按钮列表。

actions 数组中可以包含以下几种元素:

  • 一个字符串。
  • 一个包含 name 属性的对象。
  • 一个不包含 name 属性但具备生成按钮所需属性(如 icon、result 等)的对象。

actions: [
 'bold',
 'underline',
 'italic',
 {
  name: 'image',
  result: ()=> {
   const url = window.prompt('Enter the image URL')
   if (url) window.pell.exec('insertImage', ensureHTTP(url))
  }
 },
 // ...
]

在 init() 函数中,会将传入的 actions 参数与 pell.js 中定义的 actions 对象进行合并,可将 actions 对象视为默认设置。参考以下代码:


// pell.js 中的 init() 函数
settings.actions = settings.actions
  ? settings.actions.map(action=> {
    if (typeof action === 'string') return actions[action]
    // 如果参数中传入的 action 已经在默认设置中存在,用传入的参数覆盖默认设置
    else if (actions[action.name]) {
      return { ...actions[action.name], ...action }
    }
    return action
  })
  : Object.keys(actions).map(action=> actions[action])

如果参数对象 setting 中未包含 actions 数组,则默认使用之前定义的 actions 对象进行初始化。

init() 函数的另一个重要部分是创建可编辑区域。这里创建了一个 div 元素,将其 contentEditable 属性设为 true,从而允许在此区域使用前述的 document.execCommand() 命令。


// 创建编辑区域的元素
settings.element.content = document.createElement('div')
// 让 div 成为可编辑状态
settings.element.content.contentEditable = true
settings.element.content.className = settings.classes.content
// 当用户输入时,更新页面的相应部分
settings.element.content.oninput = event=> 
  settings.onChange(event.target.innerHTML)
settings.element.content.onkeydown = preventTab
settings.element.appendChild(settings.element.content)

插入链接功能流程解析

流程整理

最后以“插入链接”为例梳理整个编辑器的流程:

一、在调用 init() 函数时,在参数对象的 action 数组中加入以下一项:


{
  name: 'link',
  result: ()=> {
    const url = window.prompt('Enter the link URL')
    if (url) window.pell.exec('createLink', ensureHTTP(url))
  }
}

二、在 init() 运行过程中,会检查已定义的 actions 对象中是否存在 link 属性。经检查,该属性确实存在。


link: {
  icon: '',
  title: 'Link',
  result: ()=> {
    const url = window.prompt('Enter the link URL')
    if (url) exec('createLink', url)
  }
}

由于传入参数中包含 result 项,因此使用传入的 result 替换 link 对象中的默认值,并将修改后的 link 对象放入 settings.actions 数组中。

三、对 settings.actions 数组进行迭代以生成工具栏,link 对象作为其中一项生成了一个“插入链接”按钮。result 属性成为其点击事件处理程序。

四、点击“插入链接”按钮后,会提示输入 url,随后调用 exec('createLink', url) 在编辑区域插入该链接。

编辑器其他按钮的功能流程与此类似。

至此,Pell 编辑器的大部分内容已讲解完毕,剩余部分需自行查阅源码。鉴于项目代码量不大,以此作为文本编辑器的入门示例十分合适。

总结

以上所述是小编给大家介绍的不到200行 JavaScript 代码实现富文本编辑器的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章