CKEDITOR网页编辑器还是挺复杂的,有一个 CKEDITOR 的全局空间,有一个 CKEDITOR.instances的全局实例引用,有 Classic 编辑和 Inline 编辑两种模式,有 Plugin 也有 Widget,有自成一体的编译打包工具,与 AMD\CMD\UMD社区不兼容。
加载
生成编辑器
经典编辑(Classic Editing)
内嵌编辑(Inline Editing)
按钮面板定制(Toolbar)
一组一组定义
config.toolbarGroups = [
{ name: ‘clipboard’, groups: [ ‘clipboard’, ‘undo’ ] },
{ name: ‘editing’, groups: [ ‘find’, ‘selection’, ‘spellchecker’ ] },
{ name: ‘links’ },
{ name: ‘insert’ },
{ name: ‘forms’ },
{ name: ‘tools’ },
{ name: ‘document’, groups: [ ‘mode’, ‘document’, ‘doctools’ ] },
{ name: ‘others’ },
‘/‘,
{ name: ‘basicstyles’, groups: [ ‘basicstyles’, ‘cleanup’ ] },
{ name: ‘paragraph’, groups: [ ‘list’, ‘indent’, ‘blocks’, ‘align’, ‘bidi’ ] },
{ name: ‘styles’ },
{ name: ‘colors’ },
{ name: ‘about’ }
];
一个一个定义
config.toolbar = [
{ name: ‘document’, items: [ ‘Source’, ‘-‘, ‘NewPage’, ‘Preview’, ‘-‘, ‘Templates’ ] },
{ name: ‘clipboard’, items: [ ‘Cut’, ‘Copy’, ‘Paste’, ‘PasteText’, ‘PasteFromWord’, ‘-‘, ‘Undo’, ‘Redo’ ] },
‘/‘,
{ name: ‘basicstyles’, items: [ ‘Bold’, ‘Italic’ ] }
];
插件机制
假如我们开发一个插入当前时间戳的插件
插件目录结构
- ckeditor root/
- plugins/
- timestamp/
- icons/
- timestamp.png
- plugin.js
- icons/
- timestamp/
- plugins/
插件代码
CKEDITOR.plugins.add(‘timestamp’, {
icons: ‘timestamp’,
init: function(editor) {
editor.addCommand(‘insertTimestamp’, {
exec: function(editor) {
var now = new Date();
editor.insertHtml(‘The current date and time is: ‘ + now.toString() + ‘‘);
}
});
editor.ui.addButton(‘Timestamp’, {
label: ‘Insert Timestamp’,
command: ‘insertTimestamp’,
toolbar: ‘insert’
});
}
});
通过 CKEDITOR.plugins.add
方法添加插件,第一个参数为插件名,后面为参数列表。 通过editor.addCommand
方法添加一个 insertTimestamp 的命令 通过editor.ui.addButton
方法添加一个按钮控件,并绑定其执行的 command 通过 editor.insertHtml
方法往编辑内容区域追加内容
加载插件
通过配置文件来开启插件
config.extraPlugins = ‘timestamp’;
如果这是一个会出现在 Toolbar 的插件,且 Toolbar 被定制过,则需要显性配置 toolbar让其显示
config.toolbar = {
{name: ‘insert’, [‘Timestamp’]}
}
挂件(Widget)
挂件是由一组 html 元素组成的特殊富文本单元,类似于模板机制 与插件的区别 挂件有 template 字段,插件没有 挂件目录结构 与插件一致
挂件代码
CKEDITOR.plugins.add( ‘simplebox’, {
// 表明这是一个 widget
requires: ‘widget’,
icons: 'simplebox',
init: function( editor ) {
CKEDITOR.dialog.add( 'simplebox', this.path + 'dialogs/simplebox.js' );
editor.widgets.add( 'simplebox', {
// 鼠标 hover 在 toolbar 上出现的提示
button: 'Create a simple box',
// 挂件模板
template:
'<div class="simplebox">' +
'<h2 class="simplebox-title">Title</h2>' +
'<div class="simplebox-content"><p>Content...</p></div>' +
'</div>',
// 定义挂件中可编辑的部分
editables: {
title: {
selector: '.simplebox-title',
allowedContent: 'br strong em'
},
content: {
selector: '.simplebox-content',
allowedContent: 'p br ul ol li strong em'
}
},
// 挂件内允许出现的组合
allowedContent:
'div(!simplebox,align-left,align-right,align-center){width};' +
'div(!simplebox-content); h2(!simplebox-title)',
// 挂件最小组合,如果这个 div 被删除,则自动清除该挂件
requiredContent: 'div(simplebox)',
dialog: 'simplebox',
upcast: function( element ) {
return element.name \== 'div' && element.hasClass( 'simplebox' );
},
init: function() {
var width \= this.element.getStyle( 'width' );
if ( width )
this.setData( 'width', width );
if ( this.element.hasClass( 'align-left' ) )
this.setData( 'align', 'left' );
if ( this.element.hasClass( 'align-right' ) )
this.setData( 'align', 'right' );
if ( this.element.hasClass( 'align-center' ) )
this.setData( 'align', 'center' );
},
data: function() {
if ( this.data.width \== '' )
this.element.removeStyle( 'width' );
else
this.element.setStyle( 'width', this.data.width );
this.element.removeClass( 'align-left' );
this.element.removeClass( 'align-right' );
this.element.removeClass( 'align-center' );
if ( this.data.align )
this.element.addClass( 'align-' + this.data.align );
}
} );
}
} );
ACF
CKEditor 的高级内容过滤器,当用户在源码输入模式、editor.setData
输入、直接粘贴 html 代码等输入时候,将不希望出现的内容给过滤掉。
自动模式(Automatic Mode)
当 config.allowedContent
没有设置的时候,ACF 就会进入自动模式。 自动模式通过config.removePlugins
、config.removeButtons
和 config.format_tag
来做过滤微调
config.removePlugins = ‘image,table,tabletools,horizontalrule’;
config.removeButtons = ‘Anchor,Underline,Strike,Subscript,Superscript’;
config.format_tags = ‘p;h1;h2;pre’;
自定义模式(Custom Mode)
通过 config.allowedContent
来进入自定义模式
config.allowedContent =
‘h1 h2 h3 p blockquote strong em;’ +
‘a[!href];’ +
‘img(left,right)[!src,alt,width,height];’;
ACF语法
elements [attributes]{styles}(classes)
例如我们需要保留这样的富文本内容,规则为span(mod_fillblank),其 attributes 对 class 无效。
实战建议
- 能用 CKEditor 社区插件解决的问题,用插件解决
- 插件解决不了的问题,业务自己写plugin 或者 widget 解决
- 业务自己写的部分,尽量不要用 CKEditor 自带的
CKEDITOR.dialog
,他们的实现是用 JS 去码DOM 结构,太复杂了。随便一个 Dialog 控件都能用得很舒服 - 不要用CKEditor 的 jQuery Adapter,他家的 Adapter 对于同一个 DOM 的进行实例化、销毁等操作有 bug,时不时给你冒一个错误。自己包裹一个 Adapter 则肯定没有 bug
- 工程化的时候,构建工具做依赖分析的时候,记得排除掉 CKEditor 目录,否则他家一堆的插件,会严重拖慢依赖分析那步