本文深入解析 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 代码实现富文本编辑器的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!