前段时间在找富文本编辑器的一些资料,然后发现这个项目叫Pell,是一个所见即所得的文本编辑器,尽管功能简单,但令人惊讶的是只有1kb大小。项目的核心文件pell.js只有130行。就算加上其他部分,js总共也就不到200行。这引起了我的兴趣,我决定查看源代码以了解它是如何做到这一点的。项目的主要代码在pell.js文件中,结构非常简单。主要功能的实现依赖以下几部分:actionsobjectexec()functioninit()functionDocument.execCommand()先从最简单的部分开始,exec()函数只有下面三行:exportconstexec=(command,value=null)=>{document.execCommand(command,false,value);};它为document.execCommand()做一个简单的包装,Document.execCommand()是这个编辑器的核心,它的语法如下执行,例如:加粗'bold',创建链接'createLink',更改字体大小'fontSize'等aShowDefaultUI是否显示默认用户界面aValueArgument有些命令需要额外输入,比如插入图片和链接需要给个地址注意:经过我的实验,chrome下改了aShowDefaultUI的值并没有发现影响,这个stackoverflow的问题提到这个是老版本IE的一个参数,所以这里可以设置为默认false。在actions对象文件中定义了一个名为actions的对象,对应下图中工具栏上的那一行按钮。actions中的每个子对象都保存了一个按钮的属性。部分代码:constactions={bold:{icon:'B',title:'Bold',result:()=>exec('bold')},italic:{icon:'I',title:'Italic',result:()=>exec('italic')},underline:{icon:'U',title:'Underline',result:()=>exec('underline')},//...}这段代码显示了名为bold、italic、underline的三个对象属性,分别对应toolbar前面的bold、italic、underline可以看出它们的结构是一样的,都具有以下三个属性:icon:如何在工具栏中显示标题title:就是标题result:一个函数,会作为点击事件赋值给按钮,调用上面提到的Exec()函数对文本进行操作现在我们有了actions对象,我们如何使用它呢?这依赖于init()函数,它会按照一定的规则从actions对象中选择元素组成一个数组,数组中的每一项都会生成一个按钮。下面代码中的settings.actions就是这个数组,里面的每个元素对应一个显示在工具栏上的按钮。settings.actions的生成规则后面会讲到。//pell.js中的init()函数settings.actions.forEach(action=>{//创建一个新的按钮元素constbutton=document.createElement('button')//给按钮添加css样式button.className=settings.classes.button//将icon属性显示为contentbutton.innerHTML=action.iconbutton.title=action.title//将result属性赋值给按钮作为点击事件button.onclick=action.result//将创建的按钮添加到工具栏actionbar.appendChild(button)})以便数组中的每个元素在工具栏上生成一个按钮。init()初始化函数当你想使用pell编辑器时,只需调用init()函数来初始化一个编辑器。它接收一个设置对象作为参数,其中包含如下一些属性:元素:编辑器的DOM元素styleWithCSS:当设置为true时,动作将被onChange替换。最重要的是动作,它是一个数组,其中包含您要在工具栏中显示的按钮列表。actions数组中可以有这些类型的元素:字符串、具有name属性的对象、没有name属性但具有生成按钮所需的属性icon、result等的对象。在init()函数中,actions参数和pell.js中定义的actions对象组合在一起,可以使用actions对象作为默认设置,见如下代码:actions:['bold','underline','italic',{name:'image',result:()=>{consturl=window.prompt('EntertheimageURL')if(url)window.pell.exec('insertImage',ensureHTTP(url))}},//...]如果参数对象设置不包含actions数组,则默认使用之前定义的actions对象进行初始化。init()函数中还有一个重要的部分,就是创建一个可编辑区域。这里创建了一个div元素,并将其contentEditable属性设置为true,这样就可以在这里使用前面提到的document.execCommand()命令了。.//在编辑区创建元素settings.element.content=document.createElement('div')//使div成为可编辑状态settings.element.content.contentEditable=truesettings.element.content.className=settings.classes.content//当用户进入时,更新页面的相应部分.element.content)流程安排***以“插入链接”为例,梳理整个编辑器流程:1、调用init()函数时,在参数对象的action数组中添加如下项{name:'link',result:()=>{consturl=window.prompt('EnterthelinkURL')if(url)window.pell.exec('createLink',ensureHTTP(url))}}其次,在init运行过程中(),会检查定义的actions对象中是否有link属性。检查属性后确实存在link:{icon:'🔗',title:'Link',result:()=>{consturl=window.prompt('EnterthelinkURL')if(url)exec('createLink',url)}}因为传入的参数中有result项,所以用传入的result替换link对象中的默认值,然后将修改后的link对象放入settings.actions数组中。3、对settings.actions数组进行一次迭代,生成一个toolbar,将link对象作为其中一项,生成一个“insertlink”按钮。结果属性成为它的点击事件。4、点击“插入链接”按钮后,会要求输入一个url,然后调用exec('createLink',url)在编辑区插入链接。编辑器中其他按钮的功能流程也类似。至此,pell编辑器的大部分内容已经讲解完毕,剩下的就需要自行查看了。毕竟项目的代码不长,拿来作为文本编辑器的介绍还是不错的。
