- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
导读
本文围绕 CKEditor 4 仓库中的手动测试场景tests/plugins/tabletools/manual/allowedcontent.md展开,深入剖析其背后的核心机制:allowedContent(内容过滤规则)如何实时控制"单元格属性(Cell Properties)"对话框与右键上下文菜单的可用选项。读完本文,你将掌握 CKEditor 4 内容过滤系统(core/filter.js)与表格工具插件(plugins/tabletools/plugin.js)的联动原理,学会通过配置allowedContent精确裁剪单元格属性编辑能力,并理解为何该手动测试需要反复销毁、重建编辑器。
一、测试场景原文全貌
关联文档tests/plugins/tabletools/manual/allowedcontent.md定义了一个清晰的手动验收场景,其核心内容如下:
测试场景(Test scenario):
- 使用复选框(checkboxes)选择允许的单元格属性(allowed cell properties)。
- 在编辑器上使用上下文菜单(context menu)打开单元格属性(cell properties)。
期望结果(Expected):
- 单元格属性对话框中的选项与所勾选的复选框一一匹配。
- 如果未勾选任何属性,则"单元格属性(Cell Properties)"不会出现在上下文菜单中。
意外情况(Unexpected):
- 对话框选项与勾选的复选框不一致。
注意事项(Note):
- 勾选/取消勾选复选框会销毁并创建新的编辑器,因此页面可能出现可见的闪烁(flickering)。
这段描述虽然简短,却浓缩了两个关键技术断言:对话框选项由allowedContent动态裁剪,以及上下文菜单项由过滤器门控。下面逐一拆解。
二、配套页面:复选框如何驱动 allowedContent
该手动测试的实体页面为 tests/plugins/tabletools/manual/allowedcontent.html,页面通过脚本动态生成复选框,并建立了一张"界面文案 → 过滤规则"的映射表:
| 复选框文案 | 对应过滤规则 | 规则归属 |
|---|---|---|
| Width(宽度) | width | td的styles |
| Height(高度) | height | td的styles |
| Word Wrap(自动换行) | white-space | td的styles |
| Horizontal Align(水平对齐) | text-align | td的styles |
| Vertical Align(垂直对齐) | vertical-align | td的styles |
| Cell Type(单元格类型) | th | 元素本身 |
| Rows Span(行跨度) | rowspan | td的attributes |
| Cols Span(列跨度) | colspan | td的attributes |
| Background Color(背景颜色) | background-color | td的styles |
| Border Color(边框颜色) | border-color | td的styles |
| Color Dialog(颜色选择对话框) | colordialog | 额外插件(extra plugin) |
页面初始化一个基础allowedContent对象:
allowedContent = { p: true, div: true, table: true, thead: true, tbody: true, tr: true, td: { styles: [], attributes: [] } }td的styles与attributes数组初始为空,之后由复选框的change(IE8 下退化为click)事件驱动增删:
case 'th': if ( state ) { allowedContent.th = true; } else { delete allowedContent.th; } case 'rowspan': case 'colspan': addRemoveRule( rule, state, attributes ); break; default: addRemoveRule( rule, state, styles );addRemoveRule在数组中增删对应规则项。勾选变化后,页面执行editor.destroy( true )并调用createEditor()用新的allowedContent重建编辑器:
function createEditor() { var config = { allowedContent: allowedContent, plugins: 'wysiwygarea,toolbar,table,tabletools,sourcearea', extraPlugins: [] }; if ( CKEDITOR.document.findOne( 'input[name="colordialog"]' ).$.checked ) { config.extraPlugins.push( 'colordialog' ); } return CKEDITOR.replace( 'editor1', config ); }值得注意的是,编辑器固定加载table与tabletools插件,并视复选框状态动态追加colordialog插件——这直接呼应了对话框中"背景/边框颜色"字段在安装颜色选择插件后才会多出取色按钮的行为。
三、源码级原理:对话框字段按 requiredContent 逐项过滤
3.1 命令级 allowedContent 白名单
cellProperties命令在 plugins/tabletools/plugin.js 中注册,声明了两层过滤规则:
addCmd( 'cellProperties', new CKEDITOR.dialogCommand( 'cellProperties', createDef( { allowedContent: 'td th{width,height,border-color,background-color,white-space,vertical-align,text-align}[colspan,rowspan]', requiredContent: requiredContent, ... } ) ) );其中requiredContent数组明确定义了单元格属性能力所需的全部规则:
var requiredContent = [ 'td{width}', 'td{height}', 'td{border-color}', 'td{background-color}', 'td{white-space}', 'td{vertical-align}', 'td{text-align}', 'td[colspan]', 'td[rowspan]', 'th' ];同时命令还注册了三组contentTransformations,负责兼容旧式属性向新式样式的迁移:
background简写样式 →background-color;- 旧
valign属性 →vertical-align样式; - 行级
height样式 → 逐个单元格写入cke-row-height自定义属性再回写(对应 #16818 的修复)。
3.2 对话框字段的逐项门控
对话框定义位于 plugins/tabletools/dialogs/tableCell.js,其中每个 UI 字段都声明了自己的requiredContent:
| 对话框字段 | 对应requiredContent |
|---|---|
| width / height(尺寸) | td{width}/td{height} |
| wordWrap(自动换行) | td{white-space} |
| hAlign(水平对齐) | td{text-align} |
| vAlign(垂直对齐) | td{vertical-align} |
| cellType(单元格类型) | th[scope] |
| rowSpan / colSpan(跨行/跨列) | td[rowspan]/td[colspan] |
| bgColor(背景颜色) | td{background-color} |
| borderColor(边框颜色) | td{border-color} |
对话框构建时先对所有字段做一次过滤(tableCell.js):
items = CKEDITOR.tools.array.filter( items, function( item ) { var requiredContent = item.requiredContent, ret; delete item.requiredContent; ret = editor.filter.check( requiredContent ); if ( ret && !item.isSpacer ) { itemsCount++; } return ret; } );未通过的字段直接从定义中剔除;再依据itemsCount > 5决定对话框采用单列还是双列布局(minWidth随之在 205 与 410 之间切换)。
除此之外,plugins/dialog/plugin.js 中uiElement构造器还有第二道防线:
if ( elementDefinition.requiredContent && !dialog.getParentEditor().filter.check( elementDefinition.requiredContent ) ) { styles.display = 'none'; this.notAllowed = true; }即运行时仍以filter.check()复核,不满足的字段直接隐藏并标记notAllowed。这正是自动化测试中item.notAllowed断言(见下文)所针对的机制。
3.3 底层判定:filter.check 与结果缓存
上述所有门控最终都收敛到 core/filter.js 的filter.check()方法。该方法将测试规则字符串(如td{width})构造成"模拟元素"后走一遍完整过滤流水线,若元素被标记移除则返回false。其关键实现细节是结果缓存:
if ( typeof test == 'string' ) this._.cachedChecks[ cacheKey ] = result;一旦某条规则的判定结果被缓存,后续check直接命中缓存。这解释了手动测试页面源码中的一句注释:
// We need to recreate editor, because filter is caching checks. editor.destroy( true );——正因为过滤器缓存了判定结果,修改allowedContent后必须重建编辑器(或通过filter的重新加载机制)才能让新规则生效。这也是"勾选/取消复选框会销毁并重建编辑器、从而产生闪烁"这一注意项的根本原因。
四、源码级原理:上下文菜单项由 filter.check 门控
手动测试的第二个断言是:"如果未勾选任何属性,则单元格属性不会出现在上下文菜单中"。其实现位于 plugins/tabletools/plugin.js 的菜单项组装逻辑:
getItems: function() { var selection = editor.getSelection(), cells = getSelectedCells( selection ), items = { /* 插入/删除/合并/拆分等条目固定出现 */ }; if ( editor.filter.check( requiredContent ) ) { items.tablecell_properties = cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED; } return items; }当编辑器当前过滤器不允许requiredContent数组中的任何一条规则(例如allowedContent被配置为仅table;tbody;thead;tr;td;,未放行任何td样式/属性)时,tablecell_properties条目就不会进入菜单对象,于是右键菜单中找不到"单元格属性"入口;反之,若放行了部分规则,该条目出现,但具体字段仍由对话框级过滤进一步裁剪。
这一"上下文菜单出现与否"的断言在自动化测试 tests/plugins/tabletools/allowedcontent.js 中有对应验证:
assert editor.name === 'editor' ? 'isFalse' : 'isTrue' ;五、自动化测试:逐项组合验证字段显隐
仓库在 tests/plugins/tabletools/allowedcontent.js 中提供了一套完备的自动化测试,与手动测试场景互为印证。测试定义了一张filterMap,将对话框字段映射到"元素 → styles/attributes → 规则名":
var filterMap = { width: [ 'td', 'styles', 'width' ], height: [ 'td', 'styles', 'height' ], wordWrap: [ 'td', 'styles', 'white-space' ], hAlign: [ 'td', 'styles', 'text-align' ], vAlign: [ 'td', 'styles', 'vertical-align' ], cellType: [ 'th', 'attributes', 'scope' ], rowSpan: [ 'td', 'attributes', 'rowspan' ], colSpan: [ 'td', 'attributes', 'colspan' ], bgColor: [ 'td', 'styles', 'background-color' ], borderColor: [ 'td', 'styles', 'border-color' ] };基准编辑器配置为allowedContent: 'table;tbody;thead;tr;td;',然后为每个字段组合动态生成一个带extraAllowedContent的编辑器实例(editor_<index>_allowed:<key1,key2,...>),逐一断言:字段被允许时对话框中出现对应选项,未被允许时对应字段缺失或notAllowed。
另一份测试 tests/plugins/tabletools/cellproperties.js 从反方向验证了同一机制:
'test allowedContent rule': function() { // allowedContent: '*[lang,dir]; table tbody thead tr th td tfoot' // 断言 dialog.getContentElement( 'info', 'width' ) 为 undefined(字段被剔除) }, 'test disallowedContent rule': function() { // 默认编辑器配置下 // 断言 width / height / heightType 均 isVisible() }六、实战配置:用 allowedContent 裁剪单元格属性能力
理解了过滤机制后,即可在实际项目中按需裁剪。以下配置组合可供直接参考:
1. 仅允许基础表格结构,不允许任何单元格属性(对应手动测试"全不勾选"态):
CKEDITOR.replace( 'editor1', { allowedContent: 'table;tbody;thead;tr;td;' } );效果:右键菜单不出现"单元格属性";即使通过 API 强行打开cellProperties对话框,所有字段也均为空/隐藏。
2. 只允许尺寸与合并属性,禁止颜色与对齐设置:
CKEDITOR.replace( 'editor1', { allowedContent: { table: true, tbody: true, thead: true, tr: true, td: { styles: [ 'width', 'height' ], attributes: [ 'colspan', 'rowspan' ] } } } );效果:对话框中仅保留宽度/高度/行跨度/列跨度字段,其余字段被过滤;菜单中的"单元格属性"条目仍会出现。
3. 通过extraAllowedContent渐进式放行(与自动化测试同思路):
CKEDITOR.replace( 'editor1', { allowedContent: 'table;tbody;thead;tr;td;', extraAllowedContent: { td: { styles: [ 'background-color' ], attributes: [ 'rowspan' ] }, th: true } } );效果:仅背景颜色、行跨度、th元素被放行,对应字段随之出现。注意extraAllowedContent的规则会与默认/自定义allowedContent合并后参与filter.check()判定。
4. 颜色选择器联动:当配置中未启用colordialog插件时,背景/边框颜色字段退化为纯文本输入(tableCell.js 依据editor.plugins.colordialog决定是否追加取色按钮);启用后才会在字段旁出现取色按钮。
七、总结与注意事项
回顾整个机制链路:allowedContent(配置层)→CKEDITOR.filter.check()(判定层,含结果缓存)→ 对话框字段级requiredContent过滤与上下文菜单项门控(表现层)。手动测试场景正是用复选框直观演示了这条链路,而自动化测试则从代码层面锁定了每一处行为。
实际使用中需特别注意两点:
- 过滤器缓存:
filter.check()会缓存字符串规则的判定结果,动态修改allowedContent后必须重建编辑器(或显式重新加载过滤器)才能生效——这正是手动测试"编辑器被销毁重建、页面闪烁"的来源,属预期行为而非缺陷; - 上下文菜单与对话框的过滤粒度不同:菜单只看
requiredContent数组整体是否"至少有一条被放行",对话框则逐字段过滤,因此可能出现"菜单有入口、但对话框内字段大幅缩减"的中间状态,配置时需结合两者综合考虑。
进一步阅读:命令与菜单定义见 plugins/tabletools/plugin.js,对话框实现见 plugins/tabletools/dialogs/tableCell.js,过滤判定核心见 core/filter.js,自动化验证见 tests/plugins/tabletools/allowedcontent.js 与 tests/plugins/tabletools/cellproperties.js。
- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
相关推荐
CKEditor 4 表格 Header 与 scope 属性联动机制解析:从 manual 测试到源码实现
CKEditor 4 表格 Header 与 scope 属性联动机制解析:从 manual 测试到源码实现 导读 本文以 CKEditor 4 仓库中的手动测
前端UI组件《Android 官方培训课程中文版》OpenGL ES 系列实战:运用投影与相机视角(Projection & Camera View)完整指南
《Android 官方培训课程中文版》OpenGL ES 系列实战:运用投影与相机视角(Projection & Camera View)完整指南 本文基于本仓
前端UI组件CKEditor 4 移动端粘贴对话框行为解析:从 Bender 手动测试到 forcePasteDialog 源码实现
CKEditor 4 移动端粘贴对话框行为解析:从 Bender 手动测试到 forcePasteDialog 源码实现 导读 本文围绕 CKEditor 4
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考